| Author: beffy (c++programmer) |
NEW! Download / view this tutorial as PDF!
Ah well, after reading a post in the GG forums on a lazy saturday, I decided to give it a try and script a little teleport... and here it is... ;-)
The basic idea was to combine 2 (or more) triggers, animated dts shapes for the basic effect, particle effects, a little script swapping the player location/transform and finally some "buzz" sound.
Okay, so lets start with the function doing the actual teleport... I've put it into "server/scripts/commands.cs" and it looks like this:
function serverCmdTeleportPlayer(%client, %clientId, %targetObj)
{
%player = %clientId.player;
%currPlayerPos = %player.getPosition();
%targetPos = %targetObj.getPosition();
%x = getWord(%targetPos, 0);
%y = getWord(%targetPos, 1);
%z = getWord(%targetPos, 2);
// adjust z value to prevent player from falling through the terrain... :-P
%z += 3.0;
%finalPos = %x SPC %y SPC %z;
echo("Transforing from" SPC %currPlayerPos SPC "to" SPC %finalPos);
%player.setTransform(%finalPos);
}
Well, pretty basic, all it does is get the player's position and the position of the
target object indicated by %targetObj, adjusts the z value to prevent the player from
falling through the terrain and sends him to the %finalPos position.
Now lets look at the trigger, I've made a new file "fps/server/teleportTrigger.cs" which I execute in "fps/server/game.cs" (as always), and it looks like this:
datablock TriggerData(TeleportTrigger)
{
tickPeriodMS = 500;
};
datablock AudioProfile(TeleportBuzz)
{
fileName = "~/data/sound/fx/electricity.wav";
description = AudioClose3d;
preload = true;
};
function TeleportTrigger::onEnterTrigger(%data, %obj, %colObj)
{
%checkname = %obj.getName();
%client = %colObj.client;
if(!%client)
{
echo("not a client!");
return;
}
echo("Teleport client:" SPC %client);
if(%checkname $= "TeleportTrigger1")
{
// if the player didn't recently beam over here... otherwise
// he would be looping around between the two, I guess...
if(!$from2to1)
{
%target = "TeleportTrigger2";
CommandToClient(%client,'bottomprint',"Teleporter initializing... good luck... buahahaha!!",2,10);
$teleSched = schedule(2000,0,"goScotty",%client,%target);
$teleSound = serverPlay3D(TeleportBuzz,%client.player.getTransform());
%client.player.setCloaked(true);
$from1to2 = true;
$from2to1 = false;
}
}
else
{
if(!$from1to2)
{
%target = "TeleportTrigger1";
CommandToClient(%client,'bottomprint',"Teleporter initializing... good luck... buahahaha!!",2,10);
$teleSched = schedule(2000,0,"goScotty",%client, %target);
$teleSound = serverPlay3D(TeleportBuzz,%client.player.getTransform());
%client.player.setCloaked(true);
$from2to1 = true;
$from1to2 = false;
}
}
}
function TeleportTrigger::onLeaveTrigger(%data, %obj, %colObj)
{
%client = %colObj.client;
if(!%client)
{
echo("not a client!");
return;
}
%checkname = %obj.getName();
echo("TeleportTrigger::onLeaveTrigger called!");
cancel($teleSched);
alxStop($teleSound);
%client.player.setCloaked(false);
// if the player leaves the target trigger,
// he can use it, too...
if(%checkname $= "TeleportTrigger1")
{
$from2to1 = false;
}
else if(%checkname $= "TeleportTrigger2")
{
$from1to2 = false;
}
}
function goScotty(%client, %target)
{
// beam me up!
commandToServer('TeleportPlayer', %client, %target);
}
function TeleportTrigger::onTickTrigger(%data, %obj)
{
}
I've got two trigger objects in my mission file named "TeleportTrigger1" and "TeleportTrigger2",
furthermore there are the two shape files, which have their own datablock (btw., I took these shapefiles
and their datablock file "fxShapes.cs" from the latest RWTA build).
datablock StaticShapeData(MeshEffect)
{
category = "Effects";
shapeFile = "~/data/shapes/markers/flame.dts";
};
I use two vars "$from1to2" and "$from2to1" to keep track if the player already was teleported to the
recent teleport, and if he was, he has to step out of it (onLeaveTrigger), before he can use it again...
otherwise he would be trapped, I guess... ;-)
%client.player.setCloaked(true);which kinda fades the player out and makes him semi-transparent, and after he's teleported, he "fades back in"... make sure to try it in 3rd person perspective...!! :-)
new StaticShape(TeleportEffect1) {
position = "-27.6876 21.0268 100.362";
rotation = "1 0 0 90.5273";
scale = "1 1 1";
dataBlock = "MeshEffect";
};
new ParticleEmitterNode(TeleportParticle1) {
position = "-27.1392 22.8644 101.253";
rotation = "1 0 0 0";
scale = "1 1 1";
dataBlock = "defaultParticleEmitterNode";
emitter = "TeleportEmitter";
velocity = "1";
};
new StaticShape(TeleportEffect2) {
position = "-28.3129 125.469 100.391";
rotation = "1 0 0 90.5273";
scale = "1 1 1";
dataBlock = "MeshEffect";
};
new ParticleEmitterNode(TeleportParticle2) {
position = "-28.3129 127.069 101.391";
rotation = "1 0 0 0";
scale = "1 1 1";
dataBlock = "defaultParticleEmitterNode";
emitter = "TeleportEmitter";
velocity = "1";
};
new Trigger(TeleportTrigger2) {
position = "-29.4077 128.417 99.849";
rotation = "1 0 0 0";
scale = "3 2 2";
dataBlock = "TeleportTrigger";
polyhedron = "0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000
-1.0000000 0.0000000 0.0000000 0.0000000 1.0000000";
};
new Trigger(TeleportTrigger1) {
position = "-29.2559 23.685 99.5474";
rotation = "1 0 0 0";
scale = "3 2 2";
dataBlock = "TeleportTrigger";
polyhedron = "0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000
-1.0000000 0.0000000 0.0000000 0.0000000 1.0000000";
};
The ParticleEmitter looks like this, I keep all of my particle stuff in a file named "customParticles.cs",
which is also executed in ... well, you guess it!
datablock ParticleData(TeleportParticle)
{
dragCoefficient = 1.11437;
gravityCoefficient = -0.735043;
windCoefficient = 0;
inheritedVelFactor = 0.483366;
constantAcceleration = 0;
lifetimeMS = 1056;
lifetimeVarianceMS = 256;
useInvAlpha = 0;
spinRandomMin = -159;
spinRandomMax = 172;
textureName = "fps/data/shapes/rifle/smokeParticle";
times[0] = 0;
times[1] = 1;
colors[0] = "0.102362 0.070866 0.000000 0.370079";
colors[1] = "0.000000 0.102362 0.000000 0.740157";
sizes[0] = 6.08863;
sizes[1] = 0;
};
datablock ParticleEmitterData(TeleportEmitter)
{
ejectionPeriodMS = 10;
periodVarianceMS = 2;
ejectionVelocity = 2.75;
velocityVariance = 1.62;
ejectionOffset = 0;
thetaMin = 47;
thetaMax = 90;
phiReferenceVel = 144;
phiVariance = 360;
overrideAdvances = 0;
orientParticles= 0;
orientOnVelocity = 1;
particles = "TeleportParticle";
};
exec("./teleportTrigger.cs");
exec("./customParticles.cs");
exec("./fxShapes.cs");
in "fps\server\scripts\game.cs"EDIT (08. July 2002, 8pm): fixed some multiplayer problems, tutorial and zip file have been updated!
UPDATE (08. July 2002, 11:40pm):
Okay folks, to increase the fun factor I've written a new trigger file which supports multiple triggers - as many
as you want! :-)
And the best thing is, you don't have to change anything in the script, you simply execute it at startup (as always),
and you add your trigger, shape and particle objects in the editor (press F11, if you've already added
the objects like I described above, you can simply select a group of them [trigger, particle effect and shape] in the top right
editor pane by holding SHIFT, then CTRL-C, CTRL-V, rename them from "TeleportTrigger2",... to "TeleportTriggerX",...),
and that's it!
The only important thing is that the triggers have to be named "TeleportTrigger1" ... "TeleportTriggerN", cause the
script is iterating over the MissionGroup to find them through a string compare function!
dataBlock = "TeleportTrigger";to
dataBlock = "MultiTeleportTrigger";So here is the new script, but it's also included in the updated zip file (multiTeleportTrigger.cs)!
$numTeleports = 0;
datablock TriggerData(MultiTeleportTrigger)
{
tickPeriodMS = 500;
};
function MultiTeleportTrigger::onEnterTrigger(%data, %obj, %colObj)
{
if($numTeleports == 0)
{
// search for Triggers by their name once, so every trigger
// which has "TeleportTrigger" in its name is counted
$numTeleports = getMultiTriggerCount("TeleportTrigger");
echo("$numTeleports:" SPC $numTeleports);
}
%client = %colObj.client;
if(!%client)
{
echo("not a client!");
return;
}
%checkname = %obj.getName();
// if the player didn't recently beam over here... otherwise
// he would be looping around between the two, I guess...
if(%checkname !$= $currMultiTeleTrigger)
{
// pick random number, the teleport names start from 1,
// so if the random number is zero, simply take the last teleport:
%rand = getRandom($numTeleports) == 0 ? 1 : $numTeleports;
%target = "TeleportTrigger" @ %rand;
// we don't want to stay where we are...
if(%target $= %checkName)
{
// ... so increase or decrease the random number by 1
%rand = %rand+1 > $numTeleports ? %rand-1 : %rand+1;
%target = "TeleportTrigger" @ %rand;
}
echo("*** TELEPORT TARGET:" SPC %target);
CommandToClient(%client,'bottomprint',"Teleporter initializing... good luck... buahahaha!!",2,10);
$teleSched = schedule(2000,0,"goScotty",%client,%target);
$teleSound = serverPlay3D(TeleportBuzz,%client.player.getTransform());
%client.player.setCloaked(true);
// save the target - until the teleported client leaves it, then reset
$currMultiTeleTrigger = %target;
}
}
function MultiTeleportTrigger::onLeaveTrigger(%data, %obj, %colObj)
{
%client = %colObj.client;
if(!%client)
{
echo("not a client!");
return;
}
%checkname = %obj.getName();
cancel($teleSched);
alxStop($teleSound);
%client.player.setCloaked(false);
// if the player leaves the target trigger,
// he can use it again, too... so reset the global var
if(%checkname $= $currMultiTeleTrigger)
{
$currMultiTeleTrigger = "";
}
}
function MultiTeleportTrigger::onTickTrigger(%data, %obj)
{
}
// *********************************
// Helper functions
// *********************************
// do the teleport
function goScotty(%client, %target)
{
echo("goScotty called!");
// beam me up!
commandToServer('TeleportPlayer', %client, %target);
}
// find the number of teleport triggers by name comparison
function getMultiTriggerCount(%name)
{
%dataGroup = "MissionGroup";
%triggerCount = 0;
for(%i = 0; %i < %dataGroup.getCount(); %i++)
{
%obj = %dataGroup.getObject(%i);
if(%obj.getClassName() !$= "Trigger")
{
// no trigger!
continue;
}
if((strStr(%obj.getDatablock().getName(), %name) != -1) && isObject(%obj))
{
echo(%i SPC "Found Trigger:" SPC %obj.getName());
%triggerCount++;
}
}
return %triggerCount;
}