Paththrough RideMovers
Paththrough Main Page | Paththrough RideMovers |
RideMovers
Setting up the waypoints
RideMover points are fairly similar to UseSwitch points in many respects, although the setup is a little more complex.
In the above example, we want to achieve the following: A bot that passes waypoint A on its way towards B (in the castle) should not follow the connection A — B directly, but instead
- wait at A until the cable car arrives at C,
- board the cable car at waypoint C,
- stand in place until the cable car reaches its destination,
- go to waypoint B,
- resume normal path finding.
First, we set the paththrough property on waypoint A:
/bot waypoint_setproperty paththrough RideMover_PT:cablecar1_low
Second, we name waypoints B and C. Let's assume we called them "castle1" and "village1", respectively.
Adding the RideMovers table
In the next step, we add a RideMovers table to the Map table in the script:
RideMovers =
{
cablecar1_low =
{
// the name of the mover:
movergoal = "MOVER_cablecar1",
// offset from the origin of the mover:
rideposition = Vector3(0, 0, -150),
// the name of the start waypoint:
startwp = "village1",
// the name of the exit waypoint:
exitwp = "castle1",
// the stance the bot should take (BTN.CROUCH or BTN.PRONE, if any):
stance = BTN.CROUCH,
// maximum number of bots in the mover
maxusers = 4,
// ride is aborted if distance from mover is greater than tolerance
Tolerance = 500,
// board the mover when Map.Mover1Position is 0:
board = function(_this)
{
return Map.Mover1Position == 0;
},
// leave the mover when Map.Mover1Position is 1:
leave = function(_this)
{
return Map.Mover1Position == 1;
},
},
},
For each mover, there is a table whose name corresponds to the part after the colon in the bot waypoint_setproperty command we used earlier. In our example, this is "cablecar1_low".
- movergoal is the goal name of the vehicle;
- startwp would be waypoint C in our example;
- exitwp would be waypoint B;
- rideposition is the offset from the mover's origin the bots shall have.
If the vehicle you need is not listed when using the command
/bot show_goals mover.*
you might have to add
ShowMovers = true,
at the top of your Map table.
To find the ride position, stand where you want the bots to stand and issue
/bot sgn 300 o
this will print all goals in a radius of 300 units and your offset from them to the console.
To stop all movers, use
/set g_stopmovers 1
Determining the mover's position
The board and leave functions should return true if the mover is in the correct position to board or leave, respectively; false in all other cases. This means we need some method to determine its position. Let's discuss three possibilities:
(1) If a trigger is fired in the map each time the mover arrives at or leaves a station, a callback function can be used, like so:
TrainLeaves = function()
{
Map.Mover1Position = -1;
},
TrainAtCastle = function()
{
Map.Mover1Position = 1;
},
TrainInVillage = function()
{
Map.Mover1Position = 0;
},
(2) Using trigger regions would be another straightforward method.
(3) We can start a thread that permanently watches the mover's position and stores it in Map.Mover1Position. We could do so by adding the following WatchMoverPosition function to the Map table and start it in its own thread from the OnMapLoad function.
WatchMoverPosition = function()
{
goal = GetGoal( "MOVER_cablecar1" );
entity = goal.GetEntity();
startpos = Vector3( -3700, 960, 1450 );
endpos = Vector3( -3700, -1910, 1970 );
tolerance = 60;
frequency = 1.2; // the sleep interval for the loop
while ( true )
{
entitypos = GetEntPosition( entity );
if ( DistanceBetween( entitypos, startpos ) < tolerance && Map.Mover1Position != 0 )
{
Map.Mover1Position = 0;
if ( Map.Debug )
{ print("mover at pos.", Map.Mover1Position); }
}
else if ( DistanceBetween( entitypos, endpos ) < tolerance && Map.Mover1Position != 1 )
{
Map.Mover1Position = 1;
if ( Map.Debug )
{ print("mover at pos.", Map.Mover1Position); }
}
else if ( DistanceBetween( entitypos, endpos ) >= tolerance )
{
// it's moving!
Map.Mover1Position = -1;
if ( Map.Debug )
{ print("mover at pos.", Map.Mover1Position); }
}
if ( Map.Debug )
{
DrawDebugLine( startpos-Vector3(0,0,15), startpos+Vector3(0,0,15), COLOR.CYAN, frequency );
DrawDebugLine( endpos-Vector3(0,0,15), endpos+Vector3(0,0,15), COLOR.CYAN, frequency );
}
sleep( frequency );
}
},
Putting all this together, we get the following map script:
global Map =
{
Movers =
{
"MOVER_cablecar1",
},
Mover1Position = -1,
RideMovers =
{
cablecar1_low =
{
movergoal = "MOVER_cablecar1",
rideposition = Vector3(0, 0, -150),
startwp = "village1",
exitwp = "castle1",
board = function(_this)
{
return Map.Mover1Position == 0;
},
leave = function(_this)
{
return Map.Mover1Position == 1;
},
},
},
WatchMoverPosition = function()
{
goal = GetGoal( "MOVER_cablecar1" );
entity = goal.GetEntity();
startpos = Vector3( -3700, 960, 1450 );
endpos = Vector3( -3700, -1910, 1970 );
tolerance = 60;
frequency = 1.2;
while ( true )
{
entitypos = GetEntPosition( entity );
if ( DistanceBetween( entitypos, startpos ) < tolerance && Map.Mover1Position != 0 )
{
Map.Mover1Position = 0;
if ( Map.Debug )
{ print("mover at pos.", Map.Mover1Position); }
}
else if ( DistanceBetween( entitypos, endpos ) < tolerance && Map.Mover1Position != 1 )
{
Map.Mover1Position = 1;
if ( Map.Debug )
{ print("mover at pos.", Map.Mover1Position); }
}
else if ( DistanceBetween( entitypos, endpos ) >= tolerance )
{
Map.Mover1Position = -1;
if ( Map.Debug )
{ print("mover at pos.", Map.Mover1Position); }
}
if ( Map.Debug )
{
DrawDebugLine( startpos-Vector3(0,0,15), startpos+Vector3(0,0,15), COLOR.CYAN, frequency );
DrawDebugLine( endpos-Vector3(0,0,15), endpos+Vector3(0,0,15), COLOR.CYAN, frequency );
}
sleep( frequency );
}
},
};
global OnMapLoad = function()
{
thread(Map.WatchMoverPosition);
};