Aller au contenu

Photo

Force move across areas


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

#1
Jereniva

Jereniva
  • Members
  • 120 messages

PC has a conversation with an NPC in Area 1, and following that conversation, the NPC should move to a waypoint in Area 2,

I put a waypoint by the exit door, and assigned command on NPC to force move to the exit waypoint.

Then using a delay, jump the NPC to her home waypoint in Area 2. 

Feeding this with sDestination = tag of the waypoint in Area 2, and a delay of 10, the NPC will indeed run off towards, and disappear at, the "nwc_exit" waypoint.

But, if I go through the door to Area 2, she's not at her home waypoint. If I pass back to Area 1, she's standing in the door way of Area 1.

Does anything look wrong with this?

I'm always a little confused with using delay with Assign Command.

Thank you.

 

the below script is in the Action of the final line of conversation. I know the provided sDestination tag is correct for the WP.

void main(string sDestination, float fDelay)
{
    object oExitWP = GetObjectByTag("nwc_exit");
    location lDestWP = GetLocation(GetObjectByTag(sDestination));
    object oTarget = OBJECT_SELF;

    
    AssignCommand(oTarget, ActionForceMoveToObject(oExitWP,TRUE));
    AssignCommand(oTarget, DelayCommand(fDelay, JumpToLocation(lDestWP)));
}


#2
kamal_

kamal_
  • Members
  • 5 240 messages

You could also destroy the npc when they get to the exit, and spawn them at their area 2 spot when the player enters area 2. That would accomplish pretty much the same thing unless you are recording variables on the npc. Or have two copies of the npc, script hiding and showing as appropriate.

 

Another possibility would be that if the player goes to area 2 before the npc is given the command to jump, the jump command may not be getting processed because without a player in the area the npc would be set to minimal ai and thus not receive the command.

 

/or I could be completely wrong.

 

Uncle FB's npc control has cross area npcs, you may wish to look at his code.


  • Jereniva aime ceci

#3
kevL

kevL
  • Members
  • 4 056 messages
I'd guess it's because the script is telling NPC to 'execute delayed command, Jump' while it is still performing the action of moving to the exit-object. And since NPC is in the midst of performing an action, the delayed command gets ignored.

If so there are a couple of ways around that misbehavior. One is to muck about with ActionDoCommand() and SetCommandable() functions. The other is to use a delayed subfunction; that is, replace the JumpToLocation() line with a call to a custom function:
 
DelayCommand(fDelay, doJump(oTarget, lDestWP));
void doJump(object oTarget, location lDestWP)
{
    AssignCommand(oTarget, ClearAllActions(TRUE));
    AssignCommand(oTarget, JumpToLocation(lDestWP));
}

[edit]There's other ways, like using ActionJumpToLocation()
but then there's a risk of the action-queue cleared by random fights & dialogs ... giving rise to the SetCommandable-tango.
  • Jereniva aime ceci

#4
Tchos

Tchos
  • Members
  • 5 042 messages

My crime system in my module makes assaulted neutral NPCs flee to exit waypoints and jump to another area.  kevL is right on about the problem being trying to issue a delayed command while the NPC has been given the ActionForceMoveToObject() command, but it's because that function specifically makes the NPC uncommandable until the action is complete, not just because it's in the middle of an action.  What I did was create a new function based on ActionForceExit which instead of destroying the NPC, it jumps them to a specified waypoint at the end.


  • Jereniva aime ceci

#5
Jereniva

Jereniva
  • Members
  • 120 messages

A big thank you for the quick replies, I never expected to fix this tonight!

 

Based on the suggestions, and KevL's sub, I made the below change and it now works perfectly.

#include "ginc_param_const"
    

void doJump(object oTarget, location lDestWP)
{
    AssignCommand(oTarget, ClearAllActions(TRUE));
    AssignCommand(oTarget, JumpToLocation(lDestWP));
}


void main(string sDestination, string sTarget, float fDelay)
{
    object oExitWP = GetObjectByTag("nwc_exit");
    location lDestWP = GetLocation(GetObjectByTag(sDestination));
    object oTarget = OBJECT_SELF;

    
    AssignCommand(oTarget, ActionForceMoveToObject(oExitWP,TRUE));
    DelayCommand(fDelay, doJump(oTarget, lDestWP));
    //AssignCommand(oTarget, JumpToLocation(lDestWP));
}


