mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-03-01 03:23:52 +00:00
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.
This commit is contained in:
parent
5c8a82180b
commit
d680dc9934
2321 changed files with 296541 additions and 85 deletions
106
Templates/Modules/FPSGameplay/scripts/server/VolumetricFog.cs
Normal file
106
Templates/Modules/FPSGameplay/scripts/server/VolumetricFog.cs
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function VolumetricFog::onEnterFog(%this,%obj)
|
||||
{
|
||||
// This method is called whenever the control object (Camera or Player)
|
||||
// %obj enters the fog area.
|
||||
|
||||
// echo("Control Object " @ %obj @ " enters fog " @ %this);
|
||||
}
|
||||
|
||||
function VolumetricFog::onLeaveFog(%this,%obj)
|
||||
{
|
||||
// This method is called whenever the control object (Camera or Player)
|
||||
// %obj leaves the fog area.
|
||||
|
||||
// echo("Control Object " @ %obj @ " left fog " @ %this);
|
||||
}
|
||||
|
||||
function VolumetricFog::Dissolve(%this,%speed,%delete)
|
||||
{
|
||||
// This method dissolves the fog at speed milliseconds
|
||||
%this.isBuilding = true;
|
||||
if (%this.FogDensity > 0)
|
||||
{
|
||||
%this.setFogDensity(%this.FogDensity - 0.005);
|
||||
%this.schedule(%speed,Dissolve,%speed,%delete);
|
||||
}
|
||||
else
|
||||
{
|
||||
%this.isBuilding = false;
|
||||
%this.SetFogDensity(0.0);
|
||||
if (%delete !$= "" && %delete !$="0" && %delete !$="false")
|
||||
%this.schedule(250,delete);
|
||||
}
|
||||
}
|
||||
|
||||
function VolumetricFog::Thicken(%this,%speed, %end_density)
|
||||
{
|
||||
// This method thickens the fog at speed milliseconds to a density of %end_density
|
||||
|
||||
%this.isBuilding = true;
|
||||
if (%this.FogDensity + 0.005 < %end_density)
|
||||
{
|
||||
%this.setFogDensity(%this.FogDensity + 0.005);
|
||||
%this.schedule(%speed,Thicken,%speed, %end_density);
|
||||
}
|
||||
else
|
||||
{
|
||||
%this.setFogDensity(%end_density);
|
||||
%this.isBuilding = false;
|
||||
}
|
||||
}
|
||||
|
||||
function GenerateFog(%pos,%scale,%color,%density)
|
||||
{
|
||||
// This function can be used to generate some fog caused by massive gunfire etc.
|
||||
// Change shape and modulation data to your likings.
|
||||
|
||||
%fog=new VolumetricFog() {
|
||||
shapeName = "data/FPSGameplay/art/environment/Fog_Sphere.dts";
|
||||
fogColor = %color;
|
||||
fogDensity = "0.0";
|
||||
ignoreWater = "0";
|
||||
MinSize = "250";
|
||||
FadeSize = "750";
|
||||
texture = "data/FPSGameplay/art/environment/FogMod_heavy.dds";
|
||||
tiles = "1";
|
||||
modStrength = "0.2";
|
||||
PrimSpeed = "-0.01 0.04";
|
||||
SecSpeed = "0.02 0.02";
|
||||
position = %pos;
|
||||
rotation = "0 0 1 20.354";
|
||||
scale = %scale;
|
||||
canSave = "1";
|
||||
canSaveDynamicFields = "1";
|
||||
};
|
||||
|
||||
if (isObject(%fog))
|
||||
{
|
||||
MissionCleanup.add(%fog);
|
||||
|
||||
%fog.Thicken(500,%density);
|
||||
}
|
||||
|
||||
return %fog;
|
||||
}
|
||||
333
Templates/Modules/FPSGameplay/scripts/server/aiPlayer.cs
Normal file
333
Templates/Modules/FPSGameplay/scripts/server/aiPlayer.cs
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// AIPlayer callbacks
|
||||
// The AIPlayer class implements the following callbacks:
|
||||
//
|
||||
// PlayerData::onStop(%this,%obj)
|
||||
// PlayerData::onMove(%this,%obj)
|
||||
// PlayerData::onReachDestination(%this,%obj)
|
||||
// PlayerData::onMoveStuck(%this,%obj)
|
||||
// PlayerData::onTargetEnterLOS(%this,%obj)
|
||||
// PlayerData::onTargetExitLOS(%this,%obj)
|
||||
// PlayerData::onAdd(%this,%obj)
|
||||
//
|
||||
// Since the AIPlayer doesn't implement it's own datablock, these callbacks
|
||||
// all take place in the PlayerData namespace.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Demo Pathed AIPlayer.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function DemoPlayer::onReachDestination(%this,%obj)
|
||||
{
|
||||
//echo( %obj @ " onReachDestination" );
|
||||
|
||||
// Moves to the next node on the path.
|
||||
// Override for all player. Normally we'd override this for only
|
||||
// a specific player datablock or class of players.
|
||||
if (%obj.path !$= "")
|
||||
{
|
||||
if (%obj.currentNode == %obj.targetNode)
|
||||
%this.onEndOfPath(%obj,%obj.path);
|
||||
else
|
||||
%obj.moveToNextNode();
|
||||
}
|
||||
}
|
||||
|
||||
function DemoPlayer::onMoveStuck(%this,%obj)
|
||||
{
|
||||
//echo( %obj @ " onMoveStuck" );
|
||||
}
|
||||
|
||||
function DemoPlayer::onTargetExitLOS(%this,%obj)
|
||||
{
|
||||
//echo( %obj @ " onTargetExitLOS" );
|
||||
}
|
||||
|
||||
function DemoPlayer::onTargetEnterLOS(%this,%obj)
|
||||
{
|
||||
//echo( %obj @ " onTargetEnterLOS" );
|
||||
}
|
||||
|
||||
function DemoPlayer::onEndOfPath(%this,%obj,%path)
|
||||
{
|
||||
%obj.nextTask();
|
||||
}
|
||||
|
||||
function DemoPlayer::onEndSequence(%this,%obj,%slot)
|
||||
{
|
||||
echo("Sequence Done!");
|
||||
%obj.stopThread(%slot);
|
||||
%obj.nextTask();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// AIPlayer static functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function AIPlayer::spawnAtLocation(%name, %spawnPoint)
|
||||
{
|
||||
// Create the demo player object
|
||||
%player = new AiPlayer()
|
||||
{
|
||||
dataBlock = DemoPlayer;
|
||||
path = "";
|
||||
};
|
||||
MissionCleanup.add(%player);
|
||||
%player.setShapeName(%name);
|
||||
%player.setTransform(%spawnPoint);
|
||||
return %player;
|
||||
}
|
||||
|
||||
function AIPlayer::spawnOnPath(%name, %path)
|
||||
{
|
||||
// Spawn a player and place him on the first node of the path
|
||||
if (!isObject(%path))
|
||||
return 0;
|
||||
%node = %path.getObject(0);
|
||||
%player = AIPlayer::spawnAtLocation(%name, %node.getTransform());
|
||||
return %player;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// AIPlayer methods
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function AIPlayer::followPath(%this,%path,%node)
|
||||
{
|
||||
// Start the player following a path
|
||||
if (!isObject(%path))
|
||||
{
|
||||
%this.path = "";
|
||||
return;
|
||||
}
|
||||
|
||||
if (%node > %path.getCount() - 1)
|
||||
%this.targetNode = %path.getCount() - 1;
|
||||
else
|
||||
%this.targetNode = %node;
|
||||
|
||||
if (%this.path $= %path)
|
||||
%this.moveToNode(%this.currentNode);
|
||||
else
|
||||
{
|
||||
%this.path = %path;
|
||||
%this.moveToNode(0);
|
||||
}
|
||||
}
|
||||
|
||||
function AIPlayer::moveToNextNode(%this)
|
||||
{
|
||||
%pathNodeCount=%this.path.getCount();
|
||||
%slowdown=0;
|
||||
|
||||
%targetNode=%this.currentNode + 1;
|
||||
|
||||
if (%this.path.isLooping) {
|
||||
%targetNode %= %pathNodeCount;
|
||||
} else {
|
||||
if (%targetNode >= %pathNodeCount-1) {
|
||||
%targetNode=%pathNodeCount-1;
|
||||
|
||||
if (%currentNode < %targetNode)
|
||||
%slowdown=1;
|
||||
}
|
||||
}
|
||||
|
||||
%this.moveToNode(%targetNode, %slowdown);
|
||||
}
|
||||
|
||||
function AIPlayer::moveToNode(%this,%index,%slowdown)
|
||||
{
|
||||
// Move to the given path node index
|
||||
%this.currentNode = %index;
|
||||
%node = %this.path.getObject(%index);
|
||||
%this.setMoveDestination(%node.getTransform(),%slowdown);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function AIPlayer::pushTask(%this,%method)
|
||||
{
|
||||
if (%this.taskIndex $= "")
|
||||
{
|
||||
%this.taskIndex = 0;
|
||||
%this.taskCurrent = -1;
|
||||
}
|
||||
%this.task[%this.taskIndex] = %method;
|
||||
%this.taskIndex++;
|
||||
if (%this.taskCurrent == -1)
|
||||
%this.executeTask(%this.taskIndex - 1);
|
||||
}
|
||||
|
||||
function AIPlayer::clearTasks(%this)
|
||||
{
|
||||
%this.taskIndex = 0;
|
||||
%this.taskCurrent = -1;
|
||||
}
|
||||
|
||||
function AIPlayer::nextTask(%this)
|
||||
{
|
||||
if (%this.taskCurrent != -1)
|
||||
if (%this.taskCurrent < %this.taskIndex - 1)
|
||||
%this.executeTask(%this.taskCurrent++);
|
||||
else
|
||||
%this.taskCurrent = -1;
|
||||
}
|
||||
|
||||
function AIPlayer::executeTask(%this,%index)
|
||||
{
|
||||
%this.taskCurrent = %index;
|
||||
eval(%this.getId() @"."@ %this.task[%index] @";");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function AIPlayer::singleShot(%this)
|
||||
{
|
||||
// The shooting delay is used to pulse the trigger
|
||||
%this.setImageTrigger(0, true);
|
||||
%this.setImageTrigger(0, false);
|
||||
%delay = %this.getDataBlock().shootingDelay;
|
||||
if (%delay $= "")
|
||||
%delay = 1000;
|
||||
%this.trigger = %this.schedule(%delay, singleShot);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function AIPlayer::wait(%this, %time)
|
||||
{
|
||||
%this.schedule(%time * 1000, "nextTask");
|
||||
}
|
||||
|
||||
function AIPlayer::done(%this,%time)
|
||||
{
|
||||
%this.schedule(0, "delete");
|
||||
}
|
||||
|
||||
function AIPlayer::fire(%this,%bool)
|
||||
{
|
||||
if (%bool)
|
||||
{
|
||||
cancel(%this.trigger);
|
||||
%this.singleShot();
|
||||
}
|
||||
else
|
||||
cancel(%this.trigger);
|
||||
%this.nextTask();
|
||||
}
|
||||
|
||||
function AIPlayer::aimAt(%this,%object)
|
||||
{
|
||||
echo("Aim: "@ %object);
|
||||
%this.setAimObject(%object);
|
||||
%this.nextTask();
|
||||
}
|
||||
|
||||
function AIPlayer::animate(%this,%seq)
|
||||
{
|
||||
//%this.stopThread(0);
|
||||
//%this.playThread(0,%seq);
|
||||
%this.setActionThread(%seq);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Some handy getDistance/nearestTarget functions for the AI to use
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function AIPlayer::getTargetDistance(%this, %target)
|
||||
{
|
||||
echo("\c4AIPlayer::getTargetDistance("@ %this @", "@ %target @")");
|
||||
$tgt = %target;
|
||||
%tgtPos = %target.getPosition();
|
||||
%eyePoint = %this.getWorldBoxCenter();
|
||||
%distance = VectorDist(%tgtPos, %eyePoint);
|
||||
echo("Distance to target = "@ %distance);
|
||||
return %distance;
|
||||
}
|
||||
|
||||
function AIPlayer::getNearestPlayerTarget(%this)
|
||||
{
|
||||
echo("\c4AIPlayer::getNearestPlayerTarget("@ %this @")");
|
||||
|
||||
%index = -1;
|
||||
%botPos = %this.getPosition();
|
||||
%count = ClientGroup.getCount();
|
||||
for(%i = 0; %i < %count; %i++)
|
||||
{
|
||||
%client = ClientGroup.getObject(%i);
|
||||
if (%client.player $= "" || %client.player == 0)
|
||||
return -1;
|
||||
%playerPos = %client.player.getPosition();
|
||||
|
||||
%tempDist = VectorDist(%playerPos, %botPos);
|
||||
if (%i == 0)
|
||||
{
|
||||
%dist = %tempDist;
|
||||
%index = %i;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (%dist > %tempDist)
|
||||
{
|
||||
%dist = %tempDist;
|
||||
%index = %i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return %index;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function AIPlayer::think(%player)
|
||||
{
|
||||
// Thinking allows us to consider other things...
|
||||
%player.schedule(500, think);
|
||||
}
|
||||
|
||||
function AIPlayer::spawn(%path)
|
||||
{
|
||||
%player = AIPlayer::spawnOnPath("Shootme", %path);
|
||||
|
||||
if (isObject(%player))
|
||||
{
|
||||
%player.followPath(%path, -1);
|
||||
|
||||
// slow this sucker down, I'm tired of chasing him!
|
||||
%player.setMoveSpeed(0.5);
|
||||
|
||||
//%player.mountImage(xxxImage, 0);
|
||||
//%player.setInventory(xxxAmmo, 1000);
|
||||
//%player.think();
|
||||
|
||||
return %player;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
85
Templates/Modules/FPSGameplay/scripts/server/camera.cs
Normal file
85
Templates/Modules/FPSGameplay/scripts/server/camera.cs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Global movement speed that affects all cameras. This should be moved
|
||||
// into the camera datablock.
|
||||
$Camera::movementSpeed = 30;
|
||||
|
||||
function Observer::onTrigger(%this,%obj,%trigger,%state)
|
||||
{
|
||||
// state = 0 means that a trigger key was released
|
||||
if (%state == 0)
|
||||
return;
|
||||
|
||||
// Default player triggers: 0=fire 1=altFire 2=jump
|
||||
%client = %obj.getControllingClient();
|
||||
switch$ (%obj.mode)
|
||||
{
|
||||
case "Observer":
|
||||
// Do something interesting.
|
||||
|
||||
case "Corpse":
|
||||
// Viewing dead corpse, so we probably want to respawn.
|
||||
$Game.preparePlayer(%client);
|
||||
|
||||
// Set the camera back into observer mode, since in
|
||||
// debug mode we like to switch to it.
|
||||
%this.setMode(%obj,"Observer");
|
||||
}
|
||||
}
|
||||
|
||||
function Observer::setMode(%this,%obj,%mode,%arg1,%arg2,%arg3)
|
||||
{
|
||||
switch$ (%mode)
|
||||
{
|
||||
case "Observer":
|
||||
// Let the player fly around
|
||||
%obj.setFlyMode();
|
||||
|
||||
case "Corpse":
|
||||
// Lock the camera down in orbit around the corpse,
|
||||
// which should be arg1
|
||||
%transform = %arg1.getTransform();
|
||||
%obj.setOrbitMode(%arg1, %transform, 0.5, 4.5, 4.5);
|
||||
|
||||
}
|
||||
%obj.mode = %mode;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Camera methods
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function Camera::onAdd(%this,%obj)
|
||||
{
|
||||
// Default start mode
|
||||
%this.setMode(%this.mode);
|
||||
}
|
||||
|
||||
function Camera::setMode(%this,%mode,%arg1,%arg2,%arg3)
|
||||
{
|
||||
// Punt this one over to our datablock
|
||||
%this.getDatablock().setMode(%this,%mode,%arg1,%arg2,%arg3);
|
||||
}
|
||||
92
Templates/Modules/FPSGameplay/scripts/server/chat.cs
Normal file
92
Templates/Modules/FPSGameplay/scripts/server/chat.cs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
//---------------------------------------------------------------------------
|
||||
// Server side client chat'n
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// silly spam protection...
|
||||
$SPAM_PROTECTION_PERIOD = 10000;
|
||||
$SPAM_MESSAGE_THRESHOLD = 4;
|
||||
$SPAM_PENALTY_PERIOD = 10000;
|
||||
$SPAM_MESSAGE = '\c3FLOOD PROTECTION:\cr You must wait another %1 seconds.';
|
||||
|
||||
function GameConnection::spamMessageTimeout(%this)
|
||||
{
|
||||
if(%this.spamMessageCount > 0)
|
||||
%this.spamMessageCount--;
|
||||
}
|
||||
|
||||
function GameConnection::spamReset(%this)
|
||||
{
|
||||
%this.isSpamming = false;
|
||||
}
|
||||
|
||||
function spamAlert(%client)
|
||||
{
|
||||
if($Pref::Server::FloodProtectionEnabled != true)
|
||||
return(false);
|
||||
|
||||
if(!%client.isSpamming && (%client.spamMessageCount >= $SPAM_MESSAGE_THRESHOLD))
|
||||
{
|
||||
%client.spamProtectStart = getSimTime();
|
||||
%client.isSpamming = true;
|
||||
%client.schedule($SPAM_PENALTY_PERIOD, spamReset);
|
||||
}
|
||||
|
||||
if(%client.isSpamming)
|
||||
{
|
||||
%wait = mFloor(($SPAM_PENALTY_PERIOD - (getSimTime() - %client.spamProtectStart)) / 1000);
|
||||
messageClient(%client, "", $SPAM_MESSAGE, %wait);
|
||||
return(true);
|
||||
}
|
||||
|
||||
%client.spamMessageCount++;
|
||||
%client.schedule($SPAM_PROTECTION_PERIOD, spamMessageTimeout);
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
function chatMessageClient( %client, %sender, %voiceTag, %voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 )
|
||||
{
|
||||
//see if the client has muted the sender
|
||||
if ( !%client.muted[%sender] )
|
||||
commandToClient( %client, 'ChatMessage', %sender, %voiceTag, %voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 );
|
||||
}
|
||||
|
||||
function chatMessageTeam( %sender, %team, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 )
|
||||
{
|
||||
if ( ( %msgString $= "" ) || spamAlert( %sender ) )
|
||||
return;
|
||||
|
||||
%count = ClientGroup.getCount();
|
||||
|
||||
for ( %i = 0; %i < %count; %i++ )
|
||||
{
|
||||
%obj = ClientGroup.getObject( %i );
|
||||
if ( %obj.team == %sender.team )
|
||||
chatMessageClient( %obj, %sender, %sender.voiceTag, %sender.voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 );
|
||||
}
|
||||
}
|
||||
|
||||
function chatMessageAll( %sender, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 )
|
||||
{
|
||||
if ( ( %msgString $= "" ) || spamAlert( %sender ) )
|
||||
return;
|
||||
|
||||
%count = ClientGroup.getCount();
|
||||
|
||||
for ( %i = 0; %i < %count; %i++ )
|
||||
{
|
||||
%obj = ClientGroup.getObject( %i );
|
||||
|
||||
if(%sender.team != 0)
|
||||
chatMessageClient( %obj, %sender, %sender.voiceTag, %sender.voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 );
|
||||
else
|
||||
{
|
||||
// message sender is an observer -- only send message to other observers
|
||||
if(%obj.team == %sender.team)
|
||||
chatMessageClient( %obj, %sender, %sender.voiceTag, %sender.voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 );
|
||||
}
|
||||
}
|
||||
}
|
||||
162
Templates/Modules/FPSGameplay/scripts/server/cheetah.cs
Normal file
162
Templates/Modules/FPSGameplay/scripts/server/cheetah.cs
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function CheetahCar::onAdd(%this, %obj)
|
||||
{
|
||||
Parent::onAdd(%this, %obj);
|
||||
|
||||
%obj.setWheelTire(0,CheetahCarTire);
|
||||
%obj.setWheelTire(1,CheetahCarTire);
|
||||
%obj.setWheelTire(2,CheetahCarTireRear);
|
||||
%obj.setWheelTire(3,CheetahCarTireRear);
|
||||
|
||||
// Setup the car with some tires & springs
|
||||
for (%i = %obj.getWheelCount() - 1; %i >= 0; %i--)
|
||||
{
|
||||
%obj.setWheelPowered(%i, true);
|
||||
%obj.setWheelSpring(%i, CheetahCarSpring);
|
||||
}
|
||||
|
||||
// Steer with the front tires
|
||||
%obj.setWheelSteering(0, 1);
|
||||
%obj.setWheelSteering(1, 1);
|
||||
|
||||
// Add tail lights
|
||||
%obj.rightBrakeLight = new PointLight()
|
||||
{
|
||||
radius = "1";
|
||||
isEnabled = "0";
|
||||
color = "1 0 0.141176 1";
|
||||
brightness = "2";
|
||||
castShadows = "1";
|
||||
priority = "1";
|
||||
animate = "0";
|
||||
animationPeriod = "1";
|
||||
animationPhase = "1";
|
||||
flareScale = "1";
|
||||
attenuationRatio = "0 1 1";
|
||||
shadowType = "DualParaboloidSinglePass";
|
||||
texSize = "512";
|
||||
overDarkFactor = "2000 1000 500 100";
|
||||
shadowDistance = "400";
|
||||
shadowSoftness = "0.15";
|
||||
numSplits = "1";
|
||||
logWeight = "0.91";
|
||||
fadeStartDistance = "0";
|
||||
lastSplitTerrainOnly = "0";
|
||||
representedInLightmap = "0";
|
||||
shadowDarkenColor = "0 0 0 -1";
|
||||
includeLightmappedGeometryInShadow = "0";
|
||||
rotation = "1 0 0 0";
|
||||
canSave = "1";
|
||||
canSaveDynamicFields = "1";
|
||||
splitFadeDistances = "10 20 30 40";
|
||||
};
|
||||
%obj.leftBrakeLight = new PointLight()
|
||||
{
|
||||
radius = "1";
|
||||
isEnabled = "0";
|
||||
color = "1 0 0.141176 1";
|
||||
brightness = "2";
|
||||
castShadows = "1";
|
||||
priority = "1";
|
||||
animate = "0";
|
||||
animationPeriod = "1";
|
||||
animationPhase = "1";
|
||||
flareScale = "1";
|
||||
attenuationRatio = "0 1 1";
|
||||
shadowType = "DualParaboloidSinglePass";
|
||||
texSize = "512";
|
||||
overDarkFactor = "2000 1000 500 100";
|
||||
shadowDistance = "400";
|
||||
shadowSoftness = "0.15";
|
||||
numSplits = "1";
|
||||
logWeight = "0.91";
|
||||
fadeStartDistance = "0";
|
||||
lastSplitTerrainOnly = "0";
|
||||
representedInLightmap = "0";
|
||||
shadowDarkenColor = "0 0 0 -1";
|
||||
includeLightmappedGeometryInShadow = "0";
|
||||
rotation = "1 0 0 0";
|
||||
canSave = "1";
|
||||
canSaveDynamicFields = "1";
|
||||
splitFadeDistances = "10 20 30 40";
|
||||
};
|
||||
|
||||
// Mount a ShapeBaseImageData
|
||||
%didMount = %obj.mountImage(TurretImage, %this.turretSlot);
|
||||
|
||||
// Mount the brake lights
|
||||
%obj.mountObject(%obj.rightBrakeLight, %this.rightBrakeSlot);
|
||||
%obj.mountObject(%obj.leftBrakeLight, %this.leftBrakeSlot);
|
||||
}
|
||||
|
||||
function CheetahCar::onRemove(%this, %obj)
|
||||
{
|
||||
Parent::onRemove(%this, %obj);
|
||||
|
||||
if(isObject(%obj.rightBrakeLight))
|
||||
%obj.rightBrakeLight.delete();
|
||||
|
||||
if(isObject(%obj.leftBrakeLight))
|
||||
%obj.leftBrakeLight.delete();
|
||||
|
||||
if(isObject(%obj.turret))
|
||||
%obj.turret.delete();
|
||||
}
|
||||
|
||||
function serverCmdtoggleBrakeLights(%client)
|
||||
{
|
||||
%car = %client.player.getControlObject();
|
||||
|
||||
if (%car.getClassName() $= "WheeledVehicle")
|
||||
{
|
||||
if(%car.rightBrakeLight.isEnabled)
|
||||
{
|
||||
%car.rightBrakeLight.setLightEnabled(0);
|
||||
%car.leftBrakeLight.setLightEnabled(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
%car.rightBrakeLight.setLightEnabled(1);
|
||||
%car.leftBrakeLight.setLightEnabled(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Callback invoked when an input move trigger state changes when the CheetahCar
|
||||
// is the control object
|
||||
function CheetahCar::onTrigger(%this, %obj, %index, %state)
|
||||
{
|
||||
// Pass trigger states on to TurretImage (to fire weapon)
|
||||
switch ( %index )
|
||||
{
|
||||
case 0: %obj.setImageTrigger( %this.turretSlot, %state );
|
||||
case 1: %obj.setImageAltTrigger( %this.turretSlot, %state );
|
||||
}
|
||||
}
|
||||
|
||||
function TurretImage::onMount(%this, %obj, %slot)
|
||||
{
|
||||
// Load the gun
|
||||
%obj.setImageAmmo(%slot, true);
|
||||
}
|
||||
140
Templates/Modules/FPSGameplay/scripts/server/commands.cs
Normal file
140
Templates/Modules/FPSGameplay/scripts/server/commands.cs
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Misc server commands avialable to clients
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function serverCmdSuicide(%client)
|
||||
{
|
||||
if (isObject(%client.player))
|
||||
%client.player.kill("Suicide");
|
||||
}
|
||||
|
||||
function serverCmdPlayCel(%client,%anim)
|
||||
{
|
||||
if (isObject(%client.player))
|
||||
%client.player.playCelAnimation(%anim);
|
||||
}
|
||||
|
||||
function serverCmdTestAnimation(%client, %anim)
|
||||
{
|
||||
if (isObject(%client.player))
|
||||
%client.player.playTestAnimation(%anim);
|
||||
}
|
||||
|
||||
function serverCmdPlayDeath(%client)
|
||||
{
|
||||
if (isObject(%client.player))
|
||||
%client.player.playDeathAnimation();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Throw/Toss
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function serverCmdThrow(%client, %data)
|
||||
{
|
||||
%player = %client.player;
|
||||
if(!isObject(%player) || %player.getState() $= "Dead" || !$Game::Running)
|
||||
return;
|
||||
switch$ (%data)
|
||||
{
|
||||
case "Weapon":
|
||||
%item = (%player.getMountedImage($WeaponSlot) == 0) ? "" : %player.getMountedImage($WeaponSlot).item;
|
||||
if (%item !$="")
|
||||
%player.throw(%item);
|
||||
case "Ammo":
|
||||
%weapon = (%player.getMountedImage($WeaponSlot) == 0) ? "" : %player.getMountedImage($WeaponSlot);
|
||||
if (%weapon !$= "")
|
||||
{
|
||||
if(%weapon.ammo !$= "")
|
||||
%player.throw(%weapon.ammo);
|
||||
}
|
||||
default:
|
||||
if(%player.hasInventory(%data.getName()))
|
||||
%player.throw(%data);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Force game end and cycle
|
||||
// Probably don't want this in a final game without some checks. Anyone could
|
||||
// restart a game.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function serverCmdFinishGame()
|
||||
{
|
||||
cycleGame();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Cycle weapons
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function serverCmdCycleWeapon(%client, %direction)
|
||||
{
|
||||
%client.getControlObject().cycleWeapon(%direction);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Unmount current weapon
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function serverCmdUnmountWeapon(%client)
|
||||
{
|
||||
%client.getControlObject().unmountImage($WeaponSlot);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Weapon reloading
|
||||
// ----------------------------------------------------------------------------
|
||||
function serverCmdReloadWeapon(%client)
|
||||
{
|
||||
%player = %client.getControlObject();
|
||||
%image = %player.getMountedImage($WeaponSlot);
|
||||
|
||||
// Don't reload if the weapon's full.
|
||||
if (%player.getInventory(%image.ammo) == %image.ammo.maxInventory)
|
||||
return;
|
||||
|
||||
if (%image > 0)
|
||||
%image.clearAmmoClip(%player, $WeaponSlot);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Server chat message handlers
|
||||
//----------------------------------------------------------------------------
|
||||
function serverCmdTeamMessageSent(%client, %text)
|
||||
{
|
||||
if(strlen(%text) >= $Pref::Server::MaxChatLen)
|
||||
%text = getSubStr(%text, 0, $Pref::Server::MaxChatLen);
|
||||
chatMessageTeam(%client, %client.team, '\c3%1: %2', %client.playerName, %text);
|
||||
}
|
||||
|
||||
function serverCmdMessageSent(%client, %text)
|
||||
{
|
||||
if(strlen(%text) >= $Pref::Server::MaxChatLen)
|
||||
%text = getSubStr(%text, 0, $Pref::Server::MaxChatLen);
|
||||
chatMessageAll(%client, '\c4%1: %2', %client.playerName, %text);
|
||||
}
|
||||
725
Templates/Modules/FPSGameplay/scripts/server/deathMatchGame.cs
Normal file
725
Templates/Modules/FPSGameplay/scripts/server/deathMatchGame.cs
Normal file
|
|
@ -0,0 +1,725 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DeathmatchGame
|
||||
// ----------------------------------------------------------------------------
|
||||
// Depends on methods found in gameCore.cs. Those added here are specific to
|
||||
// this game type and/or over-ride the "default" game functionaliy.
|
||||
//
|
||||
// The desired Game Type must be added to each mission's LevelInfo object.
|
||||
// - gameType = "Deathmatch";
|
||||
// If this information is missing then the GameCore will default to Deathmatch.
|
||||
// ----------------------------------------------------------------------------
|
||||
function DeathMatchGame::initGameVars(%game)
|
||||
{
|
||||
//echo (%game @"\c4 -> "@ %game.class @" -> DeathMatchGame::initGameVars");
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// What kind of "player" is spawned is either controlled directly by the
|
||||
// SpawnSphere or it defaults back to the values set here. This also controls
|
||||
// which SimGroups to attempt to select the spawn sphere's from by walking down
|
||||
// the list of SpawnGroups till it finds a valid spawn object.
|
||||
// These override the values set in core/scripts/server/spawn.cs
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Leave $Game::defaultPlayerClass and $Game::defaultPlayerDataBlock as empty strings ("")
|
||||
// to spawn a the $Game::defaultCameraClass as the control object.
|
||||
$Game::defaultPlayerClass = "Player";
|
||||
$Game::defaultPlayerDataBlock = "DefaultPlayerData";
|
||||
$Game::defaultPlayerSpawnGroups = "PlayerSpawnPoints PlayerDropPoints";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// What kind of "camera" is spawned is either controlled directly by the
|
||||
// SpawnSphere or it defaults back to the values set here. This also controls
|
||||
// which SimGroups to attempt to select the spawn sphere's from by walking down
|
||||
// the list of SpawnGroups till it finds a valid spawn object.
|
||||
// These override the values set in core/scripts/server/spawn.cs
|
||||
//-----------------------------------------------------------------------------
|
||||
$Game::defaultCameraClass = "Camera";
|
||||
$Game::defaultCameraDataBlock = "Observer";
|
||||
$Game::defaultCameraSpawnGroups = "CameraSpawnPoints PlayerSpawnPoints PlayerDropPoints";
|
||||
|
||||
// Set the gameplay parameters
|
||||
%game.duration = 30 * 60;
|
||||
%game.endgameScore = 20;
|
||||
%game.endgamePause = 10;
|
||||
%game.allowCycling = false; // Is mission cycling allowed?
|
||||
}
|
||||
|
||||
function DeathMatchGame::onGameDurationEnd(%game)
|
||||
{
|
||||
// This "redirect" is here so that we can abort the game cycle if
|
||||
// the $Game::Duration variable has been cleared, without having
|
||||
// to have a function to cancel the schedule.
|
||||
|
||||
if ($Game::Duration && !(EditorIsActive() && GuiEditorIsActive()))
|
||||
Game.onGameDurationEnd();
|
||||
}
|
||||
|
||||
function DeathMatchGame::onClientEnterGame(%this, %client)
|
||||
{
|
||||
// This function currently relies on some helper functions defined in
|
||||
// core/scripts/spawn.cs. For custom spawn behaviors one can either
|
||||
// override the properties on the SpawnSphere's or directly override the
|
||||
// functions themselves.
|
||||
|
||||
//echo (%game @"\c4 -> "@ %game.class @" -> GameCore::onClientEntergame");
|
||||
|
||||
// Sync the client's clocks to the server's
|
||||
commandToClient(%client, 'SyncClock', $Sim::Time - $Game::StartTime);
|
||||
|
||||
//Set the player name based on the client's connection data
|
||||
%client.setPlayerName(%client.connectData);
|
||||
|
||||
// Find a spawn point for the camera
|
||||
// This function currently relies on some helper functions defined in
|
||||
// core/scripts/server/spawn.cs. For custom spawn behaviors one can either
|
||||
// override the properties on the SpawnSphere's or directly override the
|
||||
// functions themselves.
|
||||
%cameraSpawnPoint = pickCameraSpawnPoint($Game::DefaultCameraSpawnGroups);
|
||||
// Spawn a camera for this client using the found %spawnPoint
|
||||
%client.spawnCamera(%cameraSpawnPoint);
|
||||
|
||||
// Setup game parameters, the onConnect method currently starts
|
||||
// everyone with a 0 score.
|
||||
%client.score = 0;
|
||||
%client.kills = 0;
|
||||
%client.deaths = 0;
|
||||
|
||||
// weaponHUD
|
||||
%client.RefreshWeaponHud(0, "", "");
|
||||
|
||||
// Prepare the player object.
|
||||
%this.preparePlayer(%client);
|
||||
|
||||
// Inform the client of all the other clients
|
||||
%count = ClientGroup.getCount();
|
||||
for (%cl = 0; %cl < %count; %cl++)
|
||||
{
|
||||
%other = ClientGroup.getObject(%cl);
|
||||
if ((%other != %client))
|
||||
{
|
||||
// These should be "silent" versions of these messages...
|
||||
messageClient(%client, 'MsgClientJoin', "",
|
||||
%other.playerName,
|
||||
%other,
|
||||
%other.sendGuid,
|
||||
%other.team,
|
||||
%other.score,
|
||||
%other.kills,
|
||||
%other.deaths,
|
||||
%other.isAIControlled(),
|
||||
%other.isAdmin,
|
||||
%other.isSuperAdmin);
|
||||
}
|
||||
}
|
||||
|
||||
// Inform the client we've joined up
|
||||
messageClient(%client,
|
||||
'MsgClientJoin', '\c2Welcome to the Torque demo app %1.',
|
||||
%client.playerName,
|
||||
%client,
|
||||
%client.sendGuid,
|
||||
%client.team,
|
||||
%client.score,
|
||||
%client.kills,
|
||||
%client.deaths,
|
||||
%client.isAiControlled(),
|
||||
%client.isAdmin,
|
||||
%client.isSuperAdmin);
|
||||
|
||||
// Inform all the other clients of the new guy
|
||||
messageAllExcept(%client, -1, 'MsgClientJoin', '\c1%1 joined the game.',
|
||||
%client.playerName,
|
||||
%client,
|
||||
%client.sendGuid,
|
||||
%client.team,
|
||||
%client.score,
|
||||
%client.kills,
|
||||
%client.deaths,
|
||||
%client.isAiControlled(),
|
||||
%client.isAdmin,
|
||||
%client.isSuperAdmin);
|
||||
}
|
||||
|
||||
function DeathMatchGame::onClientLeaveGame(%this, %client)
|
||||
{
|
||||
// Cleanup the camera
|
||||
if (isObject(%this.camera))
|
||||
%this.camera.delete();
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// The server has started up so do some game start up
|
||||
//-----------------------------------------------------------------------------
|
||||
function DeathMatchGame::onMissionStart(%this)
|
||||
{
|
||||
//set up the game and game variables
|
||||
%this.initGameVars();
|
||||
|
||||
$Game::Duration = %this.duration;
|
||||
$Game::EndGameScore = %this.endgameScore;
|
||||
$Game::EndGamePause = %this.endgamePause;
|
||||
|
||||
//echo (%game @"\c4 -> "@ %game.class @" -> GameCore::onStartGame");
|
||||
if ($Game::Running)
|
||||
{
|
||||
error("startGame: End the game first!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Inform the client we're starting up
|
||||
for (%clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++)
|
||||
{
|
||||
%cl = ClientGroup.getObject(%clientIndex);
|
||||
commandToClient(%cl, 'GameStart');
|
||||
|
||||
// Other client specific setup..
|
||||
%cl.score = 0;
|
||||
%cl.kills = 0;
|
||||
%cl.deaths = 0;
|
||||
}
|
||||
|
||||
// Start the game timer
|
||||
if ($Game::Duration)
|
||||
$Game::Schedule = %this.schedule($Game::Duration * 1000, "onGameDurationEnd");
|
||||
|
||||
$Game::Running = true;
|
||||
|
||||
$Game = %this;
|
||||
}
|
||||
|
||||
function DeathMatchGame::onMissionEnded(%this)
|
||||
{
|
||||
if (!$Game::Running)
|
||||
{
|
||||
error("endGame: No game running!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop any game timers
|
||||
cancel($Game::Schedule);
|
||||
|
||||
for (%clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++)
|
||||
{
|
||||
%cl = ClientGroup.getObject(%clientIndex);
|
||||
commandToClient(%cl, 'GameEnd', $Game::EndGamePause);
|
||||
}
|
||||
|
||||
$Game::Running = false;
|
||||
$Game::Cycling = false;
|
||||
$Game = "";
|
||||
}
|
||||
|
||||
function DeathMatchGame::onMissionReset(%this)
|
||||
{
|
||||
// Called by resetMission(), after all the temporary mission objects
|
||||
// have been deleted.
|
||||
%this.initGameVars();
|
||||
|
||||
$Game::Duration = %this.duration;
|
||||
$Game::EndGameScore = %this.endgameScore;
|
||||
$Game::EndGamePause = %this.endgamePause;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions that implement game-play
|
||||
// These are here for backwards compatibilty only, games and/or mods should
|
||||
// really be overloading the server and mission functions listed ubove.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Added this stage to creating a player so game types can override it easily.
|
||||
// This is a good place to initiate team selection.
|
||||
function DeathMatchGame::preparePlayer(%this, %client)
|
||||
{
|
||||
//echo (%game @"\c4 -> "@ %game.class @" -> GameCore::preparePlayer");
|
||||
|
||||
// Find a spawn point for the player
|
||||
// This function currently relies on some helper functions defined in
|
||||
// core/scripts/spawn.cs. For custom spawn behaviors one can either
|
||||
// override the properties on the SpawnSphere's or directly override the
|
||||
// functions themselves.
|
||||
%playerSpawnPoint = pickPlayerSpawnPoint($Game::DefaultPlayerSpawnGroups);
|
||||
// Spawn a camera for this client using the found %spawnPoint
|
||||
//%client.spawnPlayer(%playerSpawnPoint);
|
||||
%this.spawnPlayer(%client, %playerSpawnPoint);
|
||||
|
||||
// Starting equipment
|
||||
%this.loadOut(%client.player);
|
||||
}
|
||||
|
||||
function DeathMatchGame::loadOut(%game, %player)
|
||||
{
|
||||
//echo (%game @"\c4 -> "@ %game.class @" -> GameCore::loadOut");
|
||||
|
||||
%player.clearWeaponCycle();
|
||||
|
||||
%player.setInventory(Ryder, 1);
|
||||
%player.setInventory(RyderClip, %player.maxInventory(RyderClip));
|
||||
%player.setInventory(RyderAmmo, %player.maxInventory(RyderAmmo)); // Start the gun loaded
|
||||
%player.addToWeaponCycle(Ryder);
|
||||
|
||||
%player.setInventory(Lurker, 1);
|
||||
%player.setInventory(LurkerClip, %player.maxInventory(LurkerClip));
|
||||
%player.setInventory(LurkerAmmo, %player.maxInventory(LurkerAmmo)); // Start the gun loaded
|
||||
%player.addToWeaponCycle(Lurker);
|
||||
|
||||
%player.setInventory(LurkerGrenadeLauncher, 1);
|
||||
%player.setInventory(LurkerGrenadeAmmo, %player.maxInventory(LurkerGrenadeAmmo));
|
||||
%player.addToWeaponCycle(LurkerGrenadeLauncher);
|
||||
|
||||
%player.setInventory(ProxMine, %player.maxInventory(ProxMine));
|
||||
%player.addToWeaponCycle(ProxMine);
|
||||
|
||||
%player.setInventory(DeployableTurret, %player.maxInventory(DeployableTurret));
|
||||
%player.addToWeaponCycle(DeployableTurret);
|
||||
|
||||
if (%player.getDatablock().mainWeapon.image !$= "")
|
||||
{
|
||||
%player.mountImage(%player.getDatablock().mainWeapon.image, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
%player.mountImage(Ryder, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Customized kill message for falling deaths
|
||||
function sendMsgClientKilled_Impact( %msgType, %client, %sourceClient, %damLoc )
|
||||
{
|
||||
messageAll( %msgType, '%1 fell to his death!', %client.playerName );
|
||||
}
|
||||
|
||||
// Customized kill message for suicides
|
||||
function sendMsgClientKilled_Suicide( %msgType, %client, %sourceClient, %damLoc )
|
||||
{
|
||||
messageAll( %msgType, '%1 takes his own life!', %client.playerName );
|
||||
}
|
||||
|
||||
// Default death message
|
||||
function sendMsgClientKilled_Default( %msgType, %client, %sourceClient, %damLoc )
|
||||
{
|
||||
if ( %sourceClient == %client )
|
||||
sendMsgClientKilled_Suicide(%client, %sourceClient, %damLoc);
|
||||
else if ( %sourceClient.team !$= "" && %sourceClient.team $= %client.team )
|
||||
messageAll( %msgType, '%1 killed by %2 - friendly fire!', %client.playerName, %sourceClient.playerName );
|
||||
else
|
||||
messageAll( %msgType, '%1 gets nailed by %2!', %client.playerName, %sourceClient.playerName );
|
||||
}
|
||||
|
||||
function DeathMatchGame::onDeath(%game, %client, %sourceObject, %sourceClient, %damageType, %damLoc)
|
||||
{
|
||||
//echo (%game @"\c4 -> "@ %game.class @" -> GameCore::onDeath");
|
||||
|
||||
// clear the weaponHUD
|
||||
%client.RefreshWeaponHud(0, "", "");
|
||||
|
||||
// Clear out the name on the corpse
|
||||
%client.player.setShapeName("");
|
||||
|
||||
// Switch the client over to the death cam and unhook the player object.
|
||||
if (isObject(%client.camera) && isObject(%client.player))
|
||||
{
|
||||
%client.camera.setMode("Corpse", %client.player);
|
||||
%client.setControlObject(%client.camera);
|
||||
}
|
||||
%client.player = 0;
|
||||
|
||||
// Display damage appropriate kill message
|
||||
%sendMsgFunction = "sendMsgClientKilled_" @ %damageType;
|
||||
if ( !isFunction( %sendMsgFunction ) )
|
||||
%sendMsgFunction = "sendMsgClientKilled_Default";
|
||||
call( %sendMsgFunction, 'MsgClientKilled', %client, %sourceClient, %damLoc );
|
||||
|
||||
// Dole out points and check for win
|
||||
if (( %damageType $= "Suicide" || %sourceClient == %client ) && isObject(%sourceClient))
|
||||
{
|
||||
%game.incDeaths( %client, 1, true );
|
||||
%game.incScore( %client, -1, false );
|
||||
}
|
||||
else
|
||||
{
|
||||
%game.incDeaths( %client, 1, false );
|
||||
%game.incScore( %sourceClient, 1, true );
|
||||
%game.incKills( %sourceClient, 1, false );
|
||||
|
||||
// If the game may be ended by a client getting a particular score, check that now.
|
||||
if ( $Game::EndGameScore > 0 && %sourceClient.kills >= $Game::EndGameScore )
|
||||
%game.cycleGame();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Scoring
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function DeathMatchGame::incKills(%game, %client, %kill, %dontMessageAll)
|
||||
{
|
||||
%client.kills += %kill;
|
||||
|
||||
if( !%dontMessageAll )
|
||||
messageAll('MsgClientScoreChanged', "", %client.score, %client.kills, %client.deaths, %client);
|
||||
}
|
||||
|
||||
function DeathMatchGame::incDeaths(%game, %client, %death, %dontMessageAll)
|
||||
{
|
||||
%client.deaths += %death;
|
||||
|
||||
if( !%dontMessageAll )
|
||||
messageAll('MsgClientScoreChanged', "", %client.score, %client.kills, %client.deaths, %client);
|
||||
}
|
||||
|
||||
function DeathMatchGame::incScore(%game, %client, %score, %dontMessageAll)
|
||||
{
|
||||
%client.score += %score;
|
||||
|
||||
if( !%dontMessageAll )
|
||||
messageAll('MsgClientScoreChanged', "", %client.score, %client.kills, %client.deaths, %client);
|
||||
}
|
||||
|
||||
function DeathMatchGame::getScore(%client) { return %client.score; }
|
||||
function DeathMatchGame::getKills(%client) { return %client.kills; }
|
||||
function DeathMatchGame::getDeaths(%client) { return %client.deaths; }
|
||||
|
||||
function DeathMatchGame::getTeamScore(%client)
|
||||
{
|
||||
%score = %client.score;
|
||||
if ( %client.team !$= "" )
|
||||
{
|
||||
// Compute team score
|
||||
for (%i = 0; %i < ClientGroup.getCount(); %i++)
|
||||
{
|
||||
%other = ClientGroup.getObject(%i);
|
||||
if ((%other != %client) && (%other.team $= %client.team))
|
||||
%score += %other.score;
|
||||
}
|
||||
}
|
||||
return %score;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Spawning
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function DeathMatchGame::spawnPlayer(%game, %client, %spawnPoint, %noControl)
|
||||
{
|
||||
//echo (%game @"\c4 -> "@ %game.class @" -> GameCore::spawnPlayer");
|
||||
|
||||
if (isObject(%client.player))
|
||||
{
|
||||
// The client should not already have a player. Assigning
|
||||
// a new one could result in an uncontrolled player object.
|
||||
error("Attempting to create a player for a client that already has one!");
|
||||
}
|
||||
|
||||
// Attempt to treat %spawnPoint as an object
|
||||
if (getWordCount(%spawnPoint) == 1 && isObject(%spawnPoint))
|
||||
{
|
||||
// Defaults
|
||||
%spawnClass = $Game::DefaultPlayerClass;
|
||||
%spawnDataBlock = $Game::DefaultPlayerDataBlock;
|
||||
|
||||
// Overrides by the %spawnPoint
|
||||
if (isDefined("%spawnPoint.spawnClass"))
|
||||
{
|
||||
%spawnClass = %spawnPoint.spawnClass;
|
||||
%spawnDataBlock = %spawnPoint.spawnDatablock;
|
||||
}
|
||||
else if (isDefined("%spawnPoint.spawnDatablock"))
|
||||
{
|
||||
// This may seem redundant given the above but it allows
|
||||
// the SpawnSphere to override the datablock without
|
||||
// overriding the default player class
|
||||
%spawnDataBlock = %spawnPoint.spawnDatablock;
|
||||
}
|
||||
|
||||
%spawnProperties = %spawnPoint.spawnProperties;
|
||||
%spawnScript = %spawnPoint.spawnScript;
|
||||
|
||||
// Spawn with the engine's Sim::spawnObject() function
|
||||
%player = spawnObject(%spawnClass, %spawnDatablock, "",
|
||||
%spawnProperties, %spawnScript);
|
||||
|
||||
// If we have an object do some initial setup
|
||||
if (isObject(%player))
|
||||
{
|
||||
// Pick a location within the spawn sphere.
|
||||
%spawnLocation = %game.pickPointInSpawnSphere(%player, %spawnPoint);
|
||||
%player.setTransform(%spawnLocation);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we weren't able to create the player object then warn the user
|
||||
// When the player clicks OK in one of these message boxes, we will fall through
|
||||
// to the "if (!isObject(%player))" check below.
|
||||
if (isDefined("%spawnDatablock"))
|
||||
{
|
||||
MessageBoxOK("Spawn Player Failed",
|
||||
"Unable to create a player with class " @ %spawnClass @
|
||||
" and datablock " @ %spawnDatablock @ ".\n\nStarting as an Observer instead.",
|
||||
"");
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxOK("Spawn Player Failed",
|
||||
"Unable to create a player with class " @ %spawnClass @
|
||||
".\n\nStarting as an Observer instead.",
|
||||
"");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Create a default player
|
||||
%player = spawnObject($Game::DefaultPlayerClass, $Game::DefaultPlayerDataBlock);
|
||||
|
||||
if (!%player.isMemberOfClass("Player"))
|
||||
warn("Trying to spawn a class that does not derive from Player.");
|
||||
|
||||
// Treat %spawnPoint as a transform
|
||||
%player.setTransform(%spawnPoint);
|
||||
}
|
||||
|
||||
// If we didn't actually create a player object then bail
|
||||
if (!isObject(%player))
|
||||
{
|
||||
// Make sure we at least have a camera
|
||||
%client.spawnCamera(%spawnPoint);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the default camera to start with the player
|
||||
if (isObject(%client.camera) && !isDefined("%noControl"))
|
||||
{
|
||||
if (%player.getClassname() $= "Player")
|
||||
%client.camera.setTransform(%player.getEyeTransform());
|
||||
else
|
||||
%client.camera.setTransform(%player.getTransform());
|
||||
}
|
||||
|
||||
// Add the player object to MissionCleanup so that it
|
||||
// won't get saved into the level files and will get
|
||||
// cleaned up properly
|
||||
MissionCleanup.add(%player);
|
||||
|
||||
// Store the client object on the player object for
|
||||
// future reference
|
||||
%player.client = %client;
|
||||
|
||||
// If the player's client has some owned turrets, make sure we let them
|
||||
// know that we're a friend too.
|
||||
if (%client.ownedTurrets)
|
||||
{
|
||||
for (%i=0; %i<%client.ownedTurrets.getCount(); %i++)
|
||||
{
|
||||
%turret = %client.ownedTurrets.getObject(%i);
|
||||
%turret.addToIgnoreList(%player);
|
||||
}
|
||||
}
|
||||
|
||||
// Player setup...
|
||||
if (%player.isMethod("setShapeName"))
|
||||
%player.setShapeName(%client.playerName);
|
||||
|
||||
if (%player.isMethod("setEnergyLevel"))
|
||||
%player.setEnergyLevel(%player.getDataBlock().maxEnergy);
|
||||
|
||||
if (!isDefined("%client.skin"))
|
||||
{
|
||||
// Determine which character skins are not already in use
|
||||
%availableSkins = %player.getDatablock().availableSkins; // TAB delimited list of skin names
|
||||
%count = ClientGroup.getCount();
|
||||
for (%cl = 0; %cl < %count; %cl++)
|
||||
{
|
||||
%other = ClientGroup.getObject(%cl);
|
||||
if (%other != %client)
|
||||
{
|
||||
%availableSkins = strreplace(%availableSkins, %other.skin, "");
|
||||
%availableSkins = strreplace(%availableSkins, "\t\t", ""); // remove empty fields
|
||||
}
|
||||
}
|
||||
|
||||
// Choose a random, unique skin for this client
|
||||
%count = getFieldCount(%availableSkins);
|
||||
%client.skin = addTaggedString( getField(%availableSkins, getRandom(%count)) );
|
||||
}
|
||||
|
||||
%player.setSkinName(%client.skin);
|
||||
|
||||
// Give the client control of the player
|
||||
%client.player = %player;
|
||||
|
||||
// Give the client control of the camera if in the editor
|
||||
if( $startWorldEditor )
|
||||
{
|
||||
%control = %client.camera;
|
||||
%control.mode = "Fly";
|
||||
EditorGui.syncCameraGui();
|
||||
}
|
||||
else
|
||||
%control = %player;
|
||||
|
||||
// Allow the player/camera to receive move data from the GameConnection. Without this
|
||||
// the user is unable to control the player/camera.
|
||||
if (!isDefined("%noControl"))
|
||||
%client.setControlObject(%control);
|
||||
}
|
||||
|
||||
function DeathMatchGame::pickPointInSpawnSphere(%this, %objectToSpawn, %spawnSphere)
|
||||
{
|
||||
%SpawnLocationFound = false;
|
||||
%attemptsToSpawn = 0;
|
||||
while(!%SpawnLocationFound && (%attemptsToSpawn < 5))
|
||||
{
|
||||
%sphereLocation = %spawnSphere.getTransform();
|
||||
|
||||
// Attempt to spawn the player within the bounds of the spawnsphere.
|
||||
%angleY = mDegToRad(getRandom(0, 100) * m2Pi());
|
||||
%angleXZ = mDegToRad(getRandom(0, 100) * m2Pi());
|
||||
|
||||
%sphereLocation = setWord( %sphereLocation, 0, getWord(%sphereLocation, 0) + (mCos(%angleY) * mSin(%angleXZ) * getRandom(-%spawnSphere.radius, %spawnSphere.radius)));
|
||||
%sphereLocation = setWord( %sphereLocation, 1, getWord(%sphereLocation, 1) + (mCos(%angleXZ) * getRandom(-%spawnSphere.radius, %spawnSphere.radius)));
|
||||
|
||||
%SpawnLocationFound = true;
|
||||
|
||||
// Now have to check that another object doesn't already exist at this spot.
|
||||
// Use the bounding box of the object to check if where we are about to spawn in is
|
||||
// clear.
|
||||
%boundingBoxSize = %objectToSpawn.getDatablock().boundingBox;
|
||||
%searchRadius = getWord(%boundingBoxSize, 0);
|
||||
%boxSizeY = getWord(%boundingBoxSize, 1);
|
||||
|
||||
// Use the larger dimention as the radius to search
|
||||
if (%boxSizeY > %searchRadius)
|
||||
%searchRadius = %boxSizeY;
|
||||
|
||||
// Search a radius about the area we're about to spawn for players.
|
||||
initContainerRadiusSearch( %sphereLocation, %searchRadius, $TypeMasks::PlayerObjectType );
|
||||
while ( (%objectNearExit = containerSearchNext()) != 0 )
|
||||
{
|
||||
// If any player is found within this radius, mark that we need to look
|
||||
// for another spot.
|
||||
%SpawnLocationFound = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// If the attempt at finding a clear spawn location failed
|
||||
// try no more than 5 times.
|
||||
%attemptsToSpawn++;
|
||||
}
|
||||
|
||||
// If we couldn't find a spawn location after 5 tries, spawn the object
|
||||
// At the center of the sphere and give a warning.
|
||||
if (!%SpawnLocationFound)
|
||||
{
|
||||
%sphereLocation = %spawnSphere.getTransform();
|
||||
warn("WARNING: Could not spawn player after" SPC %attemptsToSpawn
|
||||
SPC "tries in spawnsphere" SPC %spawnSphere SPC "without overlapping another player. Attempting spawn in center of sphere.");
|
||||
}
|
||||
|
||||
return %sphereLocation;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Observer
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function DeathMatchGame::spawnObserver(%game, %client)
|
||||
{
|
||||
//echo (%game @"\c4 -> "@ %game.class @" -> GameCore::spawnObserver");
|
||||
|
||||
// Position the camera on one of our observer spawn points
|
||||
%client.camera.setTransform(%game.pickObserverSpawnPoint());
|
||||
|
||||
// Set control to the camera
|
||||
%client.setControlObject(%client.camera);
|
||||
}
|
||||
|
||||
function DeathMatchGame::pickObserverSpawnPoint(%game)
|
||||
{
|
||||
//echo (%game @"\c4 -> "@ %game.class @" -> GameCore::pickObserverSpawnPoint");
|
||||
|
||||
%groupName = "MissionGroup/ObserverSpawnPoints";
|
||||
%group = nameToID(%groupName);
|
||||
|
||||
if (%group != -1)
|
||||
{
|
||||
%count = %group.getCount();
|
||||
if (%count != 0)
|
||||
{
|
||||
%index = getRandom(%count-1);
|
||||
%spawn = %group.getObject(%index);
|
||||
return %spawn.getTransform();
|
||||
}
|
||||
else
|
||||
error("No spawn points found in "@ %groupName);
|
||||
}
|
||||
else
|
||||
error("Missing spawn points group "@ %groupName);
|
||||
|
||||
// Could be no spawn points, in which case we'll stick the
|
||||
// player at the center of the world.
|
||||
return "0 0 300 1 0 0 0";
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Server
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Called by GameCore::cycleGame() when we need to destroy the server
|
||||
// because we're done playing. We don't want to call destroyServer()
|
||||
// directly so we can first check that we're about to destroy the
|
||||
// correct server session.
|
||||
function DeathMatchGame::DestroyServer(%serverSession)
|
||||
{
|
||||
if (%serverSession == $Server::Session)
|
||||
{
|
||||
if (isObject(LocalClientConnection))
|
||||
{
|
||||
// We're a local connection so issue a disconnect. The server will
|
||||
// be automatically destroyed for us.
|
||||
disconnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're a stand alone server
|
||||
destroyServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// weapon HUD
|
||||
// ----------------------------------------------------------------------------
|
||||
function GameConnection::setAmmoAmountHud(%client, %amount, %amountInClips )
|
||||
{
|
||||
commandToClient(%client, 'SetAmmoAmountHud', %amount, %amountInClips);
|
||||
}
|
||||
|
||||
function GameConnection::RefreshWeaponHud(%client, %amount, %preview, %ret, %zoomRet, %amountInClips)
|
||||
{
|
||||
commandToClient(%client, 'RefreshWeaponHud', %amount, %preview, %ret, %zoomRet, %amountInClips);
|
||||
}
|
||||
74
Templates/Modules/FPSGameplay/scripts/server/health.cs
Normal file
74
Templates/Modules/FPSGameplay/scripts/server/health.cs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Inventory items. These objects rely on the item & inventory support
|
||||
// system defined in item.cs and inventory.cs
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Health Patches cannot be picked up and are not meant to be added to inventory.
|
||||
// Health is applied automatically when an objects collides with a patch.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function HealthPatch::onCollision(%this, %obj, %col)
|
||||
{
|
||||
// Apply health to colliding object if it needs it.
|
||||
// Works for all shapebase objects.
|
||||
if (%col.getDamageLevel() != 0 && %col.getState() !$= "Dead")
|
||||
{
|
||||
%col.applyRepair(%this.repairAmount);
|
||||
|
||||
%obj.respawn();
|
||||
if (%col.client)
|
||||
messageClient(%col.client, 'MsgHealthPatchUsed', '\c2Health Patch Applied');
|
||||
serverPlay3D(HealthUseSound, %obj.getTransform());
|
||||
}
|
||||
}
|
||||
|
||||
function ShapeBase::tossPatch(%this)
|
||||
{
|
||||
//error("ShapeBase::tossPatch(" SPC %this.client.nameBase SPC ")");
|
||||
if(!isObject(%this))
|
||||
return;
|
||||
|
||||
%item = ItemData::createItem(HealthKitPatch);
|
||||
%item.sourceObject = %this;
|
||||
%item.static = false;
|
||||
MissionCleanup.add(%item);
|
||||
|
||||
%vec = (-1.0 + getRandom() * 2.0) SPC (-1.0 + getRandom() * 2.0) SPC getRandom();
|
||||
%vec = vectorScale(%vec, 10);
|
||||
%eye = %this.getEyeVector();
|
||||
%dot = vectorDot("0 0 1", %eye);
|
||||
if (%dot < 0)
|
||||
%dot = -%dot;
|
||||
%vec = vectorAdd(%vec, vectorScale("0 0 8", 1 - %dot));
|
||||
%vec = vectorAdd(%vec, %this.getVelocity());
|
||||
%pos = getBoxCenter(%this.getWorldBox());
|
||||
|
||||
%item.setTransform(%pos);
|
||||
%item.applyImpulse(%pos, %vec);
|
||||
%item.setCollisionTimeout(%this);
|
||||
//serverPlay3D(%item.getDataBlock().throwSound, %item.getTransform());
|
||||
%item.schedulePop();
|
||||
|
||||
return %item;
|
||||
}
|
||||
312
Templates/Modules/FPSGameplay/scripts/server/inventory.cs
Normal file
312
Templates/Modules/FPSGameplay/scripts/server/inventory.cs
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// This inventory system is totally scripted, no C++ code involved.
|
||||
// It uses object datablock names to track inventory and is generally
|
||||
// object type, or class, agnostic. In other words, it will inventory
|
||||
// any kind of ShapeBase object, though the throw method does assume
|
||||
// that the objects are small enough to throw :)
|
||||
//
|
||||
// For a ShapeBase object to support inventory, it must have an array
|
||||
// of inventory max values:
|
||||
// %this.maxInv[GunAmmo] = 100;
|
||||
// %this.maxInv[SpeedGun] = 1;
|
||||
// where the names "SpeedGun" and "GunAmmo" are datablocks.
|
||||
//
|
||||
// For objects to be inventoriable, they must provide a set of inventory
|
||||
// callback methods, mainly:
|
||||
// onUse
|
||||
// onThrow
|
||||
// onPickup
|
||||
//
|
||||
// Example methods are given further down. The item.cs file also contains
|
||||
// example inventory items.
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Inventory server commands
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function serverCmdUse(%client, %data)
|
||||
{
|
||||
%client.getControlObject().use(%data);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ShapeBase inventory support
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function ShapeBase::use(%this, %data)
|
||||
{
|
||||
// Use an object in the inventory.
|
||||
|
||||
// Need to prevent weapon changing when zooming, but only shapes
|
||||
// that have a connection.
|
||||
%conn = %this.getControllingClient();
|
||||
if (%conn)
|
||||
{
|
||||
%defaultFov = %conn.getControlCameraDefaultFov();
|
||||
%fov = %conn.getControlCameraFov();
|
||||
if (%fov != %defaultFov)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (%this.getInventory(%data) > 0)
|
||||
return %data.onUse(%this);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function ShapeBase::throw(%this, %data, %amount)
|
||||
{
|
||||
// Throw objects from inventory. The onThrow method is
|
||||
// responsible for decrementing the inventory.
|
||||
|
||||
if (%this.getInventory(%data) > 0)
|
||||
{
|
||||
%obj = %data.onThrow(%this, %amount);
|
||||
if (%obj)
|
||||
{
|
||||
%this.throwObject(%obj);
|
||||
serverPlay3D(ThrowSnd, %this.getTransform());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function ShapeBase::pickup(%this, %obj, %amount)
|
||||
{
|
||||
// This method is called to pickup an object and add it to the inventory.
|
||||
// The datablock onPickup method is actually responsible for doing all the
|
||||
// work, including incrementing the inventory.
|
||||
|
||||
%data = %obj.getDatablock();
|
||||
|
||||
// Try and pickup the max if no value was specified
|
||||
if (%amount $= "")
|
||||
%amount = %this.maxInventory(%data) - %this.getInventory(%data);
|
||||
|
||||
// The datablock does the work...
|
||||
if (%amount < 0)
|
||||
%amount = 0;
|
||||
if (%amount)
|
||||
return %data.onPickup(%obj, %this, %amount);
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function ShapeBase::hasInventory(%this, %data)
|
||||
{
|
||||
return (%this.inv[%data] > 0);
|
||||
}
|
||||
|
||||
function ShapeBase::hasAmmo(%this, %weapon)
|
||||
{
|
||||
if (%weapon.image.ammo $= "")
|
||||
return(true);
|
||||
else
|
||||
return(%this.getInventory(%weapon.image.ammo) > 0);
|
||||
}
|
||||
|
||||
function ShapeBase::maxInventory(%this, %data)
|
||||
{
|
||||
if (%data.isField("clip"))
|
||||
{
|
||||
// Use the clip system which uses the maxInventory
|
||||
// field on the ammo itself.
|
||||
return %data.maxInventory;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the ammo pool system which uses the maxInv[]
|
||||
// array on the object's datablock.
|
||||
// If there is no limit defined, we assume 0
|
||||
return %this.getDatablock().maxInv[%data.getName()];
|
||||
}
|
||||
}
|
||||
|
||||
function ShapeBase::incInventory(%this, %data, %amount)
|
||||
{
|
||||
// Increment the inventory by the given amount. The return value
|
||||
// is the amount actually added, which may be less than the
|
||||
// requested amount due to inventory restrictions.
|
||||
|
||||
%max = %this.maxInventory(%data);
|
||||
%total = %this.inv[%data.getName()];
|
||||
if (%total < %max)
|
||||
{
|
||||
if (%total + %amount > %max)
|
||||
%amount = %max - %total;
|
||||
%this.setInventory(%data, %total + %amount);
|
||||
return %amount;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function ShapeBase::decInventory(%this, %data, %amount)
|
||||
{
|
||||
// Decrement the inventory by the given amount. The return value
|
||||
// is the amount actually removed.
|
||||
|
||||
%total = %this.inv[%data.getName()];
|
||||
if (%total > 0)
|
||||
{
|
||||
if (%total < %amount)
|
||||
%amount = %total;
|
||||
%this.setInventory(%data, %total - %amount);
|
||||
return %amount;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function ShapeBase::getInventory(%this, %data)
|
||||
{
|
||||
// Return the current inventory amount
|
||||
return %this.inv[%data.getName()];
|
||||
}
|
||||
|
||||
function ShapeBase::setInventory(%this, %data, %value)
|
||||
{
|
||||
// Set the inventory amount for this datablock and invoke inventory
|
||||
// callbacks. All changes to inventory go through this single method.
|
||||
|
||||
// Impose inventory limits
|
||||
if (%value < 0)
|
||||
%value = 0;
|
||||
else
|
||||
{
|
||||
%max = %this.maxInventory(%data);
|
||||
if (%value > %max)
|
||||
%value = %max;
|
||||
}
|
||||
|
||||
// Set the value and invoke object callbacks
|
||||
%name = %data.getName();
|
||||
if (%this.inv[%name] != %value)
|
||||
{
|
||||
%this.inv[%name] = %value;
|
||||
%data.onInventory(%this, %value);
|
||||
%this.getDataBlock().onInventory(%data, %value);
|
||||
}
|
||||
return %value;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function ShapeBase::clearInventory(%this)
|
||||
{
|
||||
// To be filled in...
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function ShapeBase::throwObject(%this, %obj)
|
||||
{
|
||||
// Throw the given object in the direction the shape is looking.
|
||||
// The force value is hardcoded according to the current default
|
||||
// object mass and mission gravity (20m/s^2).
|
||||
|
||||
%throwForce = %this.getDataBlock().throwForce;
|
||||
if (!%throwForce)
|
||||
%throwForce = 20;
|
||||
|
||||
// Start with the shape's eye vector...
|
||||
%eye = %this.getEyeVector();
|
||||
%vec = vectorScale(%eye, %throwForce);
|
||||
|
||||
// Add a vertical component to give the object a better arc
|
||||
%verticalForce = %throwForce / 2;
|
||||
%dot = vectorDot("0 0 1", %eye);
|
||||
if (%dot < 0)
|
||||
%dot = -%dot;
|
||||
%vec = vectorAdd(%vec, vectorScale("0 0 "@%verticalForce, 1 - %dot));
|
||||
|
||||
// Add the shape's velocity
|
||||
%vec = vectorAdd(%vec, %this.getVelocity());
|
||||
|
||||
// Set the object's position and initial velocity
|
||||
%pos = getBoxCenter(%this.getWorldBox());
|
||||
%obj.setTransform(%pos);
|
||||
%obj.applyImpulse(%pos, %vec);
|
||||
|
||||
// Since the object is thrown from the center of the shape,
|
||||
// the object needs to avoid colliding with it's thrower.
|
||||
%obj.setCollisionTimeout(%this);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Callback hooks invoked by the inventory system
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ShapeBase object callbacks invoked by the inventory system
|
||||
|
||||
function ShapeBase::onInventory(%this, %data, %value)
|
||||
{
|
||||
// Invoked on ShapeBase objects whenever their inventory changes
|
||||
// for the given datablock.
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ShapeBase datablock callback invoked by the inventory system.
|
||||
|
||||
function ShapeBaseData::onUse(%this, %user)
|
||||
{
|
||||
// Invoked when the object uses this datablock, should return
|
||||
// true if the item was used.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function ShapeBaseData::onThrow(%this, %user, %amount)
|
||||
{
|
||||
// Invoked when the object is thrown. This method should
|
||||
// construct and return the actual mission object to be
|
||||
// physically thrown. This method is also responsible for
|
||||
// decrementing the user's inventory.
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function ShapeBaseData::onPickup(%this, %obj, %user, %amount)
|
||||
{
|
||||
// Invoked when the user attempts to pickup this datablock object.
|
||||
// The %amount argument is the space in the user's inventory for
|
||||
// this type of datablock. This method is responsible for
|
||||
// incrementing the user's inventory is something is addded.
|
||||
// Should return true if something was added to the inventory.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function ShapeBaseData::onInventory(%this, %user, %value)
|
||||
{
|
||||
// Invoked whenever an user's inventory total changes for
|
||||
// this datablock.
|
||||
}
|
||||
151
Templates/Modules/FPSGameplay/scripts/server/item.cs
Normal file
151
Templates/Modules/FPSGameplay/scripts/server/item.cs
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// These scripts make use of dynamic attribute values on Item datablocks,
|
||||
// these are as follows:
|
||||
//
|
||||
// maxInventory Max inventory per object (100 bullets per box, etc.)
|
||||
// pickupName Name to display when client pickups item
|
||||
//
|
||||
// Item objects can have:
|
||||
//
|
||||
// count The # of inventory items in the object. This
|
||||
// defaults to maxInventory if not set.
|
||||
|
||||
// Respawntime is the amount of time it takes for a static "auto-respawn"
|
||||
// object, such as an ammo box or weapon, to re-appear after it's been
|
||||
// picked up. Any item marked as "static" is automaticlly respawned.
|
||||
$Item::RespawnTime = 30 * 1000;
|
||||
|
||||
// Poptime represents how long dynamic items (those that are thrown or
|
||||
// dropped) will last in the world before being deleted.
|
||||
$Item::PopTime = 30 * 1000;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ItemData base class methods used by all items
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function Item::respawn(%this)
|
||||
{
|
||||
// This method is used to respawn static ammo and weapon items
|
||||
// and is usually called when the item is picked up.
|
||||
// Instant fade...
|
||||
%this.startFade(0, 0, true);
|
||||
%this.setHidden(true);
|
||||
|
||||
// Shedule a reapearance
|
||||
%this.schedule($Item::RespawnTime, "setHidden", false);
|
||||
%this.schedule($Item::RespawnTime + 100, "startFade", 1000, 0, false);
|
||||
}
|
||||
|
||||
function Item::schedulePop(%this)
|
||||
{
|
||||
// This method deletes the object after a default duration. Dynamic
|
||||
// items such as thrown or drop weapons are usually popped to avoid
|
||||
// world clutter.
|
||||
%this.schedule($Item::PopTime - 1000, "startFade", 1000, 0, true);
|
||||
%this.schedule($Item::PopTime, "delete");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Callbacks to hook items into the inventory system
|
||||
|
||||
function ItemData::onThrow(%this, %user, %amount)
|
||||
{
|
||||
// Remove the object from the inventory
|
||||
if (%amount $= "")
|
||||
%amount = 1;
|
||||
if (%this.maxInventory !$= "")
|
||||
if (%amount > %this.maxInventory)
|
||||
%amount = %this.maxInventory;
|
||||
if (!%amount)
|
||||
return 0;
|
||||
%user.decInventory(%this,%amount);
|
||||
|
||||
// Construct the actual object in the world, and add it to
|
||||
// the mission group so it's cleaned up when the mission is
|
||||
// done. The object is given a random z rotation.
|
||||
%obj = new Item()
|
||||
{
|
||||
datablock = %this;
|
||||
rotation = "0 0 1 "@ (getRandom() * 360);
|
||||
count = %amount;
|
||||
};
|
||||
MissionGroup.add(%obj);
|
||||
%obj.schedulePop();
|
||||
return %obj;
|
||||
}
|
||||
|
||||
function ItemData::onPickup(%this, %obj, %user, %amount)
|
||||
{
|
||||
// Add it to the inventory, this currently ignores the request
|
||||
// amount, you get what you get. If the object doesn't have
|
||||
// a count or the datablock doesn't have maxIventory set, the
|
||||
// object cannot be picked up.
|
||||
|
||||
// See if the object has a count
|
||||
%count = %obj.count;
|
||||
if (%count $= "")
|
||||
{
|
||||
// No, so check the datablock
|
||||
%count = %this.count;
|
||||
if (%count $= "")
|
||||
{
|
||||
// No, so attempt to provide the maximum amount
|
||||
if (%this.maxInventory !$= "")
|
||||
{
|
||||
if (!(%count = %this.maxInventory))
|
||||
return;
|
||||
}
|
||||
else
|
||||
%count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
%user.incInventory(%this, %count);
|
||||
|
||||
// Inform the client what they got.
|
||||
if (%user.client)
|
||||
messageClient(%user.client, 'MsgItemPickup', '\c0You picked up %1', %this.pickupName);
|
||||
|
||||
// If the item is a static respawn item, then go ahead and
|
||||
// respawn it, otherwise remove it from the world.
|
||||
// Anything not taken up by inventory is lost.
|
||||
if (%obj.isStatic())
|
||||
%obj.respawn();
|
||||
else
|
||||
%obj.delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
function ItemData::createItem(%data)
|
||||
{
|
||||
%obj = new Item()
|
||||
{
|
||||
dataBlock = %data;
|
||||
static = true;
|
||||
rotate = true;
|
||||
};
|
||||
return %obj;
|
||||
}
|
||||
467
Templates/Modules/FPSGameplay/scripts/server/player.cs
Normal file
467
Templates/Modules/FPSGameplay/scripts/server/player.cs
Normal file
|
|
@ -0,0 +1,467 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Timeouts for corpse deletion.
|
||||
$CorpseTimeoutValue = 45 * 1000;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Player Datablock methods
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
function PlayerData::onAdd(%this, %obj)
|
||||
{
|
||||
// Vehicle timeout
|
||||
%obj.mountVehicle = true;
|
||||
|
||||
// Default dynamic armor stats
|
||||
%obj.setRechargeRate(%this.rechargeRate);
|
||||
%obj.setRepairRate(0);
|
||||
}
|
||||
|
||||
function PlayerData::onRemove(%this, %obj)
|
||||
{
|
||||
if (%obj.client.player == %obj)
|
||||
%obj.client.player = 0;
|
||||
}
|
||||
|
||||
function PlayerData::onNewDataBlock(%this, %obj)
|
||||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
function PlayerData::onMount(%this, %obj, %vehicle, %node)
|
||||
{
|
||||
// Node 0 is the pilot's position, we need to dismount his weapon.
|
||||
if (%node == 0)
|
||||
{
|
||||
%obj.setTransform("0 0 0 0 0 1 0");
|
||||
%obj.setActionThread(%vehicle.getDatablock().mountPose[%node], true, true);
|
||||
|
||||
%obj.lastWeapon = %obj.getMountedImage($WeaponSlot);
|
||||
%obj.unmountImage($WeaponSlot);
|
||||
|
||||
%obj.setControlObject(%vehicle);
|
||||
|
||||
if(%obj.getClassName() $= "Player")
|
||||
commandToClient(%obj.client, 'toggleVehicleMap', true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (%vehicle.getDataBlock().mountPose[%node] !$= "")
|
||||
%obj.setActionThread(%vehicle.getDatablock().mountPose[%node]);
|
||||
else
|
||||
%obj.setActionThread("root", true);
|
||||
}
|
||||
}
|
||||
|
||||
function PlayerData::onUnmount(%this, %obj, %vehicle, %node)
|
||||
{
|
||||
if (%node == 0)
|
||||
{
|
||||
%obj.mountImage(%obj.lastWeapon, $WeaponSlot);
|
||||
%obj.setControlObject("");
|
||||
}
|
||||
}
|
||||
|
||||
function PlayerData::doDismount(%this, %obj, %forced)
|
||||
{
|
||||
//echo("\c4PlayerData::doDismount(" @ %this @", "@ %obj.client.nameBase @", "@ %forced @")");
|
||||
|
||||
// This function is called by player.cc when the jump trigger
|
||||
// is true while mounted
|
||||
%vehicle = %obj.mVehicle;
|
||||
if (!%obj.isMounted() || !isObject(%vehicle))
|
||||
return;
|
||||
|
||||
// Vehicle must be at rest!
|
||||
if ((VectorLen(%vehicle.getVelocity()) <= %vehicle.getDataBlock().maxDismountSpeed ) || %forced)
|
||||
{
|
||||
// Position above dismount point
|
||||
%pos = getWords(%obj.getTransform(), 0, 2);
|
||||
%rot = getWords(%obj.getTransform(), 3, 6);
|
||||
%oldPos = %pos;
|
||||
%vec[0] = " -1 0 0";
|
||||
%vec[1] = " 0 0 1";
|
||||
%vec[2] = " 0 0 -1";
|
||||
%vec[3] = " 1 0 0";
|
||||
%vec[4] = "0 -1 0";
|
||||
%impulseVec = "0 0 0";
|
||||
%vec[0] = MatrixMulVector(%obj.getTransform(), %vec[0]);
|
||||
|
||||
// Make sure the point is valid
|
||||
%pos = "0 0 0";
|
||||
%numAttempts = 5;
|
||||
%success = -1;
|
||||
for (%i = 0; %i < %numAttempts; %i++)
|
||||
{
|
||||
%pos = VectorAdd(%oldPos, VectorScale(%vec[%i], 3));
|
||||
if (%obj.checkDismountPoint(%oldPos, %pos))
|
||||
{
|
||||
%success = %i;
|
||||
%impulseVec = %vec[%i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (%forced && %success == -1)
|
||||
%pos = %oldPos;
|
||||
|
||||
%obj.mountVehicle = false;
|
||||
%obj.schedule(4000, "mountVehicles", true);
|
||||
|
||||
// Position above dismount point
|
||||
%obj.unmount();
|
||||
%obj.setTransform(%pos SPC %rot);//%obj.setTransform(%pos);
|
||||
//%obj.playAudio(0, UnmountVehicleSound);
|
||||
%obj.applyImpulse(%pos, VectorScale(%impulseVec, %obj.getDataBlock().mass));
|
||||
|
||||
// Set player velocity when ejecting
|
||||
%vel = %obj.getVelocity();
|
||||
%vec = vectorDot( %vel, vectorNormalize(%vel));
|
||||
if(%vec > 50)
|
||||
{
|
||||
%scale = 50 / %vec;
|
||||
%obj.setVelocity(VectorScale(%vel, %scale));
|
||||
}
|
||||
|
||||
//%obj.vehicleTurret = "";
|
||||
}
|
||||
else
|
||||
messageClient(%obj.client, 'msgUnmount', '\c2Cannot exit %1 while moving.', %vehicle.getDataBlock().nameTag);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
function PlayerData::onCollision(%this, %obj, %col)
|
||||
{
|
||||
if (!isObject(%col) || %obj.getState() $= "Dead")
|
||||
return;
|
||||
|
||||
// Try and pickup all items
|
||||
if (%col.getClassName() $= "Item")
|
||||
{
|
||||
%obj.pickup(%col);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mount vehicles
|
||||
if (%col.getType() & $TypeMasks::GameBaseObjectType)
|
||||
{
|
||||
%db = %col.getDataBlock();
|
||||
if ((%db.getClassName() $= "WheeledVehicleData" ) && %obj.mountVehicle && %obj.getState() $= "Move" && %col.mountable)
|
||||
{
|
||||
// Only mount drivers for now.
|
||||
ServerConnection.setFirstPerson(0);
|
||||
|
||||
// For this specific example, only one person can fit
|
||||
// into a vehicle
|
||||
%mount = %col.getMountNodeObject(0);
|
||||
if(%mount)
|
||||
return;
|
||||
|
||||
// For this specific FPS Example, always mount the player
|
||||
// to node 0
|
||||
%node = 0;
|
||||
%col.mountObject(%obj, %node);
|
||||
%obj.mVehicle = %col;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function PlayerData::onImpact(%this, %obj, %collidedObject, %vec, %vecLen)
|
||||
{
|
||||
%obj.damage(0, VectorAdd(%obj.getPosition(), %vec), %vecLen * %this.speedDamageScale, "Impact");
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
function PlayerData::damage(%this, %obj, %sourceObject, %position, %damage, %damageType)
|
||||
{
|
||||
if (!isObject(%obj) || %obj.getState() $= "Dead" || !%damage)
|
||||
return;
|
||||
|
||||
%obj.applyDamage(%damage);
|
||||
|
||||
%location = "Body";
|
||||
|
||||
// Deal with client callbacks here because we don't have this
|
||||
// information in the onDamage or onDisable methods
|
||||
%client = %obj.client;
|
||||
%sourceClient = %sourceObject ? %sourceObject.client : 0;
|
||||
|
||||
if (isObject(%client))
|
||||
{
|
||||
// Determine damage direction
|
||||
if (%damageType !$= "Suicide")
|
||||
%obj.setDamageDirection(%sourceObject, %position);
|
||||
|
||||
if (%obj.getState() $= "Dead")
|
||||
{
|
||||
$Game.onDeath(%client, %sourceObject, %sourceClient, %damageType, %location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function PlayerData::onDamage(%this, %obj, %delta)
|
||||
{
|
||||
// This method is invoked by the ShapeBase code whenever the
|
||||
// object's damage level changes.
|
||||
if (%delta > 0 && %obj.getState() !$= "Dead")
|
||||
{
|
||||
// Apply a damage flash
|
||||
%obj.setDamageFlash(1);
|
||||
|
||||
// If the pain is excessive, let's hear about it.
|
||||
if (%delta > 10)
|
||||
%obj.playPain();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// The player object sets the "disabled" state when damage exceeds it's
|
||||
// maxDamage value. This is method is invoked by ShapeBase state mangement code.
|
||||
|
||||
// If we want to deal with the damage information that actually caused this
|
||||
// death, then we would have to move this code into the script "damage" method.
|
||||
|
||||
function PlayerData::onDisabled(%this, %obj, %state)
|
||||
{
|
||||
// Release the main weapon trigger
|
||||
%obj.setImageTrigger(0, false);
|
||||
|
||||
// Toss current mounted weapon and ammo if any
|
||||
%item = %obj.getMountedImage($WeaponSlot).item;
|
||||
if (isObject(%item))
|
||||
{
|
||||
%amount = %obj.getInventory(%item.image.ammo);
|
||||
|
||||
if (!%item.image.clip)
|
||||
warn("No clip exists to throw for item ", %item);
|
||||
if(%amount)
|
||||
%obj.throw(%item.image.clip, 1);
|
||||
}
|
||||
|
||||
// Toss out a health patch
|
||||
%obj.tossPatch();
|
||||
|
||||
%obj.playDeathCry();
|
||||
%obj.playDeathAnimation();
|
||||
//%obj.setDamageFlash(0.75);
|
||||
|
||||
// Disable any vehicle map
|
||||
commandToClient(%obj.client, 'toggleVehicleMap', false);
|
||||
|
||||
// Schedule corpse removal. Just keeping the place clean.
|
||||
%obj.schedule($CorpseTimeoutValue - 1000, "startFade", 1000, 0, true);
|
||||
%obj.schedule($CorpseTimeoutValue, "delete");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function PlayerData::onLeaveMissionArea(%this, %obj)
|
||||
{
|
||||
//echo("\c4Leaving Mission Area at POS:"@ %obj.getPosition());
|
||||
|
||||
// Inform the client
|
||||
%obj.client.onLeaveMissionArea();
|
||||
|
||||
// Damage over time and kill the coward!
|
||||
//%obj.setDamageDt(0.2, "MissionAreaDamage");
|
||||
}
|
||||
|
||||
function PlayerData::onEnterMissionArea(%this, %obj)
|
||||
{
|
||||
//echo("\c4Entering Mission Area at POS:"@ %obj.getPosition());
|
||||
|
||||
// Inform the client
|
||||
%obj.client.onEnterMissionArea();
|
||||
|
||||
// Stop the punishment
|
||||
//%obj.clearDamageDt();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function PlayerData::onEnterLiquid(%this, %obj, %coverage, %type)
|
||||
{
|
||||
//echo("\c4this:"@ %this @" object:"@ %obj @" just entered water of type:"@ %type @" for "@ %coverage @"coverage");
|
||||
}
|
||||
|
||||
function PlayerData::onLeaveLiquid(%this, %obj, %type)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function PlayerData::onTrigger(%this, %obj, %triggerNum, %val)
|
||||
{
|
||||
// This method is invoked when the player receives a trigger move event.
|
||||
// The player automatically triggers slot 0 and slot one off of triggers #
|
||||
// 0 & 1. Trigger # 2 is also used as the jump key.
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function PlayerData::onPoseChange(%this, %obj, %oldPose, %newPose)
|
||||
{
|
||||
// Set the script anim prefix to be that of the current pose
|
||||
%obj.setImageScriptAnimPrefix( $WeaponSlot, addTaggedString(%newPose) );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function PlayerData::onStartSprintMotion(%this, %obj)
|
||||
{
|
||||
%obj.setImageGenericTrigger($WeaponSlot, 0, true);
|
||||
}
|
||||
|
||||
function PlayerData::onStopSprintMotion(%this, %obj)
|
||||
{
|
||||
%obj.setImageGenericTrigger($WeaponSlot, 0, false);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Player methods
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
function Player::kill(%this, %damageType)
|
||||
{
|
||||
%this.damage(0, %this.getPosition(), 10000, %damageType);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
function Player::mountVehicles(%this, %bool)
|
||||
{
|
||||
// If set to false, this variable disables vehicle mounting.
|
||||
%this.mountVehicle = %bool;
|
||||
}
|
||||
|
||||
function Player::isPilot(%this)
|
||||
{
|
||||
%vehicle = %this.getObjectMount();
|
||||
// There are two "if" statements to avoid a script warning.
|
||||
if (%vehicle)
|
||||
if (%vehicle.getMountNodeObject(0) == %this)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
function Player::playDeathAnimation(%this)
|
||||
{
|
||||
%numDeathAnimations = %this.getNumDeathAnimations();
|
||||
if ( %numDeathAnimations > 0 )
|
||||
{
|
||||
if (isObject(%this.client))
|
||||
{
|
||||
if (%this.client.deathIdx++ > %numDeathAnimations)
|
||||
%this.client.deathIdx = 1;
|
||||
%this.setActionThread("Death" @ %this.client.deathIdx);
|
||||
}
|
||||
else
|
||||
{
|
||||
%rand = getRandom(1, %numDeathAnimations);
|
||||
%this.setActionThread("Death" @ %rand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Player::playCelAnimation(%this, %anim)
|
||||
{
|
||||
if (%this.getState() !$= "Dead")
|
||||
%this.setActionThread("cel"@%anim);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
function Player::playDeathCry(%this)
|
||||
{
|
||||
%this.playAudio(0, DeathCrySound);
|
||||
}
|
||||
|
||||
function Player::playPain(%this)
|
||||
{
|
||||
%this.playAudio(0, PainCrySound);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function Player::setDamageDirection(%player, %sourceObject, %damagePos)
|
||||
{
|
||||
if (isObject(%sourceObject))
|
||||
{
|
||||
if (%sourceObject.isField(initialPosition))
|
||||
{
|
||||
// Projectiles have this field set to the muzzle point of
|
||||
// the firing weapon at the time the projectile was created.
|
||||
// This gives a damage direction towards the firing player,
|
||||
// turret, vehicle, etc. Bullets and weapon fired grenades
|
||||
// are examples of projectiles.
|
||||
%damagePos = %sourceObject.initialPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Other objects that cause damage, such as mines, use their own
|
||||
// location as the damage position. This gives a damage direction
|
||||
// towards the explosive origin rather than the person that lay the
|
||||
// explosives.
|
||||
%damagePos = %sourceObject.getPosition();
|
||||
}
|
||||
}
|
||||
|
||||
// Rotate damage vector into object space
|
||||
%damageVec = VectorSub(%damagePos, %player.getWorldBoxCenter());
|
||||
%damageVec = VectorNormalize(%damageVec);
|
||||
%damageVec = MatrixMulVector(%player.client.getCameraObject().getInverseTransform(), %damageVec);
|
||||
|
||||
// Determine largest component of damage vector to get direction
|
||||
%vecComponents = -%damageVec.x SPC %damageVec.x SPC -%damageVec.y SPC %damageVec.y SPC -%damageVec.z SPC %damageVec.z;
|
||||
%vecDirections = "Left" SPC "Right" SPC "Bottom" SPC "Front" SPC "Bottom" SPC "Top";
|
||||
|
||||
%max = -1;
|
||||
for (%i = 0; %i < 6; %i++)
|
||||
{
|
||||
%value = getWord(%vecComponents, %i);
|
||||
if (%value > %max)
|
||||
{
|
||||
%max = %value;
|
||||
%damageDir = getWord(%vecDirections, %i);
|
||||
}
|
||||
}
|
||||
commandToClient(%player.client, 'setDamageDirection', %damageDir);
|
||||
}
|
||||
|
||||
function Player::use(%player, %data)
|
||||
{
|
||||
// No mounting/using weapons when you're driving!
|
||||
if (%player.isPilot())
|
||||
return(false);
|
||||
|
||||
Parent::use(%player, %data);
|
||||
}
|
||||
46
Templates/Modules/FPSGameplay/scripts/server/projectile.cs
Normal file
46
Templates/Modules/FPSGameplay/scripts/server/projectile.cs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// "Universal" script methods for projectile damage handling. You can easily
|
||||
// override these support functions with an equivalent namespace method if your
|
||||
// weapon needs a unique solution for applying damage.
|
||||
|
||||
function ProjectileData::onCollision(%data, %proj, %col, %fade, %pos, %normal)
|
||||
{
|
||||
//echo("ProjectileData::onCollision("@%data.getName()@", "@%proj@", "@%col.getClassName()@", "@%fade@", "@%pos@", "@%normal@")");
|
||||
|
||||
// Apply damage to the object all shape base objects
|
||||
if (%data.directDamage > 0)
|
||||
{
|
||||
if (%col.getType() & ($TypeMasks::ShapeBaseObjectType))
|
||||
%col.damage(%proj, %pos, %data.directDamage, %data.damageType);
|
||||
}
|
||||
}
|
||||
|
||||
function ProjectileData::onExplode(%data, %proj, %position, %mod)
|
||||
{
|
||||
//echo("ProjectileData::onExplode("@%data.getName()@", "@%proj@", "@%position@", "@%mod@")");
|
||||
|
||||
// Damage objects within the projectiles damage radius
|
||||
if (%data.damageRadius > 0)
|
||||
radiusDamage(%proj, %position, %data.damageRadius, %data.radiusDamage, %data.damageType, %data.areaImpulse);
|
||||
}
|
||||
118
Templates/Modules/FPSGameplay/scripts/server/proximityMine.cs
Normal file
118
Templates/Modules/FPSGameplay/scripts/server/proximityMine.cs
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function ProximityMineData::onThrow( %this, %user, %amount )
|
||||
{
|
||||
// Remove the object from the inventory
|
||||
%user.decInventory( %this, 1 );
|
||||
|
||||
// Construct the actual object in the world, and add it to
|
||||
// the mission group so its cleaned up when the mission is
|
||||
// done. The object is given a random z rotation.
|
||||
%obj = new ProximityMine()
|
||||
{
|
||||
datablock = %this;
|
||||
sourceObject = %user;
|
||||
rotation = "0 0 1 "@ (getRandom() * 360);
|
||||
static = false;
|
||||
client = %user.client;
|
||||
};
|
||||
MissionCleanup.add(%obj);
|
||||
return %obj;
|
||||
}
|
||||
|
||||
|
||||
function ProximityMineData::onTriggered( %this, %obj, %target )
|
||||
{
|
||||
//echo(%this.name SPC "triggered by " @ %target.getClassName());
|
||||
}
|
||||
|
||||
function ProximityMineData::onExplode( %this, %obj, %position )
|
||||
{
|
||||
// Damage objects within the mine's damage radius
|
||||
if ( %this.damageRadius > 0 )
|
||||
radiusDamage( %obj, %position, %this.damageRadius, %this.radiusDamage, %this.damageType, %this.areaImpulse );
|
||||
}
|
||||
|
||||
function ProximityMineData::damage( %this, %obj, %position, %source, %amount, %damageType )
|
||||
{
|
||||
// Explode if any damage is applied to the mine
|
||||
%obj.schedule(50 + getRandom(50), explode);
|
||||
}
|
||||
|
||||
// Customized kill message for deaths caused by proximity mines
|
||||
function sendMsgClientKilled_MineDamage( %msgType, %client, %sourceClient, %damLoc )
|
||||
{
|
||||
if ( %sourceClient $= "" ) // editor placed mine
|
||||
messageAll( %msgType, '%1 was blown up!', %client.playerName );
|
||||
else if ( %sourceClient == %client ) // own mine
|
||||
messageAll( %msgType, '%1 stepped on his own mine!', %client.playerName );
|
||||
else // enemy placed mine
|
||||
messageAll( %msgType, '%1 was blown up by %2!', %client.playerName, %sourceClient.playerName );
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Player deployable proximity mine
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Cannot use the Weapon class for ProximityMineData datablocks as it is already tied
|
||||
// to ItemData.
|
||||
|
||||
function ProxMine::onUse(%this, %obj)
|
||||
{
|
||||
// Act like a weapon on use
|
||||
Weapon::onUse( %this, %obj );
|
||||
}
|
||||
|
||||
function ProxMine::onPickup( %this, %obj, %shape, %amount )
|
||||
{
|
||||
// Act like a weapon on pickup
|
||||
Weapon::onPickup( %this, %obj, %shape, %amount );
|
||||
}
|
||||
|
||||
function ProxMine::onInventory( %this, %obj, %amount )
|
||||
{
|
||||
%obj.client.setAmmoAmountHud( 1, %amount );
|
||||
|
||||
// Cycle weapons if we are out of ammo
|
||||
if ( !%amount && ( %slot = %obj.getMountSlot( %this.image ) ) != -1 )
|
||||
%obj.cycleWeapon( "prev" );
|
||||
}
|
||||
|
||||
function ProxMineImage::onMount( %this, %obj, %slot )
|
||||
{
|
||||
// The mine doesn't use ammo from a player's perspective.
|
||||
%obj.setImageAmmo( %slot, true );
|
||||
%numMines = %obj.getInventory(%this.item);
|
||||
%obj.client.RefreshWeaponHud( 1, %this.item.previewImage, %this.item.reticle, %this.item.zoomReticle, %numMines );
|
||||
}
|
||||
|
||||
function ProxMineImage::onUnmount( %this, %obj, %slot )
|
||||
{
|
||||
%obj.client.RefreshWeaponHud( 0, "", "" );
|
||||
}
|
||||
|
||||
function ProxMineImage::onFire( %this, %obj, %slot )
|
||||
{
|
||||
// To fire a deployable mine is to throw it
|
||||
%obj.throw( %this.item );
|
||||
}
|
||||
73
Templates/Modules/FPSGameplay/scripts/server/radiusDamage.cs
Normal file
73
Templates/Modules/FPSGameplay/scripts/server/radiusDamage.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Support function which applies damage to objects within the radius of
|
||||
// some effect, usually an explosion. This function will also optionally
|
||||
// apply an impulse to each object.
|
||||
|
||||
function radiusDamage(%sourceObject, %position, %radius, %damage, %damageType, %impulse)
|
||||
{
|
||||
// Use the container system to iterate through all the objects
|
||||
// within our explosion radius. We'll apply damage to all ShapeBase
|
||||
// objects.
|
||||
InitContainerRadiusSearch(%position, %radius, $TypeMasks::ShapeBaseObjectType | $TypeMasks::DynamicShapeObjectType);
|
||||
|
||||
%halfRadius = %radius / 2;
|
||||
while ((%targetObject = containerSearchNext()) != 0)
|
||||
{
|
||||
// Calculate how much exposure the current object has to
|
||||
// the explosive force. The object types listed are objects
|
||||
// that will block an explosion. If the object is totally blocked,
|
||||
// then no damage is applied.
|
||||
%coverage = calcExplosionCoverage(%position, %targetObject,
|
||||
$TypeMasks::InteriorObjectType |
|
||||
$TypeMasks::TerrainObjectType |
|
||||
$TypeMasks::ForceFieldObjectType |
|
||||
$TypeMasks::StaticShapeObjectType |
|
||||
$TypeMasks::VehicleObjectType);
|
||||
if (%coverage == 0)
|
||||
continue;
|
||||
|
||||
// Radius distance subtracts out the length of smallest bounding
|
||||
// box axis to return an appriximate distance to the edge of the
|
||||
// object's bounds, as opposed to the distance to it's center.
|
||||
%dist = containerSearchCurrRadiusDist();
|
||||
|
||||
// Calculate a distance scale for the damage and the impulse.
|
||||
// Full damage is applied to anything less than half the radius away,
|
||||
// linear scale from there.
|
||||
%distScale = (%dist < %halfRadius)? 1.0 : 1.0 - ((%dist - %halfRadius) / %halfRadius);
|
||||
%distScale = mClamp(%distScale,0.0,1.0);
|
||||
|
||||
// Apply the damage
|
||||
%targetObject.damage(%sourceObject, %position, %damage * %coverage * %distScale, %damageType);
|
||||
|
||||
// Apply the impulse
|
||||
if (%impulse)
|
||||
{
|
||||
%impulseVec = VectorSub(%targetObject.getWorldBoxCenter(), %position);
|
||||
%impulseVec = VectorNormalize(%impulseVec);
|
||||
%impulseVec = VectorScale(%impulseVec, %impulse * %distScale);
|
||||
%targetObject.applyImpulse(%position, %impulseVec);
|
||||
}
|
||||
}
|
||||
}
|
||||
121
Templates/Modules/FPSGameplay/scripts/server/shapeBase.cs
Normal file
121
Templates/Modules/FPSGameplay/scripts/server/shapeBase.cs
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// This file contains ShapeBase methods used by all the derived classes
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ShapeBase object
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// A raycast helper function to keep from having to duplicate code everytime
|
||||
// that a raycast is needed.
|
||||
// %this = the object doing the cast, usually a player
|
||||
// %range = range to search
|
||||
// %mask = what to look for
|
||||
|
||||
function ShapeBase::doRaycast(%this, %range, %mask)
|
||||
{
|
||||
// get the eye vector and eye transform of the player
|
||||
%eyeVec = %this.getEyeVector();
|
||||
%eyeTrans = %this.getEyeTransform();
|
||||
|
||||
// extract the position of the player's camera from the eye transform (first 3 words)
|
||||
%eyePos = getWord(%eyeTrans, 0) SPC getWord(%eyeTrans, 1) SPC getWord(%eyeTrans, 2);
|
||||
|
||||
// normalize the eye vector
|
||||
%nEyeVec = VectorNormalize(%eyeVec);
|
||||
|
||||
// scale (lengthen) the normalized eye vector according to the search range
|
||||
%scEyeVec = VectorScale(%nEyeVec, %range);
|
||||
|
||||
// add the scaled & normalized eye vector to the position of the camera
|
||||
%eyeEnd = VectorAdd(%eyePos, %scEyeVec);
|
||||
|
||||
// see if anything gets hit
|
||||
%searchResult = containerRayCast(%eyePos, %eyeEnd, %mask, %this);
|
||||
|
||||
return %searchResult;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function ShapeBase::damage(%this, %sourceObject, %position, %damage, %damageType)
|
||||
{
|
||||
// All damage applied by one object to another should go through this method.
|
||||
// This function is provided to allow objects some chance of overriding or
|
||||
// processing damage values and types. As opposed to having weapons call
|
||||
// ShapeBase::applyDamage directly. Damage is redirected to the datablock,
|
||||
// this is standard procedure for many built in callbacks.
|
||||
|
||||
if (isObject(%this))
|
||||
%this.getDataBlock().damage(%this, %sourceObject, %position, %damage, %damageType);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function ShapeBase::setDamageDt(%this, %damageAmount, %damageType)
|
||||
{
|
||||
// This function is used to apply damage over time. The damage is applied
|
||||
// at a fixed rate (50 ms). Damage could be applied over time using the
|
||||
// built in ShapBase C++ repair functions (using a neg. repair), but this
|
||||
// has the advantage of going through the normal script channels.
|
||||
|
||||
if (%this.getState() !$= "Dead")
|
||||
{
|
||||
%this.damage(0, "0 0 0", %damageAmount, %damageType);
|
||||
%this.damageSchedule = %this.schedule(50, "setDamageDt", %damageAmount, %damageType);
|
||||
}
|
||||
else
|
||||
%this.damageSchedule = "";
|
||||
}
|
||||
|
||||
function ShapeBase::clearDamageDt(%this)
|
||||
{
|
||||
if (%this.damageSchedule !$= "")
|
||||
{
|
||||
cancel(%this.damageSchedule);
|
||||
%this.damageSchedule = "";
|
||||
}
|
||||
}
|
||||
|
||||
function GameBase::damage(%this, %sourceObject, %position, %damage, %damageType)
|
||||
{
|
||||
// All damage applied by one object to another should go through this method.
|
||||
// This function is provided to allow objects some chance of overriding or
|
||||
// processing damage values and types. As opposed to having weapons call
|
||||
// ShapeBase::applyDamage directly. Damage is redirected to the datablock,
|
||||
// this is standard procedure for many built in callbacks.
|
||||
|
||||
%datablock = %this.getDataBlock();
|
||||
if ( isObject( %datablock ) )
|
||||
%datablock.damage(%this, %sourceObject, %position, %damage, %damageType);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ShapeBase datablock
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function ShapeBaseData::damage(%this, %obj, %source, %position, %amount, %damageType)
|
||||
{
|
||||
// Ignore damage by default. This empty method is here to
|
||||
// avoid console warnings.
|
||||
}
|
||||
346
Templates/Modules/FPSGameplay/scripts/server/spawn.cs
Normal file
346
Templates/Modules/FPSGameplay/scripts/server/spawn.cs
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// What kind of "player" is spawned is either controlled directly by the
|
||||
// SpawnSphere or it defaults back to the values set here. This also controls
|
||||
// which SimGroups to attempt to select the spawn sphere's from by walking down
|
||||
// the list of SpawnGroups till it finds a valid spawn object.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Leave $Game::defaultPlayerClass and $Game::defaultPlayerDataBlock as empty strings ("")
|
||||
// to spawn a the $Game::defaultCameraClass as the control object.
|
||||
$Game::DefaultPlayerClass = "Player";
|
||||
$Game::DefaultPlayerDataBlock = "DefaultPlayerData";
|
||||
$Game::DefaultPlayerSpawnGroups = "PlayerSpawnPoints";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// What kind of "camera" is spawned is either controlled directly by the
|
||||
// SpawnSphere or it defaults back to the values set here. This also controls
|
||||
// which SimGroups to attempt to select the spawn sphere's from by walking down
|
||||
// the list of SpawnGroups till it finds a valid spawn object.
|
||||
//-----------------------------------------------------------------------------
|
||||
$Game::DefaultCameraClass = "Camera";
|
||||
$Game::DefaultCameraDataBlock = "Observer";
|
||||
$Game::DefaultCameraSpawnGroups = "CameraSpawnPoints PlayerSpawnPoints";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// pickCameraSpawnPoint() is responsible for finding a valid spawn point for a
|
||||
// camera.
|
||||
//-----------------------------------------------------------------------------
|
||||
function pickCameraSpawnPoint(%spawnGroups)
|
||||
{
|
||||
// Walk through the groups until we find a valid object
|
||||
for (%i = 0; %i < getWordCount(%spawnGroups); %i++)
|
||||
{
|
||||
%group = getWord(%spawnGroups, %i);
|
||||
|
||||
%count = getWordCount(%group);
|
||||
|
||||
if (isObject(%group))
|
||||
%spawnPoint = %group.getRandom();
|
||||
|
||||
if (isObject(%spawnPoint))
|
||||
return %spawnPoint;
|
||||
}
|
||||
|
||||
// Didn't find a spawn point by looking for the groups
|
||||
// so let's return the "default" SpawnSphere
|
||||
// First create it if it doesn't already exist
|
||||
if (!isObject(DefaultCameraSpawnSphere))
|
||||
{
|
||||
%spawn = new SpawnSphere(DefaultCameraSpawnSphere)
|
||||
{
|
||||
dataBlock = "SpawnSphereMarker";
|
||||
spawnClass = $Game::DefaultCameraClass;
|
||||
spawnDatablock = $Game::DefaultCameraDataBlock;
|
||||
};
|
||||
|
||||
// Add it to the MissionCleanup group so that it
|
||||
// doesn't get saved to the Mission (and gets cleaned
|
||||
// up of course)
|
||||
MissionCleanup.add(%spawn);
|
||||
}
|
||||
|
||||
return DefaultCameraSpawnSphere;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// pickPlayerSpawnPoint() is responsible for finding a valid spawn point for a
|
||||
// player.
|
||||
//-----------------------------------------------------------------------------
|
||||
function pickPlayerSpawnPoint(%spawnGroups)
|
||||
{
|
||||
// Walk through the groups until we find a valid object
|
||||
for (%i = 0; %i < getWordCount(%spawnGroups); %i++)
|
||||
{
|
||||
%group = getWord(%spawnGroups, %i);
|
||||
|
||||
if (isObject(%group))
|
||||
%spawnPoint = %group.getRandom();
|
||||
|
||||
if (isObject(%spawnPoint))
|
||||
return %spawnPoint;
|
||||
}
|
||||
|
||||
// Didn't find a spawn point by looking for the groups
|
||||
// so let's return the "default" SpawnSphere
|
||||
// First create it if it doesn't already exist
|
||||
if (!isObject(DefaultPlayerSpawnSphere))
|
||||
{
|
||||
%spawn = new SpawnSphere(DefaultPlayerSpawnSphere)
|
||||
{
|
||||
dataBlock = "SpawnSphereMarker";
|
||||
spawnClass = $Game::DefaultPlayerClass;
|
||||
spawnDatablock = $Game::DefaultPlayerDataBlock;
|
||||
};
|
||||
|
||||
// Add it to the MissionCleanup group so that it
|
||||
// doesn't get saved to the Mission (and gets cleaned
|
||||
// up of course)
|
||||
MissionCleanup.add(%spawn);
|
||||
}
|
||||
|
||||
return DefaultPlayerSpawnSphere;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GameConnection::spawnCamera() is responsible for spawning a camera for a
|
||||
// client
|
||||
//-----------------------------------------------------------------------------
|
||||
//function GameConnection::spawnCamera(%this, %spawnPoint)
|
||||
//{
|
||||
//// Set the control object to the default camera
|
||||
//if (!isObject(%this.camera))
|
||||
//{
|
||||
//if (isDefined("$Game::DefaultCameraClass"))
|
||||
//%this.camera = spawnObject($Game::DefaultCameraClass, $Game::DefaultCameraDataBlock);
|
||||
//}
|
||||
//
|
||||
//if(!isObject(%this.PathCamera))
|
||||
//{
|
||||
//// Create path camera
|
||||
//%this.PathCamera = spawnObject("PathCamera", "LoopingCam");
|
||||
////%this.PathCamera = new PathCamera() {
|
||||
////dataBlock = LoopingCam;
|
||||
////position = "0 0 300 1 0 0 0";
|
||||
////};
|
||||
//}
|
||||
//if(isObject(%this.PathCamera))
|
||||
//{
|
||||
//%this.PathCamera.setPosition("-54.0187 1.81237 5.14039");
|
||||
//%this.PathCamera.followPath(MenuPath);
|
||||
//MissionCleanup.add( %this.PathCamera);
|
||||
//%this.PathCamera.scopeToClient(%this);
|
||||
//%this.setControlObject(%this.PathCamera);
|
||||
//}
|
||||
//// If we have a camera then set up some properties
|
||||
//if (isObject(%this.camera))
|
||||
//{
|
||||
//MissionCleanup.add( %this.camera );
|
||||
//%this.camera.scopeToClient(%this);
|
||||
//
|
||||
////%this.setControlObject(%this.camera);
|
||||
////%this.setControlObject(%this.PathCamera);
|
||||
//
|
||||
//if (isDefined("%spawnPoint"))
|
||||
//{
|
||||
//// Attempt to treat %spawnPoint as an object
|
||||
//if (getWordCount(%spawnPoint) == 1 && isObject(%spawnPoint))
|
||||
//{
|
||||
//%this.camera.setTransform(%spawnPoint.getTransform());
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
//// Treat %spawnPoint as an AxisAngle transform
|
||||
//%this.camera.setTransform(%spawnPoint);
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
|
||||
function GameConnection::spawnCamera(%this, %spawnPoint)
|
||||
{
|
||||
// Set the control object to the default camera
|
||||
if (!isObject(%this.camera))
|
||||
{
|
||||
if (isDefined("$Game::DefaultCameraClass"))
|
||||
%this.camera = spawnObject($Game::DefaultCameraClass, $Game::DefaultCameraDataBlock);
|
||||
}
|
||||
|
||||
// If we have a camera then set up some properties
|
||||
if (isObject(%this.camera))
|
||||
{
|
||||
MissionCleanup.add( %this.camera );
|
||||
%this.camera.scopeToClient(%this);
|
||||
|
||||
%this.setControlObject(%this.camera);
|
||||
|
||||
if (isDefined("%spawnPoint"))
|
||||
{
|
||||
// Attempt to treat %spawnPoint as an object
|
||||
if (getWordCount(%spawnPoint) == 1 && isObject(%spawnPoint))
|
||||
{
|
||||
%this.camera.setTransform(%spawnPoint.getTransform());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Treat %spawnPoint as an AxisAngle transform
|
||||
%this.camera.setTransform(%spawnPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GameConnection::spawnPlayer() is responsible for spawning a player for a
|
||||
// client
|
||||
//-----------------------------------------------------------------------------
|
||||
function GameConnection::spawnPlayer(%this, %spawnPoint, %noControl)
|
||||
{
|
||||
if (isObject(%this.player))
|
||||
{
|
||||
// The client should not already have a player. Assigning
|
||||
// a new one could result in an uncontrolled player object.
|
||||
error("Attempting to create a player for a client that already has one!");
|
||||
}
|
||||
|
||||
// Attempt to treat %spawnPoint as an object
|
||||
if (getWordCount(%spawnPoint) == 1 && isObject(%spawnPoint))
|
||||
{
|
||||
// Defaults
|
||||
%spawnClass = $Game::DefaultPlayerClass;
|
||||
%spawnDataBlock = $Game::DefaultPlayerDataBlock;
|
||||
|
||||
// Overrides by the %spawnPoint
|
||||
if (isDefined("%spawnPoint.spawnClass"))
|
||||
{
|
||||
%spawnClass = %spawnPoint.spawnClass;
|
||||
%spawnDataBlock = %spawnPoint.spawnDatablock;
|
||||
}
|
||||
|
||||
// This may seem redundant given the above but it allows
|
||||
// the SpawnSphere to override the datablock without
|
||||
// overriding the default player class
|
||||
if (isDefined("%spawnPoint.spawnDatablock"))
|
||||
%spawnDataBlock = %spawnPoint.spawnDatablock;
|
||||
|
||||
%spawnProperties = %spawnPoint.spawnProperties;
|
||||
%spawnScript = %spawnPoint.spawnScript;
|
||||
|
||||
// Spawn with the engine's Sim::spawnObject() function
|
||||
%player = spawnObject(%spawnClass, %spawnDatablock, "",
|
||||
%spawnProperties, %spawnScript);
|
||||
|
||||
// If we have an object do some initial setup
|
||||
if (isObject(%player))
|
||||
{
|
||||
// Set the transform to %spawnPoint's transform
|
||||
%player.setTransform(%spawnPoint.getTransform());
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we weren't able to create the player object then warn the user
|
||||
if (isDefined("%spawnDatablock"))
|
||||
{
|
||||
MessageBoxOK("Spawn Player Failed",
|
||||
"Unable to create a player with class " @ %spawnClass @
|
||||
" and datablock " @ %spawnDatablock @ ".\n\nStarting as an Observer instead.",
|
||||
%this @ ".spawnCamera();");
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxOK("Spawn Player Failed",
|
||||
"Unable to create a player with class " @ %spawnClass @
|
||||
".\n\nStarting as an Observer instead.",
|
||||
%this @ ".spawnCamera();");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a default player
|
||||
%player = spawnObject($Game::DefaultPlayerClass, $Game::DefaultPlayerDataBlock);
|
||||
|
||||
if (!%player.isMemberOfClass("Player"))
|
||||
warn("Trying to spawn a class that does not derive from Player.");
|
||||
|
||||
//Ensure we have a valid spawn point
|
||||
if(%spawnPoint $= "")
|
||||
{
|
||||
%spawnPoint = pickPlayerSpawnPoint($Game::DefaultPlayerSpawnGroups).getTransform();
|
||||
}
|
||||
|
||||
// Treat %spawnPoint as a transform
|
||||
%player.setTransform(%spawnPoint);
|
||||
}
|
||||
|
||||
// If we didn't actually create a player object then bail
|
||||
if (!isObject(%player))
|
||||
{
|
||||
// Make sure we at least have a camera
|
||||
%this.spawnCamera(%spawnPoint);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the default camera to start with the player
|
||||
if (isObject(%this.camera))
|
||||
{
|
||||
if (%player.getClassname() $= "Player")
|
||||
%this.camera.setTransform(%player.getEyeTransform());
|
||||
else
|
||||
%this.camera.setTransform(%player.getTransform());
|
||||
}
|
||||
|
||||
// Add the player object to MissionCleanup so that it
|
||||
// won't get saved into the level files and will get
|
||||
// cleaned up properly
|
||||
MissionCleanup.add(%player);
|
||||
|
||||
// Store the client object on the player object for
|
||||
// future reference
|
||||
%player.client = %this;
|
||||
|
||||
// Player setup...
|
||||
if (%player.isMethod("setShapeName"))
|
||||
%player.setShapeName(%this.playerName);
|
||||
|
||||
if (%player.isMethod("setEnergyLevel"))
|
||||
%player.setEnergyLevel(%player.getDataBlock().maxEnergy);
|
||||
|
||||
// Give the client control of the player
|
||||
%this.player = %player;
|
||||
|
||||
// Give the client control of the camera if in the editor
|
||||
if( $startWorldEditor )
|
||||
{
|
||||
%control = %this.camera;
|
||||
%control.mode = toggleCameraFly;
|
||||
EditorGui.syncCameraGui();
|
||||
}
|
||||
else
|
||||
%control = %player;
|
||||
|
||||
if(!isDefined("%noControl"))
|
||||
%this.setControlObject(%control);
|
||||
}
|
||||
251
Templates/Modules/FPSGameplay/scripts/server/teleporter.cs
Normal file
251
Templates/Modules/FPSGameplay/scripts/server/teleporter.cs
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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);
|
||||
}
|
||||
48
Templates/Modules/FPSGameplay/scripts/server/triggers.cs
Normal file
48
Templates/Modules/FPSGameplay/scripts/server/triggers.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DefaultTrigger is used by the mission editor. This is also an example
|
||||
// of trigger methods and callbacks.
|
||||
|
||||
function DefaultTrigger::onEnterTrigger(%this,%trigger,%obj)
|
||||
{
|
||||
// This method is called whenever an object enters the %trigger
|
||||
// area, the object is passed as %obj.
|
||||
}
|
||||
|
||||
function DefaultTrigger::onLeaveTrigger(%this,%trigger,%obj)
|
||||
{
|
||||
// This method is called whenever an object leaves the %trigger
|
||||
// area, the object is passed as %obj.
|
||||
}
|
||||
|
||||
function DefaultTrigger::onTickTrigger(%this,%trigger)
|
||||
{
|
||||
// This method is called every tickPerioMS, as long as any
|
||||
// objects intersect the trigger.
|
||||
|
||||
// You can iterate through the objects in the list by using these
|
||||
// methods:
|
||||
// %trigger.getNumObjects();
|
||||
// %trigger.getObject(n);
|
||||
}
|
||||
492
Templates/Modules/FPSGameplay/scripts/server/turret.cs
Normal file
492
Templates/Modules/FPSGameplay/scripts/server/turret.cs
Normal file
|
|
@ -0,0 +1,492 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Respawntime is the amount of time it takes for a static "auto-respawn"
|
||||
// turret to re-appear after it's been picked up. Any turret marked as "static"
|
||||
// is automaticlly respawned.
|
||||
$TurretShape::RespawnTime = 30 * 1000;
|
||||
|
||||
// DestroyedFadeDelay is the how long a destroyed turret sticks around before it
|
||||
// fades out and is deleted.
|
||||
$TurretShape::DestroyedFadeDelay = 5 * 1000;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TurretShapeData
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function TurretShapeData::onAdd(%this, %obj)
|
||||
{
|
||||
%obj.setRechargeRate(%this.rechargeRate);
|
||||
%obj.setEnergyLevel(%this.MaxEnergy);
|
||||
%obj.setRepairRate(0);
|
||||
|
||||
if (%obj.mountable || %obj.mountable $= "")
|
||||
%this.isMountable(%obj, true);
|
||||
else
|
||||
%this.isMountable(%obj, false);
|
||||
|
||||
if (%this.nameTag !$= "")
|
||||
%obj.setShapeName(%this.nameTag);
|
||||
|
||||
// Mount weapons
|
||||
for(%i = 0; %i < %this.numWeaponMountPoints; %i++)
|
||||
{
|
||||
// Handle inventory
|
||||
%obj.incInventory(%this.weapon[%i], 1);
|
||||
%obj.incInventory(%this.weaponAmmo[%i], %this.weaponAmmoAmount[%i]);
|
||||
|
||||
// Mount the image
|
||||
%obj.mountImage(%this.weapon[%i].image, %i, %this.startLoaded);
|
||||
%obj.setImageGenericTrigger(%i, 0, false); // Used to indicate the turret is destroyed
|
||||
}
|
||||
|
||||
if (%this.enterSequence !$= "")
|
||||
{
|
||||
%obj.entranceThread = 0;
|
||||
%obj.playThread(%obj.entranceThread, %this.enterSequence);
|
||||
%obj.pauseThread(%obj.entranceThread);
|
||||
}
|
||||
else
|
||||
{
|
||||
%obj.entranceThread = -1;
|
||||
}
|
||||
}
|
||||
|
||||
function TurretShapeData::onRemove(%this, %obj)
|
||||
{
|
||||
//echo("\c4TurretShapeData::onRemove("@ %this.getName() @", "@ %obj.getClassName() @")");
|
||||
|
||||
// if there are passengers/driver, kick them out
|
||||
for(%i = 0; %i < %this.numMountPoints; %i++)
|
||||
{
|
||||
if (%obj.getMountNodeObject(%i))
|
||||
{
|
||||
%passenger = %obj.getMountNodeObject(%i);
|
||||
%passenger.getDataBlock().doDismount(%passenger, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is on MissionGroup so it doesn't happen when the mission has ended
|
||||
function MissionGroup::respawnTurret(%this, %datablock, %className, %transform, %static, %respawn)
|
||||
{
|
||||
%turret = new (%className)()
|
||||
{
|
||||
datablock = %datablock;
|
||||
static = %static;
|
||||
respawn = %respawn;
|
||||
};
|
||||
|
||||
%turret.setTransform(%transform);
|
||||
MissionGroup.add(%turret);
|
||||
return %turret;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TurretShapeData damage state
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// This method is called by weapons fire
|
||||
function TurretShapeData::damage(%this, %turret, %sourceObject, %position, %damage, %damageType)
|
||||
{
|
||||
//echo("\TurretShapeData::damage(" @ %turret @ ", "@ %sourceObject @ ", " @ %position @ ", "@ %damage @ ", "@ %damageType @ ")");
|
||||
|
||||
if (%turret.getState() $= "Dead")
|
||||
return;
|
||||
|
||||
%turret.applyDamage(%damage);
|
||||
|
||||
// Update the numerical Health HUD
|
||||
%mountedObject = %turret.getObjectMount();
|
||||
if (%mountedObject)
|
||||
%mountedObject.updateHealth();
|
||||
|
||||
// Kill any occupants
|
||||
if (%turret.getState() $= "Dead")
|
||||
{
|
||||
for (%i = 0; %i < %this.numMountPoints; %i++)
|
||||
{
|
||||
%player = %turret.getMountNodeObject(%i);
|
||||
if (%player != 0)
|
||||
%player.killWithSource(%sourceObject, "InsideTurret");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function TurretShapeData::onDamage(%this, %obj, %delta)
|
||||
{
|
||||
// This method is invoked by the ShapeBase code whenever the
|
||||
// object's damage level changes.
|
||||
}
|
||||
|
||||
function TurretShapeData::onDestroyed(%this, %obj, %lastState)
|
||||
{
|
||||
// This method is invoked by the ShapeBase code whenever the
|
||||
// object's damage state changes.
|
||||
|
||||
// Fade out the destroyed object. Then schedule a return.
|
||||
%obj.startFade(1000, $TurretShape::DestroyedFadeDelay, true);
|
||||
%obj.schedule($TurretShape::DestroyedFadeDelay + 1000, "delete");
|
||||
|
||||
if (%obj.doRespawn())
|
||||
{
|
||||
MissionGroup.schedule($TurretShape::RespawnTime, "respawnTurret", %this, %obj.getClassName(), %obj.getTransform(), true, true);
|
||||
}
|
||||
}
|
||||
|
||||
function TurretShapeData::onDisabled(%this, %obj, %lastState)
|
||||
{
|
||||
// This method is invoked by the ShapeBase code whenever the
|
||||
// object's damage state changes.
|
||||
}
|
||||
|
||||
function TurretShapeData::onEnabled(%this, %obj, %lastState)
|
||||
{
|
||||
// This method is invoked by the ShapeBase code whenever the
|
||||
// object's damage state changes.
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TurretShapeData player mounting and dismounting
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function TurretShapeData::isMountable(%this, %obj, %val)
|
||||
{
|
||||
%obj.mountable = %val;
|
||||
}
|
||||
|
||||
function TurretShapeData::onMountObject(%this, %turret, %player, %node)
|
||||
{
|
||||
if (%turret.entranceThread >= 0)
|
||||
{
|
||||
%turret.setThreadDir(%turret.entranceThread, true);
|
||||
%turret.setThreadPosition(%turret.entranceThread, 0);
|
||||
%turret.playThread(%turret.entranceThread, "");
|
||||
}
|
||||
}
|
||||
|
||||
function TurretShapeData::onUnmountObject(%this, %turret, %player)
|
||||
{
|
||||
if (%turret.entranceThread >= 0)
|
||||
{
|
||||
// Play the entrance thread backwards for an exit
|
||||
%turret.setThreadDir(%turret.entranceThread, false);
|
||||
%turret.setThreadPosition(%turret.entranceThread, 1);
|
||||
%turret.playThread(%turret.entranceThread, "");
|
||||
}
|
||||
}
|
||||
|
||||
function TurretShapeData::mountPlayer(%this, %turret, %player)
|
||||
{
|
||||
//echo("\c4TurretShapeData::mountPlayer("@ %this.getName() @", "@ %turret @", "@ %player.client.nameBase @")");
|
||||
|
||||
if (isObject(%turret) && %turret.getDamageState() !$= "Destroyed")
|
||||
{
|
||||
//%player.startFade(1000, 0, true);
|
||||
//%this.schedule(1000, "setMountTurret", %turret, %player);
|
||||
//%player.schedule(1500, "startFade", 1000, 0, false);
|
||||
%this.setMountTurret(%turret, %player);
|
||||
}
|
||||
}
|
||||
|
||||
function TurretShapeData::setMountTurret(%this, %turret, %player)
|
||||
{
|
||||
//echo("\c4TurretShapeData::setMountTurret("@ %this.getName() @", "@ %turret @", "@ %player.client.nameBase @")");
|
||||
|
||||
if (isObject(%turret) && %turret.getDamageState() !$= "Destroyed")
|
||||
{
|
||||
%node = %this.findEmptySeat(%turret, %player);
|
||||
if (%node >= 0)
|
||||
{
|
||||
//echo("\c4Mount Node: "@ %node);
|
||||
%turret.mountObject(%player, %node);
|
||||
//%player.playAudio(0, MountVehicleSound);
|
||||
%player.mVehicle = %turret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function TurretShapeData::findEmptySeat(%this, %turret, %player)
|
||||
{
|
||||
//echo("\c4This turret has "@ %this.numMountPoints @" mount points.");
|
||||
|
||||
for (%i = 0; %i < %this.numMountPoints; %i++)
|
||||
{
|
||||
%node = %turret.getMountNodeObject(%i);
|
||||
if (%node == 0)
|
||||
return %i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function TurretShapeData::switchSeats(%this, %turret, %player)
|
||||
{
|
||||
for (%i = 0; %i < %this.numMountPoints; %i++)
|
||||
{
|
||||
%node = %turret.getMountNodeObject(%i);
|
||||
if (%node == %player || %node > 0)
|
||||
continue;
|
||||
|
||||
if (%node == 0)
|
||||
return %i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function TurretShapeData::onMount(%this, %turret, %player, %node)
|
||||
{
|
||||
//echo("\c4TurretShapeData::onMount("@ %this.getName() @", "@ %turret @", "@ %player.client.nameBase @")");
|
||||
|
||||
%player.client.RefreshVehicleHud(%turret, %this.reticle, %this.zoomReticle);
|
||||
//%player.client.UpdateVehicleHealth(%turret);
|
||||
}
|
||||
|
||||
function TurretShapeData::onUnmount(%this, %turret, %player, %node)
|
||||
{
|
||||
//echo("\c4TurretShapeData::onUnmount(" @ %this.getName() @ ", " @ %turret @ ", " @ %player.client.nameBase @ ")");
|
||||
|
||||
%player.client.RefreshVehicleHud(0, "", "");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TurretShape damage
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// This method is called by weapons fire
|
||||
function TurretShape::damage(%this, %sourceObject, %position, %damage, %damageType)
|
||||
{
|
||||
//echo("\TurretShape::damage(" @ %this @ ", "@ %sourceObject @ ", " @ %position @ ", "@ %damage @ ", "@ %damageType @ ")");
|
||||
|
||||
%this.getDataBlock().damage(%this, %sourceObject, %position, %damage, %damageType);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TurretDamage
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Customized kill message for deaths caused by turrets
|
||||
function sendMsgClientKilled_TurretDamage( %msgType, %client, %sourceClient, %damLoc )
|
||||
{
|
||||
if ( %sourceClient $= "" ) // editor placed turret
|
||||
messageAll( %msgType, '%1 was shot down by a turret!', %client.playerName );
|
||||
else if ( %sourceClient == %client ) // own mine
|
||||
messageAll( %msgType, '%1 kill by his own turret!', %client.playerName );
|
||||
else // enemy placed mine
|
||||
messageAll( %msgType, '%1 was killed by a turret of %2!', %client.playerName, %sourceClient.playerName );
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// AITurretShapeData
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function AITurretShapeData::onAdd(%this, %obj)
|
||||
{
|
||||
Parent::onAdd(%this, %obj);
|
||||
|
||||
%obj.mountable = false;
|
||||
}
|
||||
|
||||
// Player has thrown a deployable turret. This copies from ItemData::onThrow()
|
||||
function AITurretShapeData::onThrow(%this, %user, %amount)
|
||||
{
|
||||
// Remove the object from the inventory
|
||||
if (%amount $= "")
|
||||
%amount = 1;
|
||||
if (%this.maxInventory !$= "")
|
||||
if (%amount > %this.maxInventory)
|
||||
%amount = %this.maxInventory;
|
||||
if (!%amount)
|
||||
return 0;
|
||||
%user.decInventory(%this,%amount);
|
||||
|
||||
// Construct the actual object in the world, and add it to
|
||||
// the mission group so it's cleaned up when the mission is
|
||||
// done. The turret's rotation matches the player's.
|
||||
%rot = %user.getEulerRotation();
|
||||
%obj = new AITurretShape()
|
||||
{
|
||||
datablock = %this;
|
||||
rotation = "0 0 1 " @ getWord(%rot, 2);
|
||||
count = 1;
|
||||
sourceObject = %user;
|
||||
client = %user.client;
|
||||
isAiControlled = true;
|
||||
};
|
||||
MissionGroup.add(%obj);
|
||||
|
||||
// Let the turret know that we're a firend
|
||||
%obj.addToIgnoreList(%user);
|
||||
|
||||
// We need to add this turret to a list on the client so that if we die,
|
||||
// the turret will still ignore our player.
|
||||
%client = %user.client;
|
||||
if (%client)
|
||||
{
|
||||
if (!%client.ownedTurrets)
|
||||
{
|
||||
%client.ownedTurrets = new SimSet();
|
||||
}
|
||||
|
||||
// Go through the client's owned turret list. Make sure we're
|
||||
// a friend of every turret and every turret is a friend of ours.
|
||||
// Commence hugging!
|
||||
for (%i=0; %i<%client.ownedTurrets.getCount(); %i++)
|
||||
{
|
||||
%turret = %client.ownedTurrets.getObject(%i);
|
||||
%turret.addToIgnoreList(%obj);
|
||||
%obj.addToIgnoreList(%turret);
|
||||
}
|
||||
|
||||
// Add ourselves to the client's owned list.
|
||||
%client.ownedTurrets.add(%obj);
|
||||
}
|
||||
|
||||
return %obj;
|
||||
}
|
||||
|
||||
function AITurretShapeData::onDestroyed(%this, %turret, %lastState)
|
||||
{
|
||||
// This method is invoked by the ShapeBase code whenever the
|
||||
// object's damage state changes.
|
||||
|
||||
%turret.playAudio(0, TurretDestroyed);
|
||||
%turret.setAllGunsFiring(false);
|
||||
%turret.resetTarget();
|
||||
%turret.setTurretState( "Destroyed", true );
|
||||
|
||||
// Set the weapons to destoryed
|
||||
for(%i = 0; %i < %this.numWeaponMountPoints; %i++)
|
||||
{
|
||||
%turret.setImageGenericTrigger(%i, 0, true);
|
||||
}
|
||||
|
||||
Parent::onDestroyed(%this, %turret, %lastState);
|
||||
}
|
||||
|
||||
function AITurretShapeData::OnScanning(%this, %turret)
|
||||
{
|
||||
//echo("AITurretShapeData::OnScanning: " SPC %this SPC %turret);
|
||||
|
||||
%turret.startScanForTargets();
|
||||
%turret.playAudio(0, TurretScanningSound);
|
||||
}
|
||||
|
||||
function AITurretShapeData::OnTarget(%this, %turret)
|
||||
{
|
||||
//echo("AITurretShapeData::OnTarget: " SPC %this SPC %turret);
|
||||
|
||||
%turret.startTrackingTarget();
|
||||
%turret.playAudio(0, TargetAquiredSound);
|
||||
}
|
||||
|
||||
function AITurretShapeData::OnNoTarget(%this, %turret)
|
||||
{
|
||||
//echo("AITurretShapeData::OnNoTarget: " SPC %this SPC %turret);
|
||||
|
||||
%turret.setAllGunsFiring(false);
|
||||
%turret.recenterTurret();
|
||||
%turret.playAudio(0, TargetLostSound);
|
||||
}
|
||||
|
||||
function AITurretShapeData::OnFiring(%this, %turret)
|
||||
{
|
||||
//echo("AITurretShapeData::OnFiring: " SPC %this SPC %turret);
|
||||
|
||||
%turret.setAllGunsFiring(true);
|
||||
}
|
||||
|
||||
function AITurretShapeData::OnThrown(%this, %turret)
|
||||
{
|
||||
//echo("AITurretShapeData::OnThrown: " SPC %this SPC %turret);
|
||||
|
||||
%turret.playAudio(0, TurretThrown);
|
||||
}
|
||||
|
||||
function AITurretShapeData::OnDeploy(%this, %turret)
|
||||
{
|
||||
//echo("AITurretShapeData::OnDeploy: " SPC %this SPC %turret);
|
||||
|
||||
// Set the weapons to loaded
|
||||
for(%i = 0; %i < %this.numWeaponMountPoints; %i++)
|
||||
{
|
||||
%turret.setImageLoaded(%i, true);
|
||||
}
|
||||
|
||||
%turret.playAudio(0, TurretActivatedSound);
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Player deployable turret
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Cannot use the Weapon class for deployable turrets as it is already tied
|
||||
// to ItemData.
|
||||
|
||||
function DeployableTurretWeapon::onUse(%this, %obj)
|
||||
{
|
||||
Weapon::onUse(%this, %obj);
|
||||
}
|
||||
|
||||
function DeployableTurretWeapon::onPickup(%this, %obj, %shape, %amount)
|
||||
{
|
||||
Weapon::onPickup(%this, %obj, %shape, %amount);
|
||||
}
|
||||
|
||||
function DeployableTurretWeapon::onInventory(%this, %obj, %amount)
|
||||
{
|
||||
if (%obj.client !$= "" && !%obj.isAiControlled)
|
||||
{
|
||||
%obj.client.setAmmoAmountHud( 1, %amount );
|
||||
}
|
||||
|
||||
// Cycle weapons if we are out of ammo
|
||||
if ( !%amount && ( %slot = %obj.getMountSlot( %this.image ) ) != -1 )
|
||||
%obj.cycleWeapon( "prev" );
|
||||
}
|
||||
|
||||
function DeployableTurretWeaponImage::onMount(%this, %obj, %slot)
|
||||
{
|
||||
// The turret doesn't use ammo from a player's perspective.
|
||||
%obj.setImageAmmo(%slot, true);
|
||||
%numTurrets = %obj.getInventory(%this.item);
|
||||
|
||||
if (%obj.client !$= "" && !%obj.isAiControlled)
|
||||
%obj.client.RefreshWeaponHud( 1, %this.item.previewImage, %this.item.reticle, %this.item.zoomReticle, %numTurrets);
|
||||
}
|
||||
|
||||
function DeployableTurretWeaponImage::onUnmount(%this, %obj, %slot)
|
||||
{
|
||||
if (%obj.client !$= "" && !%obj.isAiControlled)
|
||||
%obj.client.RefreshWeaponHud(0, "", "");
|
||||
}
|
||||
|
||||
function DeployableTurretWeaponImage::onFire(%this, %obj, %slot)
|
||||
{
|
||||
//echo("\DeployableTurretWeaponImage::onFire( "@%this.getName()@", "@%obj.client.nameBase@", "@%slot@" )");
|
||||
|
||||
// To fire a deployable turret is to throw it. Schedule the throw
|
||||
// so that it doesn't happen during this ShapeBaseImageData's state machine.
|
||||
// If we throw the last one then we end up unmounting while the state machine
|
||||
// is still being processed.
|
||||
%obj.schedule(0, "throw", %this.item);
|
||||
}
|
||||
130
Templates/Modules/FPSGameplay/scripts/server/vehicle.cs
Normal file
130
Templates/Modules/FPSGameplay/scripts/server/vehicle.cs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Parenting is in place for WheeledVehicleData to VehicleData. This should
|
||||
// make it easier for people to simply drop in new (generic) vehicles. All that
|
||||
// the user needs to create is a set of datablocks for the new wheeled vehicle
|
||||
// to use. This means that no (or little) scripting should be necessary.
|
||||
|
||||
// Special, or unique vehicles however will still require some scripting. They
|
||||
// may need to override the onAdd() function in order to mount weapons,
|
||||
// differing tires/springs, etc., almost everything else is taken care of in the
|
||||
// WheeledVehicleData and VehicleData methods. This helps us by not having to
|
||||
// duplicate the same code for every new vehicle.
|
||||
|
||||
// In theory this would work for HoverVehicles and FlyingVehicles also, but
|
||||
// hasn't been tested or fully implemented for those classes -- yet.
|
||||
|
||||
function VehicleData::onAdd(%this, %obj)
|
||||
{
|
||||
%obj.setRechargeRate(%this.rechargeRate);
|
||||
%obj.setEnergyLevel(%this.MaxEnergy);
|
||||
%obj.setRepairRate(0);
|
||||
|
||||
if (%obj.mountable || %obj.mountable $= "")
|
||||
%this.isMountable(%obj, true);
|
||||
else
|
||||
%this.isMountable(%obj, false);
|
||||
|
||||
if (%this.nameTag !$= "")
|
||||
%obj.setShapeName(%this.nameTag);
|
||||
}
|
||||
|
||||
function VehicleData::onRemove(%this, %obj)
|
||||
{
|
||||
//echo("\c4VehicleData::onRemove("@ %this.getName() @", "@ %obj.getClassName() @")");
|
||||
|
||||
// if there are passengers/driver, kick them out
|
||||
for(%i = 0; %i < %obj.getDatablock().numMountPoints; %i++)
|
||||
{
|
||||
if (%obj.getMountNodeObject(%i))
|
||||
{
|
||||
%passenger = %obj.getMountNodeObject(%i);
|
||||
%passenger.getDataBlock().doDismount(%passenger, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Vehicle player mounting and dismounting
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function VehicleData::isMountable(%this, %obj, %val)
|
||||
{
|
||||
%obj.mountable = %val;
|
||||
}
|
||||
|
||||
function VehicleData::mountPlayer(%this, %vehicle, %player)
|
||||
{
|
||||
//echo("\c4VehicleData::mountPlayer("@ %this.getName() @", "@ %vehicle @", "@ %player.client.nameBase @")");
|
||||
|
||||
if (isObject(%vehicle) && %vehicle.getDamageState() !$= "Destroyed")
|
||||
{
|
||||
%player.startFade(1000, 0, true);
|
||||
%this.schedule(1000, "setMountVehicle", %vehicle, %player);
|
||||
%player.schedule(1500, "startFade", 1000, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
function VehicleData::setMountVehicle(%this, %vehicle, %player)
|
||||
{
|
||||
//echo("\c4VehicleData::setMountVehicle("@ %this.getName() @", "@ %vehicle @", "@ %player.client.nameBase @")");
|
||||
|
||||
if (isObject(%vehicle) && %vehicle.getDamageState() !$= "Destroyed")
|
||||
{
|
||||
%node = %this.findEmptySeat(%vehicle, %player);
|
||||
if (%node >= 0)
|
||||
{
|
||||
//echo("\c4Mount Node: "@ %node);
|
||||
%vehicle.mountObject(%player, %node);
|
||||
//%player.playAudio(0, MountVehicleSound);
|
||||
%player.mVehicle = %vehicle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function VehicleData::findEmptySeat(%this, %vehicle, %player)
|
||||
{
|
||||
//echo("\c4This vehicle has "@ %this.numMountPoints @" mount points.");
|
||||
|
||||
for (%i = 0; %i < %this.numMountPoints; %i++)
|
||||
{
|
||||
%node = %vehicle.getMountNodeObject(%i);
|
||||
if (%node == 0)
|
||||
return %i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function VehicleData::switchSeats(%this, %vehicle, %player)
|
||||
{
|
||||
for (%i = 0; %i < %this.numMountPoints; %i++)
|
||||
{
|
||||
%node = %vehicle.getMountNodeObject(%i);
|
||||
if (%node == %player || %node > 0)
|
||||
continue;
|
||||
|
||||
if (%node == 0)
|
||||
return %i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
102
Templates/Modules/FPSGameplay/scripts/server/vehicleWheeled.cs
Normal file
102
Templates/Modules/FPSGameplay/scripts/server/vehicleWheeled.cs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// This file contains script methods unique to the WheeledVehicle class. All
|
||||
// other necessary methods are contained in "../server/scripts/vehicle.cs" in
|
||||
// which the "generic" Vehicle class methods that are shared by all vehicles,
|
||||
// (flying, hover, and wheeled) can be found.
|
||||
|
||||
// Parenting is in place for WheeledVehicleData to VehicleData. This should
|
||||
// make it easier for people to simply drop in new (generic) vehicles. All that
|
||||
// the user needs to create is a set of datablocks for the new wheeled vehicle
|
||||
// to use. This means that no (or little) scripting should be necessary.
|
||||
|
||||
function WheeledVehicleData::onAdd(%this, %obj)
|
||||
{
|
||||
Parent::onAdd(%this, %obj);
|
||||
|
||||
// Setup the car with some tires & springs
|
||||
for (%i = %obj.getWheelCount() - 1; %i >= 0; %i--)
|
||||
{
|
||||
%obj.setWheelTire(%i, DefaultCarTire);
|
||||
%obj.setWheelSpring(%i, DefaultCarSpring);
|
||||
%obj.setWheelPowered(%i, false);
|
||||
}
|
||||
|
||||
// Steer with the front tires
|
||||
%obj.setWheelSteering(0, 1);
|
||||
%obj.setWheelSteering(1, 1);
|
||||
|
||||
// Only power the two rear wheels... assuming there are only 4 wheels.
|
||||
%obj.setWheelPowered(2, true);
|
||||
%obj.setWheelPowered(3, true);
|
||||
}
|
||||
|
||||
function WheeledVehicleData::onCollision(%this, %obj, %col, %vec, %speed)
|
||||
{
|
||||
// Collision with other objects, including items
|
||||
}
|
||||
|
||||
// Used to kick the players out of the car that your crosshair is over
|
||||
function serverCmdcarUnmountObj(%client, %obj)
|
||||
{
|
||||
%obj.unmount();
|
||||
%obj.setControlObject(%obj);
|
||||
|
||||
%ejectpos = %obj.getPosition();
|
||||
%ejectpos = VectorAdd(%ejectpos, "0 0 5");
|
||||
%obj.setTransform(%ejectpos);
|
||||
|
||||
%ejectvel = %obj.mVehicle.getVelocity();
|
||||
%ejectvel = VectorAdd(%ejectvel, "0 0 10");
|
||||
%ejectvel = VectorScale(%ejectvel, %obj.getDataBlock().mass);
|
||||
%obj.applyImpulse(%ejectpos, %ejectvel);
|
||||
}
|
||||
|
||||
// Used to flip the car over if it manages to get stuck upside down
|
||||
function serverCmdflipCar(%client)
|
||||
{
|
||||
%car = %client.player.getControlObject();
|
||||
|
||||
if (%car.getClassName() $= "WheeledVehicle")
|
||||
{
|
||||
%carPos = %car.getPosition();
|
||||
%carPos = VectorAdd(%carPos, "0 0 3");
|
||||
|
||||
%car.setTransform(%carPos SPC "0 0 1 0");
|
||||
}
|
||||
}
|
||||
|
||||
function serverCmdsetPlayerControl(%client)
|
||||
{
|
||||
%client.setControlObject(%client.player);
|
||||
}
|
||||
|
||||
function serverCmddismountVehicle(%client)
|
||||
{
|
||||
%car = %client.player.getControlObject();
|
||||
|
||||
%passenger = %car.getMountNodeObject(0);
|
||||
%passenger.getDataBlock().doDismount(%passenger, true);
|
||||
|
||||
%client.setControlObject(%client.player);
|
||||
}
|
||||
643
Templates/Modules/FPSGameplay/scripts/server/weapon.cs
Normal file
643
Templates/Modules/FPSGameplay/scripts/server/weapon.cs
Normal file
|
|
@ -0,0 +1,643 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file contains Weapon and Ammo Class/"namespace" helper methods as well
|
||||
// as hooks into the inventory system. These functions are not attached to a
|
||||
// specific C++ class or datablock, but define a set of methods which are part
|
||||
// of dynamic namespaces "class". The Items include these namespaces into their
|
||||
// scope using the ItemData and ItemImageData "className" variable.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// All ShapeBase images are mounted into one of 8 slots on a shape. This weapon
|
||||
// system assumes all primary weapons are mounted into this specified slot:
|
||||
$WeaponSlot = 0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Weapon Class
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function Weapon::onUse(%data, %obj)
|
||||
{
|
||||
// Default behavior for all weapons is to mount it into the object's weapon
|
||||
// slot, which is currently assumed to be slot 0
|
||||
if (%obj.getMountedImage($WeaponSlot) != %data.image.getId())
|
||||
{
|
||||
serverPlay3D(WeaponUseSound, %obj.getTransform());
|
||||
|
||||
%obj.mountImage(%data.image, $WeaponSlot);
|
||||
if (%obj.client)
|
||||
{
|
||||
if (%data.description !$= "")
|
||||
messageClient(%obj.client, 'MsgWeaponUsed', '\c0%1 selected.', %data.description);
|
||||
else
|
||||
messageClient(%obj.client, 'MsgWeaponUsed', '\c0Weapon selected');
|
||||
}
|
||||
|
||||
// If this is a Player class object then allow the weapon to modify allowed poses
|
||||
if (%obj.isInNamespaceHierarchy("Player"))
|
||||
{
|
||||
// Start by allowing everything
|
||||
%obj.allowAllPoses();
|
||||
|
||||
// Now see what isn't allowed by the weapon
|
||||
|
||||
%image = %data.image;
|
||||
|
||||
if (%image.jumpingDisallowed)
|
||||
%obj.allowJumping(false);
|
||||
|
||||
if (%image.jetJumpingDisallowed)
|
||||
%obj.allowJetJumping(false);
|
||||
|
||||
if (%image.sprintDisallowed)
|
||||
%obj.allowSprinting(false);
|
||||
|
||||
if (%image.crouchDisallowed)
|
||||
%obj.allowCrouching(false);
|
||||
|
||||
if (%image.proneDisallowed)
|
||||
%obj.allowProne(false);
|
||||
|
||||
if (%image.swimmingDisallowed)
|
||||
%obj.allowSwimming(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Weapon::onPickup(%this, %obj, %shape, %amount)
|
||||
{
|
||||
// The parent Item method performs the actual pickup.
|
||||
// For player's we automatically use the weapon if the
|
||||
// player does not already have one in hand.
|
||||
if (Parent::onPickup(%this, %obj, %shape, %amount))
|
||||
{
|
||||
serverPlay3D(WeaponPickupSound, %shape.getTransform());
|
||||
if (%shape.getClassName() $= "Player" && %shape.getMountedImage($WeaponSlot) == 0)
|
||||
%shape.use(%this);
|
||||
}
|
||||
}
|
||||
|
||||
function Weapon::onInventory(%this, %obj, %amount)
|
||||
{
|
||||
// Weapon inventory has changed, make sure there are no weapons
|
||||
// of this type mounted if there are none left in inventory.
|
||||
if (!%amount && (%slot = %obj.getMountSlot(%this.image)) != -1)
|
||||
%obj.unmountImage(%slot);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Weapon Image Class
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function WeaponImage::onMount(%this, %obj, %slot)
|
||||
{
|
||||
// Images assume a false ammo state on load. We need to
|
||||
// set the state according to the current inventory.
|
||||
if(%this.isField("clip"))
|
||||
{
|
||||
// Use the clip system for this weapon. Check if the player already has
|
||||
// some ammo in a clip.
|
||||
if (%obj.getInventory(%this.ammo))
|
||||
{
|
||||
%obj.setImageAmmo(%slot, true);
|
||||
%currentAmmo = %obj.getInventory(%this.ammo);
|
||||
}
|
||||
else if(%obj.getInventory(%this.clip) > 0)
|
||||
{
|
||||
// Fill the weapon up from the first clip
|
||||
%obj.setInventory(%this.ammo, %this.ammo.maxInventory);
|
||||
%obj.setImageAmmo(%slot, true);
|
||||
|
||||
// Add any spare ammo that may be "in the player's pocket"
|
||||
%currentAmmo = %this.ammo.maxInventory;
|
||||
%amountInClips += %obj.getFieldValue( "remaining" @ %this.ammo.getName());
|
||||
}
|
||||
else
|
||||
{
|
||||
%currentAmmo = 0 + %obj.getFieldValue( "remaining" @ %this.ammo.getName());
|
||||
}
|
||||
|
||||
%amountInClips = %obj.getInventory(%this.clip);
|
||||
%amountInClips *= %this.ammo.maxInventory;
|
||||
|
||||
if (%obj.client !$= "" && !%obj.isAiControlled)
|
||||
%obj.client.RefreshWeaponHud(%currentAmmo, %this.item.previewImage, %this.item.reticle, %this.item.zoomReticle, %amountInClips);
|
||||
}
|
||||
else if(%this.ammo !$= "")
|
||||
{
|
||||
// Use the ammo pool system for this weapon
|
||||
if (%obj.getInventory(%this.ammo))
|
||||
{
|
||||
%obj.setImageAmmo(%slot, true);
|
||||
%currentAmmo = %obj.getInventory(%this.ammo);
|
||||
}
|
||||
else
|
||||
%currentAmmo = 0;
|
||||
|
||||
if (%obj.client !$= "" && !%obj.isAiControlled)
|
||||
%obj.client.RefreshWeaponHud( 1, %this.item.previewImage, %this.item.reticle, %this.item.zoomReticle, %currentAmmo );
|
||||
}
|
||||
}
|
||||
|
||||
function WeaponImage::onUnmount(%this, %obj, %slot)
|
||||
{
|
||||
if (%obj.client !$= "" && !%obj.isAiControlled)
|
||||
%obj.client.RefreshWeaponHud(0, "", "");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// A "generic" weaponimage onFire handler for most weapons. Can be overridden
|
||||
// with an appropriate namespace method for any weapon that requires a custom
|
||||
// firing solution.
|
||||
|
||||
// projectileSpread is a dynamic property declared in the weaponImage datablock
|
||||
// for those weapons in which bullet skew is desired. Must be greater than 0,
|
||||
// otherwise the projectile goes straight ahead as normal. lower values give
|
||||
// greater accuracy, higher values increase the spread pattern.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function WeaponImage::onFire(%this, %obj, %slot)
|
||||
{
|
||||
//echo("\c4WeaponImage::onFire( "@%this.getName()@", "@%obj.client.nameBase@", "@%slot@" )");
|
||||
|
||||
// Make sure we have valid data
|
||||
if (!isObject(%this.projectile))
|
||||
{
|
||||
error("WeaponImage::onFire() - Invalid projectile datablock");
|
||||
return;
|
||||
}
|
||||
|
||||
// Decrement inventory ammo. The image's ammo state is updated
|
||||
// automatically by the ammo inventory hooks.
|
||||
if ( !%this.infiniteAmmo )
|
||||
%obj.decInventory(%this.ammo, 1);
|
||||
|
||||
// Get the player's velocity, we'll then add it to that of the projectile
|
||||
%objectVelocity = %obj.getVelocity();
|
||||
|
||||
%numProjectiles = %this.projectileNum;
|
||||
if (%numProjectiles == 0)
|
||||
%numProjectiles = 1;
|
||||
|
||||
for (%i = 0; %i < %numProjectiles; %i++)
|
||||
{
|
||||
if (%this.projectileSpread)
|
||||
{
|
||||
// We'll need to "skew" this projectile a little bit. We start by
|
||||
// getting the straight ahead aiming point of the gun
|
||||
%vec = %obj.getMuzzleVector(%slot);
|
||||
|
||||
// Then we'll create a spread matrix by randomly generating x, y, and z
|
||||
// points in a circle
|
||||
%matrix = "";
|
||||
for(%j = 0; %j < 3; %j++)
|
||||
%matrix = %matrix @ (getRandom() - 0.5) * 2 * 3.1415926 * %this.projectileSpread @ " ";
|
||||
%mat = MatrixCreateFromEuler(%matrix);
|
||||
|
||||
// Which we'll use to alter the projectile's initial vector with
|
||||
%muzzleVector = MatrixMulVector(%mat, %vec);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Weapon projectile doesn't have a spread factor so we fire it using
|
||||
// the straight ahead aiming point of the gun
|
||||
%muzzleVector = %obj.getMuzzleVector(%slot);
|
||||
}
|
||||
|
||||
// Add player's velocity
|
||||
%muzzleVelocity = VectorAdd(
|
||||
VectorScale(%muzzleVector, %this.projectile.muzzleVelocity),
|
||||
VectorScale(%objectVelocity, %this.projectile.velInheritFactor));
|
||||
|
||||
// Create the projectile object
|
||||
%p = new (%this.projectileType)()
|
||||
{
|
||||
dataBlock = %this.projectile;
|
||||
initialVelocity = %muzzleVelocity;
|
||||
initialPosition = %obj.getMuzzlePoint(%slot);
|
||||
sourceObject = %obj;
|
||||
sourceSlot = %slot;
|
||||
client = %obj.client;
|
||||
sourceClass = %obj.getClassName();
|
||||
};
|
||||
MissionCleanup.add(%p);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// A "generic" weaponimage onAltFire handler for most weapons. Can be
|
||||
// overridden with an appropriate namespace method for any weapon that requires
|
||||
// a custom firing solution.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function WeaponImage::onAltFire(%this, %obj, %slot)
|
||||
{
|
||||
//echo("\c4WeaponImage::onAltFire("@%this.getName()@", "@%obj.client.nameBase@", "@%slot@")");
|
||||
|
||||
// Decrement inventory ammo. The image's ammo state is updated
|
||||
// automatically by the ammo inventory hooks.
|
||||
%obj.decInventory(%this.ammo, 1);
|
||||
|
||||
// Get the player's velocity, we'll then add it to that of the projectile
|
||||
%objectVelocity = %obj.getVelocity();
|
||||
|
||||
%numProjectiles = %this.altProjectileNum;
|
||||
if (%numProjectiles == 0)
|
||||
%numProjectiles = 1;
|
||||
|
||||
for (%i = 0; %i < %numProjectiles; %i++)
|
||||
{
|
||||
if (%this.altProjectileSpread)
|
||||
{
|
||||
// We'll need to "skew" this projectile a little bit. We start by
|
||||
// getting the straight ahead aiming point of the gun
|
||||
%vec = %obj.getMuzzleVector(%slot);
|
||||
|
||||
// Then we'll create a spread matrix by randomly generating x, y, and z
|
||||
// points in a circle
|
||||
%matrix = "";
|
||||
for(%i = 0; %i < 3; %i++)
|
||||
%matrix = %matrix @ (getRandom() - 0.5) * 2 * 3.1415926 * %this.altProjectileSpread @ " ";
|
||||
%mat = MatrixCreateFromEuler(%matrix);
|
||||
|
||||
// Which we'll use to alter the projectile's initial vector with
|
||||
%muzzleVector = MatrixMulVector(%mat, %vec);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Weapon projectile doesn't have a spread factor so we fire it using
|
||||
// the straight ahead aiming point of the gun.
|
||||
%muzzleVector = %obj.getMuzzleVector(%slot);
|
||||
}
|
||||
|
||||
// Add player's velocity
|
||||
%muzzleVelocity = VectorAdd(
|
||||
VectorScale(%muzzleVector, %this.altProjectile.muzzleVelocity),
|
||||
VectorScale(%objectVelocity, %this.altProjectile.velInheritFactor));
|
||||
|
||||
// Create the projectile object
|
||||
%p = new (%this.projectileType)()
|
||||
{
|
||||
dataBlock = %this.altProjectile;
|
||||
initialVelocity = %muzzleVelocity;
|
||||
initialPosition = %obj.getMuzzlePoint(%slot);
|
||||
sourceObject = %obj;
|
||||
sourceSlot = %slot;
|
||||
client = %obj.client;
|
||||
};
|
||||
MissionCleanup.add(%p);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// A "generic" weaponimage onWetFire handler for most weapons. Can be
|
||||
// overridden with an appropriate namespace method for any weapon that requires
|
||||
// a custom firing solution.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function WeaponImage::onWetFire(%this, %obj, %slot)
|
||||
{
|
||||
//echo("\c4WeaponImage::onWetFire("@%this.getName()@", "@%obj.client.nameBase@", "@%slot@")");
|
||||
|
||||
// Decrement inventory ammo. The image's ammo state is updated
|
||||
// automatically by the ammo inventory hooks.
|
||||
%obj.decInventory(%this.ammo, 1);
|
||||
|
||||
// Get the player's velocity, we'll then add it to that of the projectile
|
||||
%objectVelocity = %obj.getVelocity();
|
||||
|
||||
%numProjectiles = %this.projectileNum;
|
||||
if (%numProjectiles == 0)
|
||||
%numProjectiles = 1;
|
||||
|
||||
for (%i = 0; %i < %numProjectiles; %i++)
|
||||
{
|
||||
if (%this.wetProjectileSpread)
|
||||
{
|
||||
// We'll need to "skew" this projectile a little bit. We start by
|
||||
// getting the straight ahead aiming point of the gun
|
||||
%vec = %obj.getMuzzleVector(%slot);
|
||||
|
||||
// Then we'll create a spread matrix by randomly generating x, y, and z
|
||||
// points in a circle
|
||||
%matrix = "";
|
||||
for(%j = 0; %j < 3; %j++)
|
||||
%matrix = %matrix @ (getRandom() - 0.5) * 2 * 3.1415926 * %this.wetProjectileSpread @ " ";
|
||||
%mat = MatrixCreateFromEuler(%matrix);
|
||||
|
||||
// Which we'll use to alter the projectile's initial vector with
|
||||
%muzzleVector = MatrixMulVector(%mat, %vec);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Weapon projectile doesn't have a spread factor so we fire it using
|
||||
// the straight ahead aiming point of the gun.
|
||||
%muzzleVector = %obj.getMuzzleVector(%slot);
|
||||
}
|
||||
|
||||
// Add player's velocity
|
||||
%muzzleVelocity = VectorAdd(
|
||||
VectorScale(%muzzleVector, %this.wetProjectile.muzzleVelocity),
|
||||
VectorScale(%objectVelocity, %this.wetProjectile.velInheritFactor));
|
||||
|
||||
// Create the projectile object
|
||||
%p = new (%this.projectileType)()
|
||||
{
|
||||
dataBlock = %this.wetProjectile;
|
||||
initialVelocity = %muzzleVelocity;
|
||||
initialPosition = %obj.getMuzzlePoint(%slot);
|
||||
sourceObject = %obj;
|
||||
sourceSlot = %slot;
|
||||
client = %obj.client;
|
||||
};
|
||||
MissionCleanup.add(%p);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Clip Management
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function WeaponImage::onClipEmpty(%this, %obj, %slot)
|
||||
{
|
||||
//echo("WeaponImage::onClipEmpty: " SPC %this SPC %obj SPC %slot);
|
||||
|
||||
// Attempt to automatically reload. Schedule this so it occurs
|
||||
// outside of the current state that called this method
|
||||
%this.schedule(0, "reloadAmmoClip", %obj, %slot);
|
||||
}
|
||||
|
||||
function WeaponImage::reloadAmmoClip(%this, %obj, %slot)
|
||||
{
|
||||
//echo("WeaponImage::reloadAmmoClip: " SPC %this SPC %obj SPC %slot);
|
||||
|
||||
// Make sure we're indeed the currect image on the given slot
|
||||
if (%this != %obj.getMountedImage(%slot))
|
||||
return;
|
||||
|
||||
if ( %this.isField("clip") )
|
||||
{
|
||||
if (%obj.getInventory(%this.clip) > 0)
|
||||
{
|
||||
%obj.decInventory(%this.clip, 1);
|
||||
%obj.setInventory(%this.ammo, %this.ammo.maxInventory);
|
||||
%obj.setImageAmmo(%slot, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
%amountInPocket = %obj.getFieldValue( "remaining" @ %this.ammo.getName());
|
||||
if ( %amountInPocket )
|
||||
{
|
||||
%obj.setFieldValue( "remaining" @ %this.ammo.getName(), 0);
|
||||
%obj.setInventory( %this.ammo, %amountInPocket );
|
||||
%obj.setImageAmmo( %slot, true );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function WeaponImage::clearAmmoClip( %this, %obj, %slot )
|
||||
{
|
||||
//echo("WeaponImage::clearAmmoClip: " SPC %this SPC %obj SPC %slot);
|
||||
|
||||
// if we're not empty put the remaining bullets from the current clip
|
||||
// in to the player's "pocket".
|
||||
|
||||
if ( %this.isField( "clip" ) )
|
||||
{
|
||||
// Commenting out this line will use a "hard clip" system, where
|
||||
// A player will lose any ammo currently in the gun when reloading.
|
||||
%pocketAmount = %this.stashSpareAmmo( %obj );
|
||||
|
||||
if ( %obj.getInventory( %this.clip ) > 0 || %pocketAmount != 0 )
|
||||
%obj.setImageAmmo(%slot, false);
|
||||
}
|
||||
}
|
||||
function WeaponImage::stashSpareAmmo( %this, %player )
|
||||
{
|
||||
// If the amount in our pocket plus what we are about to add from the clip
|
||||
// Is over a clip, add a clip to inventory and keep the remainder
|
||||
// on the player
|
||||
if (%player.getInventory( %this.ammo ) < %this.ammo.maxInventory )
|
||||
{
|
||||
%nameOfAmmoField = "remaining" @ %this.ammo.getName();
|
||||
|
||||
%amountInPocket = %player.getFieldValue( %nameOfAmmoField );
|
||||
|
||||
%amountInGun = %player.getInventory( %this.ammo );
|
||||
|
||||
%combinedAmmo = %amountInGun + %amountInPocket;
|
||||
|
||||
// Give the player another clip if the amount in our pocket + the
|
||||
// Amount in our gun is over the size of a clip.
|
||||
if ( %combinedAmmo >= %this.ammo.maxInventory )
|
||||
{
|
||||
%player.setFieldValue( %nameOfAmmoField, %combinedAmmo - %this.ammo.maxInventory );
|
||||
%player.incInventory( %this.clip, 1 );
|
||||
}
|
||||
else if ( %player.getInventory(%this.clip) > 0 )// Only put it back in our pocket if we have clips.
|
||||
%player.setFieldValue( %nameOfAmmoField, %combinedAmmo );
|
||||
|
||||
return %player.getFieldValue( %nameOfAmmoField );
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Clip Class
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function AmmoClip::onPickup(%this, %obj, %shape, %amount)
|
||||
{
|
||||
// The parent Item method performs the actual pickup.
|
||||
if (Parent::onPickup(%this, %obj, %shape, %amount))
|
||||
serverPlay3D(AmmoPickupSound, %shape.getTransform());
|
||||
|
||||
// The clip inventory state has changed, we need to update the
|
||||
// current mounted image using this clip to reflect the new state.
|
||||
if ((%image = %shape.getMountedImage($WeaponSlot)) > 0)
|
||||
{
|
||||
// Check if this weapon uses the clip we just picked up and if
|
||||
// there is no ammo.
|
||||
if (%image.isField("clip") && %image.clip.getId() == %this.getId())
|
||||
{
|
||||
%outOfAmmo = !%shape.getImageAmmo($WeaponSlot);
|
||||
|
||||
%currentAmmo = %shape.getInventory(%image.ammo);
|
||||
|
||||
if ( isObject( %image.clip ) )
|
||||
%amountInClips = %shape.getInventory(%image.clip);
|
||||
|
||||
%amountInClips *= %image.ammo.maxInventory;
|
||||
%amountInClips += %obj.getFieldValue( "remaining" @ %this.ammo.getName() );
|
||||
|
||||
%shape.client.setAmmoAmountHud(%currentAmmo, %amountInClips );
|
||||
|
||||
if (%outOfAmmo)
|
||||
{
|
||||
%image.onClipEmpty(%shape, $WeaponSlot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Ammmo Class
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function Ammo::onPickup(%this, %obj, %shape, %amount)
|
||||
{
|
||||
// The parent Item method performs the actual pickup.
|
||||
if (Parent::onPickup(%this, %obj, %shape, %amount))
|
||||
serverPlay3D(AmmoPickupSound, %shape.getTransform());
|
||||
}
|
||||
|
||||
function Ammo::onInventory(%this, %obj, %amount)
|
||||
{
|
||||
// The ammo inventory state has changed, we need to update any
|
||||
// mounted images using this ammo to reflect the new state.
|
||||
for (%i = 0; %i < 8; %i++)
|
||||
{
|
||||
if ((%image = %obj.getMountedImage(%i)) > 0)
|
||||
if (isObject(%image.ammo) && %image.ammo.getId() == %this.getId())
|
||||
{
|
||||
%obj.setImageAmmo(%i, %amount != 0);
|
||||
%currentAmmo = %obj.getInventory(%this);
|
||||
|
||||
if (%obj.getClassname() $= "Player")
|
||||
{
|
||||
if ( isObject( %this.clip ) )
|
||||
{
|
||||
%amountInClips = %obj.getInventory(%this.clip);
|
||||
%amountInClips *= %this.maxInventory;
|
||||
%amountInClips += %obj.getFieldValue( "remaining" @ %this.getName() );
|
||||
}
|
||||
else //Is a single fire weapon, like the grenade launcher.
|
||||
{
|
||||
%amountInClips = %currentAmmo;
|
||||
%currentAmmo = 1;
|
||||
}
|
||||
|
||||
if (%obj.client !$= "" && !%obj.isAiControlled)
|
||||
%obj.client.setAmmoAmountHud(%currentAmmo, %amountInClips);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Weapon cycling
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function ShapeBase::clearWeaponCycle(%this)
|
||||
{
|
||||
%this.totalCycledWeapons = 0;
|
||||
}
|
||||
|
||||
function ShapeBase::addToWeaponCycle(%this, %weapon)
|
||||
{
|
||||
%this.cycleWeapon[%this.totalCycledWeapons++ - 1] = %weapon;
|
||||
}
|
||||
|
||||
function ShapeBase::cycleWeapon(%this, %direction)
|
||||
{
|
||||
// Can't cycle what we don't have
|
||||
if (%this.totalCycledWeapons == 0)
|
||||
return;
|
||||
|
||||
// Find out the index of the current weapon, if any (not all
|
||||
// available weapons may be part of the cycle)
|
||||
%currentIndex = -1;
|
||||
if (%this.getMountedImage($WeaponSlot) != 0)
|
||||
{
|
||||
%curWeapon = %this.getMountedImage($WeaponSlot).item.getName();
|
||||
for (%i=0; %i<%this.totalCycledWeapons; %i++)
|
||||
{
|
||||
if (%this.cycleWeapon[%i] $= %curWeapon)
|
||||
{
|
||||
%currentIndex = %i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the next weapon index
|
||||
%nextIndex = 0;
|
||||
%dir = 1;
|
||||
if (%currentIndex != -1)
|
||||
{
|
||||
if (%direction $= "prev")
|
||||
{
|
||||
%dir = -1;
|
||||
%nextIndex = %currentIndex - 1;
|
||||
if (%nextIndex < 0)
|
||||
{
|
||||
// Wrap around to the end
|
||||
%nextIndex = %this.totalCycledWeapons - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
%nextIndex = %currentIndex + 1;
|
||||
if (%nextIndex >= %this.totalCycledWeapons)
|
||||
{
|
||||
// Wrap back to the beginning
|
||||
%nextIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We now need to check if the next index is a valid weapon. If not,
|
||||
// then continue to cycle to the next weapon, in the appropriate direction,
|
||||
// until one is found. If nothing is found, then do nothing.
|
||||
%found = false;
|
||||
for (%i=0; %i<%this.totalCycledWeapons; %i++)
|
||||
{
|
||||
%weapon = %this.cycleWeapon[%nextIndex];
|
||||
if (%weapon !$= "" && %this.hasInventory(%weapon) && %this.hasAmmo(%weapon))
|
||||
{
|
||||
// We've found out weapon
|
||||
%found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
%nextIndex = %nextIndex + %dir;
|
||||
if (%nextIndex < 0)
|
||||
{
|
||||
%nextIndex = %this.totalCycledWeapons - 1;
|
||||
}
|
||||
else if (%nextIndex >= %this.totalCycledWeapons)
|
||||
{
|
||||
%nextIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (%found)
|
||||
{
|
||||
%this.use(%this.cycleWeapon[%nextIndex]);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue