Torque3D/Templates/Modules/FPSGameplay/scripts/server/teleporter.cs
Areloch d680dc9934 Initial implementation of the new Base Game Template and some starting modules.
This makes some tweaks to the engine to support this, specifically, it tweaks the hardcoded shaderpaths to defer to a pref variable, so none of the shader paths are hardcoded.

Also tweaks how post effects read in texture files, removing a bizzare filepath interpretation choice, where if the file path didn't start with "/" it forcefully appended the script's file path. This made it impossible to have images not in the same dir as the script file defining the post effect.

This was changed and the existing template's post effects tweaked for now to just add "./" to those few paths impacted, as well as the perf vars to support the non-hardcoded shader paths in the engine.
2017-02-24 02:40:56 -06:00

252 lines
9.5 KiB
C#

//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Trigger-derrived teleporter object. Teleports an object from it's entrance to
// it's exit if one is defined.
function TeleporterTrigger::onAdd( %this, %teleporter )
{
// Setup default parameters.
if ( %teleporter.exit $= "" )
%teleporter.exit = "NameOfTeleporterExit";
if ( %teleporter.teleporterCooldown $= "" )
%teleporter.teleporterCooldown = %this.teleporterCooldown;
if ( %teleporter.exitVelocityScale $= "" )
%teleporter.exitVelocityScale = %this.exitVelocityScale;
if ( %teleporter.reorientPlayer $= "" )
%teleporter.reorientPlayer = %this.reorientPlayer;
if ( %teleporter.oneSided $= "" )
%teleporter.oneSided = %this.oneSided;
if ( %teleporter.entranceEffect $= "" )
%teleporter.entranceEffect = %this.entranceEffect;
if ( %teleporter.exitEffect $= "" )
%teleporter.exitEffect = %this.exitEffect;
// We do not want to save this variable between levels,
// clear it out every time the teleporter is added
// to the scene.
%teleporter.timeOfLastTeleport = "";
}
function TeleporterTrigger::onLeaveTrigger(%this,%trigger,%obj)
{
// This is called after onEnterTrigger for BOTH
// The teleporter's entrance and exit
%obj.isTeleporting = false;
}
//ARGS:
// %this - The teleporter datablock.
// %entrance - The teleporter the player has entered (The one calling this function).
// %obj - The object that entered the teleporter.
function TeleporterTrigger::onEnterTrigger(%this, %entrance, %obj)
{
// Get the location of our target position
%exit = nameToID(%entrance.exit);
// Check if the the teleport is valid.
%valid = %this.verifyObject(%obj, %entrance, %exit);
// Bail out early if we cannot complete this teleportation
if (!%valid)
return;
// Kill any players in the exit teleporter.
%this.telefrag(%obj, %exit);
// Create our entrance effects on all clients.
if (isObject(%entrance.entranceEffect))
{
for(%idx = 0; %idx < ClientGroup.getCount(); %idx++)
commandToClient(ClientGroup.getObject(%idx), 'PlayTeleportEffect', %entrance.position, %entrance.entranceEffect.getId());
}
// Teleport the player to the exit teleporter.
%this.teleportPlayer(%obj, %exit);
// Create our exit effects on all clients.
if (isObject(%exit.exitEffect))
{
for(%idx = 0; %idx < ClientGroup.getCount(); %idx++)
commandToClient(ClientGroup.getObject(%idx), 'PlayTeleportEffect', %exit.position, %exit.exitEffect.getId());
}
// Record what time we last teleported so we can determine if enough
// time has elapsed to teleport again
%entrance.timeOfLastTeleport = getSimTime();
// If this is a bidirectional teleporter, log it's exit too.
if (%exit.exit $= %entrance.name)
%exit.timeOfLastTeleport = %entrance.timeOfLastTeleport;
// Tell the client to play the 2D sound for the player that teleported.
if (isObject(%this.teleportSound) && isObject(%obj.client))
%obj.client.play2D(%this.teleportSound);
}
// Here we verify that the teleport is valid.
// Tests go here like if the object is of a 'teleportable' type, if the
// given teleporter has an exit defined, etc.
function TeleporterTrigger::verifyObject(%this, %obj, %entrance, %exit)
{
// Bail out early if we couldn't find an exit for this teleporter.
if (!isObject(%exit))
{
logError("Cound not find an exit for " @ %entrance.name @ ".");
return false;
}
// If the entrance is once sided, make sure the object
// approached it from it's front.
if (%entrance.oneSided)
{
%dotProduct = VectorDot(%entrance.getForwardVector(), %obj.getVelocity());
if (%dotProduct > 0)
return false;
}
// If we are coming directly from another teleporter and it happens
// to be bidirectional, We need to avoid ending sending objects through
// an infinite loop.
if (%obj.isTeleporting)
return false;
// We only want to teleport players
// So bail out early if we have found any
// other object.
if (!%obj.isMemberOfClass("Player"))
return false;
if (%entrance.timeOfLastTeleport > 0 && %entrance.teleporterCooldown > 0)
{
// Get the current time, subtract it from the time it last teleported
// And compare the difference to see if enough time has elapsed to
// activate the teleporter again.
%currentTime = getSimTime();
%timeDifference = %currentTime - %entrance.timeOfLastTeleport;
%db = %entrance.getDatablock();
if (%timeDifference <= %db.teleporterCooldown)
return false;
}
return true;
}
// Function to teleport object %player to teleporter %exit.
function TeleporterTrigger::teleportPlayer(%this, %player, %exit)
{
// Teleport our player to the exit teleporter.
if (%exit.reorientPlayer)
%targetPosition = %exit.getTransform();
else
{
%pos = %exit.getPosition();
%rot = getWords(%player.getTransform(), 3, 6);
%targetPosition = %pos SPC %rot;
}
%player.setTransform(%targetPosition);
// Adjust the player's velocity by the Exit location's scale.
%player.setVelocity(vectorScale(%player.getVelocity(), %exit.exitVelocityScale));
// Prevent the object from doing an immediate second teleport
// In the case of a bidirectional teleporter
%player.isTeleporting = true;
}
// Telefrag is a term used in multiplayer gaming when a player takes a teleporter
// while another player is occupying it's exit. The player at the exit location
// is killed, allowing the original player to arrive at the teleporter.
function TeleporterTrigger::teleFrag(%this, %player, %exit)
{
// When a telefrag happens, there are two cases we have to consider.
// The first case occurs when the player's bounding box is much larger than the exit location,
// it is possible to have players colide even though a player is not within the bounds
// of the trigger Because of this we first check a radius the size of a player's bounding
// box around the exit location.
// Get the bounding box of the player
%boundingBoxSize = %player.getDatablock().boundingBox;
%radius = getWord(%boundingBoxSize, 0);
%boxSizeY = getWord(%boundingBoxSize, 1);
%boxSizeZ = getWord(%boundingBoxSize, 2);
// Use the largest dimention as the radius to check
if (%boxSizeY > %radius)
%radius = %boxSizeY;
if (%boxSizeZ > %radius)
%radius = %boxSizeZ;
%position = %exit.getPosition();
%mask = $TypeMasks::PlayerObjectType;
// Check all objects within the found radius of the exit location, and telefrag
// any players that meet the conditions.
initContainerRadiusSearch( %position, %radius, %mask );
while ( (%objectNearExit = containerSearchNext()) != 0 )
{
if (!%objectNearExit.isMemberOfClass("Player"))
continue;
// Avoid killing the player that is teleporting in the case of two
// Teleporters near eachother.
if (%objectNearExit == %player)
continue;
%objectNearExit.damage(%player, %exit.getTransform(), 10000, "Telefrag");
}
// The second case occurs when the bounds of the trigger are much larger
// than the bounding box of the player. (So multiple players can exist within the
// same trigger). For this case we check all objects contained within the trigger
// and telefrag all players.
%objectsInExit = %exit.getNumObjects();
// Loop through all objects in the teleporter exit
// And kill any players
for(%i = 0; %i < %objectsInExit; %i++)
{
%objectInTeleporter = %exit.getObject(%i);
if (!%objectInTeleporter.isMemberOfClass("Player"))
continue;
// Avoid killing the player that is teleporting in the case of two
// Teleporters near eachother.
if (%objectInTeleporter == %player)
continue;
%objectInTeleporter.damage(%player, %exit.getTransform(), 10000, "Telefrag");
}
}
// Customized kill message for telefrag deaths
function sendMsgClientKilled_Telefrag(%msgType, %client, %sourceClient, %damLoc)
{
messageAll(%msgType, '%1 was telefragged by %2!', %client.playerName, %sourceClient.playerName);
}