#6
Tchos

Tchos
  • Members
  • 5 042 messages

The way you're doing it, the NPC will jump to the area whether it reaches the destination or not, because you're using a delay instead of putting it into an action queue so that the jump command will be given only once the NPC has reached the destination.  I would use a delayed command only as a failsafe for if the NPC gets stuck somehow, that if the action queue commands failed after the NPC has had more than enough time to reach the waypoint, then jump.


  • Jereniva aime ceci

#7
kevL

kevL
  • Members
  • 4 056 messages
J.,
looks like you don't need the #include or 'sTarget' ...
glad to hear it's working.


T,
sure, but sometimes all ya need is a good guess and a delay,
  • Jereniva aime ceci

#8
Dann-J

Dann-J
  • Members
  • 3 161 messages

Having them jump prematurely (if they take a bit longer than usual to get where they're going) is better than not having them jump at all.

 

I still occasionally use an old 'run and jump' script that calculates the jump delay required based on the speed of the creature and the distance it has to run, assuming a straight line. If for some reason the creature takes a detour then the delay doesn't quite work as perfectly (but still works).

 

One method I use now is to set a local variable on them and have them run to a trigger. The trigger OnEnter script jumps the creature if it finds that local variable set on them. Otherwise it does nothing. That way the jump always occurs at the right time, and doesn't accidentally jump anyone else (or even that particular creature, until the right variable gets set on them). Sometimes I'll use a door instead, and have its OnOpened script do the variable check.


  • Jereniva aime ceci

#9
Tchos

Tchos
  • Members
  • 5 042 messages

Having them jump prematurely (if they take a bit longer than usual to get where they're going) is better than not having them jump at all.

 

Right, which is why I said I'd leave a delayed command as a failsafe.


  • Jereniva aime ceci

#10
Claudius33

Claudius33
  • Members
  • 256 messages
I personally prefer to have two instances of the NPC : one in the first area force exited (ga_force_exit)  or destroyed (in a script) after the first conversation. The second just waiting in area 2 or spawned when appropriate.
 
Both can share the same module or campaign conversation. The two instances must have different tags, for instance : mynpc_intro, mynpc.
 
If you absolutely need to carry variables from instance 1 to instance 2 :
  • In the same module use any placeable or npc tagged 'plot' with a unique tag to carry the variables.
  • In different modules, use global variables (it works very well) or have the PC carrying them.

  • Jereniva aime ceci

#11
4760

4760
  • Members
  • 1 204 messages

Just out of curiosity: any reason why two instances instead of moving the same one from one area to another area?

As you mentioned, with two instances you'd need to store variables differently (I personally tend to avoid global variables), not mentioning that should you wish to have the NPC wear a different outfit (because of the way the plot progresses for example), you'll have to create a different version whereas a single instance only has to be given a different armor. Again, just wondering.


  • Jereniva aime ceci

#12
Claudius33

Claudius33
  • Members
  • 256 messages

it's just a safer way to do. In my first mod a few players encountered troubles when I was moving the same instance from one area to another. For whatever reason the NPC wasn't moved and they got stuck. Fortunately the mod was released with debug scripts so a player could do the move through the console. 

 

Having different instances also allows you to have different outfits, different armors, different inventories (you can even change the hair color). There are plenty of

Spoiler
in Sarmates! wearing different armors or clothes, but all sharing the same campaign conversation. In one module one instance is a regular NPC wearing civilian clothes while a second one is a henchman wearing a 'Roman' armor.

 

Other possibility : a first instance can be an introductory NPC, the second becoming a companion with different starting classes / inventories depending on the first conversation, as

Spoiler
in 16 Cygni . 


  • Jereniva aime ceci

#13
kamal_

kamal_
  • Members
  • 5 240 messages

Just out of curiosity: any reason why two instances instead of moving the same one from one area to another area?

As you mentioned, with two instances you'd need to store variables differently (I personally tend to avoid global variables), not mentioning that should you wish to have the NPC wear a different outfit (because of the way the plot progresses for example), you'll have to create a different version whereas a single instance only has to be given a different armor. Again, just wondering.

I do things like Claudius does, and like him store variables as globals or as locals on something other than the npc. For me it has the advantage of being conceptually easier for me to understand. I've also found it to be quite reliable.


  • Jereniva aime ceci

#14
Tchos

Tchos
  • Members
  • 5 042 messages

I used two instances once, but I decided I liked it better to simply use the same NPC.  When I used two instances, I did store the variables on the NPC's spawn waypoint instead of on the NPC.  That works fine, but is inconsistent with my other NPCs and so is prone to mistakes (for me).  So for me, it's safer to always have only one version of an NPC, and only one blueprint for it.  While I can't change hair colour this way (unless I were to use wig helmets), I change clothing for them easily by script.  I can put any outfit I want them to wear into their inventory at the start, or spawn it into their inventory at any time, and make them equip it.  Same goes for scripts (companion scripts or normal NPC), other inventory manipulation, conversation, etc.  All can be manipulated on a single NPC instance by script.

 

One alternative, preserving variables on the NPC, would be the "murdering twinmaker teleporter" approach, using CopyObject and DestroyObject.


  • Jereniva aime ceci

#15
Dann-J

Dann-J
  • Members
  • 3 161 messages

I've had trouble getting an NPC to walk a different set of waypoints once they'd been jumped to another area. Even pausing their walk waypoints, jumping them, changing their waypoint set, and restarting them walking often failed. I ended up using the two-version method as well, with an outdoor version of an NPC for daytime and an indoor version for nighttime. That way they could wear different clothing, take off gloves and hats, etc while relaxing in their homes.

 

I didn't bother copying local variables between them. I just had their shared conversation check one of them specifically for certain variables, by specifying their tag in the GC_ and GA_ scripts (rather than leaving the tag blank to default to the conversation owner). That way one of them becomes the official Keeper of the Variables.


  • Jereniva aime ceci

#16
4760

4760
  • Members
  • 1 204 messages

Thanks all for all the info.


  • Jereniva aime ceci

#17
rjshae

rjshae
  • Members
  • 4 485 messages

Even though I'm setting SetHenchOption( HENCH_OPTION_OPEN, TRUE ), I'm having trouble getting NPCs to jump between areas using the ActionForceMoveToObject command. Oddly it's working for one character that has a Merchant Faction ID, but not for two others--one a Defender and the other a Commoner. The two are being passed a ClearAllActions command prior to the jump, so they shouldn't be doing anything.

 

Here's example code:

    SetAILevel(oThesta, AI_LEVEL_NORMAL);
    AssignCommand(oThesta, ClearAllActions(TRUE));
    AssignCommand(oThesta, ActionForceMoveToObject(oWellRoom, TRUE, 1.0, 5.0));
SendMessageToPC( oPC, "act_q2adurnan_1: Move Thesta to Well Room" );
    DelayCommand(12.0, SetAILevel(oThesta, AI_LEVEL_DEFAULT));

It's printing "act_q2adurnan_1: Move Thesta to Well Room" but nothing else happens.

 

Any suggestions?

 

Ed.: When I check the object (oThesta) it returns OBJECT_INVALID, which is odd since I find the NPC with that tag when I move to the adjacent room. *scratching my head*

 

Ed2: When I check the area for the NPC's tag using a GetFirstObjectInArea/GetNextObjectInArea loop then I find it. However, a GetObjectByTag returns OBJECT_INVALID. Wha?



#18
kamal_

kamal_
  • Members
  • 5 240 messages

Even though I'm setting SetHenchOption( HENCH_OPTION_OPEN, TRUE ), I'm having trouble getting NPCs to jump between areas using the ActionForceMoveToObject command. Oddly it's working for one character that has a Merchant Faction ID, but not for two others--one a Defender and the other a Commoner. The two are being passed a ClearAllActions command prior to the jump, so they shouldn't be doing anything.
 
Here's example code:

    SetAILevel(oThesta, AI_LEVEL_NORMAL);
    AssignCommand(oThesta, ClearAllActions(TRUE));
    AssignCommand(oThesta, ActionForceMoveToObject(oWellRoom, TRUE, 1.0, 5.0));
SendMessageToPC( oPC, "act_q2adurnan_1: Move Thesta to Well Room" );
    DelayCommand(12.0, SetAILevel(oThesta, AI_LEVEL_DEFAULT));
It's printing "act_q2adurnan_1: Move Thesta to Well Room" but nothing else happens.
 
Any suggestions?

 

 
ForceMove is not used to cross area movement in the script that handles cohorts, ActionJumpToObject is. Here's the relevant bit. The jump command makes them fade out from wherever they are, not move to an exit point, so it may not be what you want.

	else if (GetArea(oHangOut) == GetArea(oRM))
	{	// if in same area, walk to spot
		PrettyDebug("GoToHangOutSpot(" + sName + "): if in same area, walk to spot ");
    	AssignCommand(oRM, ClearAllActions(TRUE));
    	AssignCommand(oRM, ActionForceMoveToObject(oHangOut));
	}
	else
	{ // jump to hang out spot in another area.
		PrettyDebug("GoToHangOutSpot(" + sName + "): jump to hang out spot in another area.");
    	AssignCommand(oRM, ClearAllActions(TRUE));
    	AssignCommand(oRM, ActionJumpToObject(oHangOut));
	}


#19
Tchos

Tchos
  • Members
  • 5 042 messages

This is my modification of ActionForceExit:

void ForceMoveToObjectThenJump(string sLeaver, string sExitTag, string sDestinationTag, int bRun)
{
	object oLeaver = GetObjectByTag(sLeaver);
	object oExit = GetObjectByTag(sExitTag);
	object oDestination = GetObjectByTag(sDestinationTag);
	if (GetIsObjectValid(oExit))
	{
		AssignCommand(oLeaver, ClearAllActions());
		AssignCommand(oLeaver, ActionForceMoveToObject(oExit, bRun));
		AssignCommand(oLeaver, ActionJumpToObject(oDestination));
		ActionDoCommand(SetCommandable(TRUE, oLeaver));
		SetCommandable(FALSE, oLeaver);
	}
}

The NPC walks or runs to a specified waypoint in the current area, then teleports to a waypoint elsewhere, whether in this area or another area.


  • GCoyote aime ceci

#20
rjshae

rjshae
  • Members
  • 4 485 messages

Okay. I guess part of my problem though is that GetObjectByTag isn't working consistently across areas for some reason.



#21
Dann-J

Dann-J
  • Members
  • 3 161 messages

This is a run/jump script that I've been using. It attempts to calculate the delay between starting to walk/run and jumping to the new area by assuming the creature will move in a straight line to get there. If there are any obstacles in the way then they'll jump a bit before they get to the exit point. Talking to them after they've started moving will also ruin the illusion of reaching an exit.

// ga_run_jump
//
// sCreature = tag of creature to run/jump
// sRunTo = tag of waypoint to run to
// sJumpTo = tag of waypoint to jump to
// int iRun = 0 to walk, 1 to run

include "ginc_param_const"

void main(string sCreature, string sRunTo, string sJumpTo, int iRun)
{
if (iRun > 1)
iRun = 1;

object oNPC = GetObjectByTag(sCreature);
object oJumpTo = GetObjectByTag(sJumpTo);
object oWP = GetNearestObjectByTag(sRunTo);
location oLoc = GetLocation(oWP);

// Calculate jump delay, assuming walk/run in a straight line
float fDistance = GetDistanceBetween(oNPC, oWP);
float fRunSpeed = GetMovementRate(oNPC) * GetMovementRateFactor(oNPC);
float fDelay = (fDistance / fRunSpeed) + 1.0 + ((fDistance / fRunSpeed)*abs(iRun-1));

AssignCommand(oNPC, ActionForceMoveToLocation(oLoc,iRun,fDelay));
AssignCommand(oNPC, DelayCommand(fDelay, ActionJumpToObject(oJumpTo)));

}


#22
Tchos

Tchos
  • Members
  • 5 042 messages

Okay. I guess part of my problem though is that GetObjectByTag isn't working consistently across areas for some reason.

 

Do you have more than one object with the same tag?  GetObjectByTag doesn't care what area the object is in, as long as it's in the same module.



#23
rjshae

rjshae
  • Members
  • 4 485 messages

Do you have more than one object with the same tag?  GetObjectByTag doesn't care what area the object is in, as long as it's in the same module.

 

I don't think that's it because it returns an invalid object; if there were more than one then it should at least return an object pointer. I'm concerned now that something may be corrupted.



#24
Tchos

Tchos
  • Members
  • 5 042 messages

Are we talking about waypoints, or is it something that could possibly be destroyed?



#25
rjshae

rjshae
  • Members
  • 4 485 messages

They are creatures (NPCs) that I can confirm exist by moving to those areas.

 

Even stranger, I'm able to find the NPCs using GetObjectByTagAndType. Just not via GetObjectByTag. *sigh*