Some functions require DM 1.0 Nightly build.
A lot of people have been wondering about scripted traffic and there is more than one way of doing it. With the new ped functions (DM 1.0 Nightly build) a convenient method is to set control states. However, setting control states is client-sided and no one except you will be able to see the vehicle moving unless you tell all clients to do the same. This code snippet will demonstrate how to move vehicles along scripted paths by using server-side velocity changes. It is very basic and probably not the most efficient way of moving vehicles but it may help people with their scripts.
Also note that it is recommended to do all this client-side. Timers do not provide fast enough intervals to make the vehicle rotate smoothly. This sample is just to show how it could be done server-side.
Server script:
ROOT = getRootElement ();
RES = getThisResource ();
PATHS = "paths.xml";
local pPaths = {};
local iPath = 1;
local bHandleSpawn = true;
local bCreateMarkers = true; -- Just for testing.
local pMover = nil;
function getTargetAngle ( pSource, fTargetX, fTargetY, fTmp )
local fSourceX, fSourceY, fSourceZ = getElementPosition( pSource );
local _, _, fRot = getVehicleRotation ( pSource );
local fRelPosX = fTargetX - fSourceX;
local fRelPosY = fTargetY - fSourceY;
local fDist = math.sqrt ( ( fRelPosX * fRelPosX ) + ( fRelPosY * fRelPosY ) );
local fDot = 0;
fRot = fRot + fTmp;
fRelPosX = fRelPosX / fDist;
fRelPosY = fRelPosY / fDist;
fDot = ( ( math.sin ( math.rad ( 360 - fRot ) ) ) * fRelPosX ) + ( ( math.cos ( math.rad ( 360 - fRot ) ) ) * fRelPosY );
return math.deg ( math.acos ( fDot ) );
end;
function onTimer ()
local fPosX, fPosY = 0, 0;
local fRotZ = 0;
local fVelX, fVelY, fVelZ = 0, 0, 0;
local fPerc = 0;
if ( pMover and #pPaths > 0 ) then
fPosX, fPosY = getElementPosition ( pMover );
if ( getDistanceBetweenPoints2D ( fPosX, fPosY, pPaths[ iPath ].fPosX, pPaths[ iPath ].fPosY ) > pPaths[ iPath ].fMaxTargetDistance ) then
_, _, fRotZ = getVehicleRotation ( pMover );
fVelX, fVelY, fVelZ = getElementVelocity ( pMover );
if ( fRotZ >= 0 and fRotZ <= 90 ) then
fPerc = ( fRotZ / 90 ) * 100;
if ( fPerc < 50 ) then
fVelX = -( ( fPerc / pPaths[ iPath ].fSpeedDivision ) * 1 );
fVelY = ( ( 100 - fPerc ) / pPaths[ iPath ].fSpeedDivision ) * 1;
else
fVelX = -( ( fPerc / pPaths[ iPath ].fSpeedDivision ) * 1 );
fVelY = ( ( 100 - fPerc ) / pPaths[ iPath ].fSpeedDivision ) * 1;
end;
end;
if ( fRotZ >= 90 and fRotZ <= 180 ) then
fRotZ = fRotZ - 90;
fPerc = ( fRotZ / 90 ) * 100;
if ( fPerc < 50 ) then
fVelX = -( ( ( 100 - fPerc ) / pPaths[ iPath ].fSpeedDivision ) * 1 );
fVelY = -( ( fPerc / pPaths[ iPath ].fSpeedDivision ) * 1 );
else
fVelX = -( ( ( 100 - fPerc ) / pPaths[ iPath ].fSpeedDivision ) * 1 );
fVelY = -( ( fPerc / pPaths[ iPath ].fSpeedDivision ) * 1 );
end;
end;
if ( fRotZ >= 180 and fRotZ <= 270 ) then
fRotZ = fRotZ - 180;
fPerc = ( fRotZ / 90 ) * 100;
if ( fPerc < 50 ) then
fVelX = ( fPerc / pPaths[ iPath ].fSpeedDivision ) * 1;
fVelY = -( ( ( 100 - fPerc ) / pPaths[ iPath ].fSpeedDivision ) * 1 );
else
fVelX = ( fPerc / pPaths[ iPath ].fSpeedDivision ) * 1;
fVelY = -( ( ( 100 - fPerc ) / pPaths[ iPath ].fSpeedDivision ) * 1 );
end;
end;
if ( fRotZ >= 270 and fRotZ <= 360 ) then
fRotZ = fRotZ - 270;
fPerc = ( fRotZ / 90 ) * 100;
if ( fPerc < 50 ) then
fVelX = ( ( 100 - fPerc ) / pPaths[ iPath ].fSpeedDivision ) * 1;
fVelY = ( fPerc / pPaths[ iPath ].fSpeedDivision ) * 1;
else
fVelX = ( ( 100 - fPerc ) / pPaths[ iPath ].fSpeedDivision ) * 1;
fVelY = ( fPerc / pPaths[ iPath ].fSpeedDivision ) * 1;
end;
end;
setElementVelocity ( pMover, fVelX, fVelY, fVelZ );
if ( getTargetAngle ( pMover, pPaths[ iPath ].fPosX, pPaths[ iPath ].fPosY, 0 ) > pPaths[ iPath ].fMaxTargetAngle ) then
if ( getTargetAngle ( pMover, pPaths[ iPath ].fPosX, pPaths[ iPath ].fPosY, -1 ) < getTargetAngle ( pMover, pPaths[ iPath ].fPosX, pPaths[ iPath ].fPosY, 1 ) ) then
print ( "* Right turn." );
setVehicleTurnVelocity ( pMover, 0, 0, -( pPaths[ iPath ].fTurnVelocity ) );
else
print ( "* Left turn." );
setVehicleTurnVelocity ( pMover, 0, 0, pPaths[ iPath ].fTurnVelocity );
end;
else
setVehicleTurnVelocity ( pMover, 0, 0, 0 );
end;
else
if ( iPath < #pPaths ) then
iPath = iPath + 1;
else
iPath = 1;
end;
print ( "* Next path." );
end;
end;
setTimer ( onTimer, 50, 1 );
end;
function onResourceStart ( pStartedResource )
local nRoot = false;
if ( pStartedResource == RES ) then
nRoot = xmlLoadFile ( PATHS, RES );
if ( nRoot ) then
for _, nPath in pairs ( xmlNodeGetChildren ( nRoot ) or {} ) do
table.insert ( pPaths,
{
fPosX = tonumber ( xmlNodeGetAttribute ( nPath, "posX" ) ) or -1,
fPosY = tonumber ( xmlNodeGetAttribute ( nPath, "posY" ) ) or -1,
fTurnVelocity = tonumber ( xmlNodeGetAttribute ( nPath, "turnVelocity" ) ) or -1,
fMaxTargetDistance = tonumber ( xmlNodeGetAttribute ( nPath, "maxTargetDistance" ) ) or -1,
fMaxTargetAngle = tonumber ( xmlNodeGetAttribute ( nPath, "maxTargetAngle" ) ) or -1,
fSpeedDivision = tonumber ( xmlNodeGetAttribute ( nPath, "speedDivision" ) ) or -1
}
);
end;
if ( bCreateMarkers ) then
for _, pPath in ipairs ( pPaths ) do
createMarker ( pPath.fPosX, pPath.fPosY, 0, "checkpoint", 1.5, math.random ( 0, 255 ), math.random ( 0, 255 ), math.random ( 0, 255 ), 255 );
end;
end;
pMover = getElementByID ( "mover" );
end;
end;
end;
function onKeyPressed ( pPlayer, szKey, szKeyState )
if ( szKey == "F1" and szKeyState == "down" ) then
onTimer ();
end;
end;
function onPlayerJoin ()
setCameraTarget ( source, source );
fadeCamera ( source, true );
spawnPlayer ( source, 0, 0, 2.5 );
bindKey ( source, "F1", "down", onKeyPressed );
end;
addEventHandler ( "onResourceStart", ROOT, onResourceStart, true );
if ( bHandleSpawn ) then
addEventHandler ( "onPlayerJoin", ROOT, onPlayerJoin, true );
end;
In order to make this work you must place a data file with paths in the same folder as your server script.
Example paths.xml data file:
<paths>
<path posX="45.761882781982" posY="-2.5386297702789" turnVelocity="0.01" maxTargetDistance="10" maxTargetAngle="10" speedDivision="1000" />
<path posX="132.56669616699" posY="78.492034912109" turnVelocity="0.01" maxTargetDistance="10" maxTargetAngle="10" speedDivision="1000" />
<path posX="228.1443939209" posY="50.259208679199" turnVelocity="0.01" maxTargetDistance="10" maxTargetAngle="10" speedDivision="1000" />
<path posX="233.02215576172" posY="-70.206611633301" turnVelocity="0.01" maxTargetDistance="10" maxTargetAngle="10" speedDivision="1000" />
<path posX="144.71810913086" posY="-71.875244140625" turnVelocity="0.01" maxTargetDistance="10" maxTargetAngle="10" speedDivision="1000" />
<path posX="80.65113067627" posY="-91.124290466309" turnVelocity="0.01" maxTargetDistance="10" maxTargetAngle="10" speedDivision="1000" />
</paths>
And make sure there is a vehicle with ID "mover" in your map file (or create it on the fly).
Example snippet.map file:
<map>
<info description="" type="map" gamemodes="snippet" author="" name="" version="1.0" />
<vehicle id="mover" model="427" posX="10" posY="0" posZ="5" rotX="0" rotY="0" rotZ="270" />
</map>
I've attached all script files for quick reference.