Replacement game.cs for Torque3D (Full Template)
by Michael Hall · 08/21/2010 (8:54 am) · 8 comments
There are a few issues with the gametype code due to it not being maintained. There are several places where additional overrides are now needed or alternatively some of the core scripts should be changed to account for gametypes instead of relying on packages. It's my opinion that this will be easy to fix but difficult to keep maintained until Torque 3D has a stable release version and/or the gametype code is more closely integrated.
Personally I feel that this module should never have been included with the Template, given that the team code and TDM & CTF gametypes were stripped out, and is better left up to you the developer to implement depending on project - especially since in it's original incarnation it was intended for the old FPS Genre Kit and not a general purpose Template. Futhermore there are no actual mission examples that make use of this code, so in an effort to "clean" things up I removed it from my Template.
Simple replace the entirety of game.cs with the following code. After doing so you can safely remove the gameCore.cs and gameDM.cs script files after removing the exec lines for them in scriptExec.cs. We will also be making a small change to camera.cs. All files are found in the scripts/server directory.
game.cs
camera.cs
Modify function Observer::onTrigger so we can properly respawn after death
Change the "Corpse" case of the switch$ statement
OUTSTANDING ISSUE:The current implementation of the playerList.gui is broken with the removal of the gameCore package. It was responsible for overriding several "core" script methods, one of which was GameConnection::onConnect() in order to initialize, reset, and pass some client scoring variables to the scoreHUD. Compare the overridden methods in the GameCore package with those in the Core package if you wish to restore this. Perhaps it would be better to implement a custom solution or revert back to the simple playerList.gui of TGE/TGEa - I'll let user feedback guide whether or not I update this Resource to "fix" this -- leaving room for a future Resource, hint hint for anyone who wants to do this ;)
Eventually I may even extend this Resource to remove several other unused/unnecessary features for a simpler more cleaner Template -- perhaps even a replacement Template.
Personally I feel that this module should never have been included with the Template, given that the team code and TDM & CTF gametypes were stripped out, and is better left up to you the developer to implement depending on project - especially since in it's original incarnation it was intended for the old FPS Genre Kit and not a general purpose Template. Futhermore there are no actual mission examples that make use of this code, so in an effort to "clean" things up I removed it from my Template.
Simple replace the entirety of game.cs with the following code. After doing so you can safely remove the gameCore.cs and gameDM.cs script files after removing the exec lines for them in scriptExec.cs. We will also be making a small change to camera.cs. All files are found in the scripts/server directory.
game.cs
//-----------------------------------------------------------------------------
// Torque 3d
// Copyright (C) InstantAction, Inc.
//-----------------------------------------------------------------------------
// Game duration in secs, no limit if the duration is set to 0
$Game::Duration = 20 * 60;
// When a client score reaches this value, the game is ended.
$Game::EndGameScore = 30;
// Pause while looking over the end game screen (in secs)
$Game::EndGamePause = 10;
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
$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.
//-----------------------------------------------------------------------------
$Game::DefaultCameraClass = "Camera";
$Game::DefaultCameraDataBlock = "Observer";
$Game::DefaultCameraSpawnGroups = "CameraSpawnPoints PlayerSpawnPoints PlayerDropPoints";
// ============================================================================
// Server, mission, and game management
// ============================================================================
function onServerCreated()
{
// The server has started so do some game setup
// Server::GameType is sent to the master server.
// This variable should uniquely identify your game and/or mod.
$Server::GameType = $appName;
// Server::MissionType sent to the master server. Clients can
// filter servers based on mission type.
$Server::MissionType = "Deathmatch";
// GameStartTime is the sim time the game started. Used to calculated
// game elapsed time.
$Game::StartTime = 0;
// Create the server physics world.
physicsInitWorld("server");
// Load up any objects or datablocks saved to the editor managed scripts
if (isScriptFile("art/shapes/particles/managedParticleData.cs"))
exec("art/shapes/particles/managedParticleData.cs");
if (isScriptFile("art/decals/managedDecalData.cs"))
exec("art/decals/managedDecalData.cs");
if (isScriptFile("art/datablocks/managedDatablocks.cs"))
exec("art/datablocks/managedDatablocks.cs");
if (isScriptFile("art/forest/managedItemData.cs"))
exec("art/forest/managedItemData.cs");
// Load up user specified data and object declarations
exec("art/datablocks/datablockExec.cs");
// Run the other gameplay scripts in this folder
exec("./scriptExec.cs");
// Keep track of when the game started
$Game::StartTime = $Sim::Time;
}
function onServerDestroyed()
{
// This function is called as part of a server shutdown
// Destroy the server physcis world
physicsDestroyWorld("server");
}
// ----------------------------------------------------------------------------
function onMissionLoaded()
{
// Called by loadMission() once the mission is finished loading
// Start the server side physics simulation
physicsStartSimulation("server");
// Nothing special for now, just start up the game play.
startGame();
}
function onMissionEnded()
{
// Called by endMission(), right before the mission is destroyed
// Normally the game should be ended first before the next mission is loaded,
// this is here in case loadMission has been called directly. The mission
// will be ended if the server is destroyed, so we only need to cleanup here.
// Stop the server physics simulation
physicsStopSimulation("server");
cancel($Game::Schedule);
$Game::Running = false;
$Game::Cycling = false;
}
function onGameDurationEnd()
{
// 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 && !isObject(EditorGui))
cycleGame();
}
// ----------------------------------------------------------------------------
function startGame()
{
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;
}
// Start the game timer
if ($Game::Duration)
$Game::Schedule = schedule($Game::Duration * 1000, 0, "onGameDurationEnd");
$Game::Running = true;
// Start the AIManager
new ScriptObject(AIManager) {};
MissionCleanup.add(AIManager);
AIManager.think();
}
function endGame()
{
if (!$Game::Running)
{
error("endGame: No game running!");
return;
}
// Stop the AIManager
AIManager.delete();
// Stop any game timers
cancel($Game::Schedule);
// Inform the client the game is over
for(%clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++)
{
%cl = ClientGroup.getObject(%clientIndex);
commandToClient(%cl, 'GameEnd');
}
// Delete all the temporary mission objects
resetMission();
$Game::Running = false;
}
// ----------------------------------------------------------------------------
function cycleGame()
{
// This is setup as a schedule so that this function can be called directly
// from object callbacks. Object callbacks have to be careful about
// invoking server functions that could cause their object to be deleted.
if (!$Game::Cycling)
{
$Game::Cycling = true;
$Game::Schedule = schedule(0, 0, "onCycleExec");
}
}
function onCycleExec()
{
// End the current game and start another one, we'll pause for a little
// so the end game victory screen can be examined by the clients.
endGame();
$Game::Schedule = schedule($Game::EndGamePause * 1000, 0, "onCyclePauseEnd");
}
function onCyclePauseEnd()
{
$Game::Cycling = false;
// Just cycle through the missions for now.
%search = $Server::MissionFileSpec;
for (%file = findFirstFile(%search); %file !$= ""; %file = findNextFile(%search))
{
if (%file $= $Server::MissionFile)
{
// Get the next one, back to the first if there is no next.
%file = findNextFile(%search);
if (%file $= "")
%file = findFirstFile(%search);
break;
}
}
loadMission(%file);
}
// ============================================================================
// Client Management
// ============================================================================
// ----------------------------------------------------------------------------
// GameConnection Methods
// ----------------------------------------------------------------------------
// GameConnection manages the communication between the server's world and the
// client's simulation. These functions are responsible for maintaining the
// client's camera and player objects.
// These methods are extensions to the GameConnection class. Extending
// GameConnection makes it easier to deal with some of this functionality,
// but these could also be implemented as stand-alone functions.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// This is the main entry point for spawning a control object for the client.
// The control object is the actual game object that the client is responsible
// for controlling in the client and server simulations. We also spawn a
// convenient camera object for use as an alternate control object. We do not
// have to spawn this camera object in order to function in the simulation.
function GameConnection::onClientEnterGame(%this)
{
// Called for each client after it's finished downloading the mission and is
// ready to start playing.
commandToClient(%this, 'SyncClock', $Sim::Time - $Game::StartTime);
// 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
%this.spawnCamera(%cameraSpawnPoint);
// Setup game parameters, the onConnect method currently starts
// everyone with a 0 score.
%this.score = 0;
%this.kills = 0;
%this.deaths = 0;
// weaponHUD
%this.RefreshWeaponHud(0, "", "");
// Create a player object.
%this.preparePlayer();
}
function GameConnection::preparePlayer(%this)
{
// 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 player object for this client using the found %spawnPoint
%this.spawnPlayer(%playerSpawnPoint);
// Give the play some starting equipment - separated out for easier override
%this.loadOut(%this.player);
}
function GameConnection::onClientLeaveGame(%this)
{
// Cleanup the camera
if (isObject(%this.camera))
%this.camera.delete();
// Cleanup the player
if (isObject(%this.player))
%this.player.delete();
}
// ----------------------------------------------------------------------------
// Functions that implement game-play
// ----------------------------------------------------------------------------
function GameConnection::loadOut(%this, %player)
{
%player.setInventory(RocketLauncher, 1);
%player.setInventory(RocketLauncherAmmo, %player.maxInventory(RocketLauncherAmmo));
%player.mountImage(RocketLauncherImage, 0);
}
function GameConnection::onLeaveMissionArea(%this)
{
// The control objects invoke this method when they
// move out of the mission area.
messageClient(%this, 'MsgClientJoin', '\c2Now leaving the mission area!');
}
function GameConnection::onEnterMissionArea(%this)
{
// The control objects invoke this method when they
// move back into the mission area.
messageClient(%this, 'MsgClientJoin', '\c2Now entering the mission area.');
}
function GameConnection::onDeath(%this, %sourceObject, %sourceClient, %damageType, %damLoc)
{
// Handle a player's death
// clear the weaponHUD
%this.RefreshWeaponHud(0, "", "");
// Clear out the name on the corpse
if (isObject(%this.player))
{
if (%this.player.isMethod("setShapeName"))
%this.player.setShapeName("");
}
// Update the numerical Health HUD
%this.player.updateHealth(%obj);
// Switch the client over to the death cam and unhook the player object.
if (isObject(%this.camera) && isObject(%this.player))
{
%this.camera.setMode("Corpse", %this.player);
%this.setControlObject(%this.camera);
}
%this.player = 0;
// Dole out points and display an appropriate message
if (%damageType $= "Suicide" || %sourceClient == %this)
{
// TODO: current implementaion of the playerList.gui score functionality
// will not work correctly without rewriting some core scripts.
%this.incScore(-1, true);
%this.incDeaths(1, true);
messageAll('MsgClientScoreChange', "", %this.score, %this.kills, %this.deaths, %this);
messageAll('MsgClientKilled', '%1 takes his own life!', %this.playerName);
}
else
{
// TODO: current implementaion of the playerList.gui score functionality
// will not work correctly without rewriting some core scripts.
%sourceClient.incScore(1, true);
%sourceCLient.incKills(1, true);
%sourceClient.incDeaths(1, true);
messageAll('MsgClientScoreChange', "", %this.score, %this.kills, %this.deaths, %this);
messageAll('MsgClientKilled','%1 gets nailed by %2!',%this.playerName, %sourceClient.playerName);
if (%sourceClient.score >= $Game::EndGameScore)
cycleGame();
}
}
// ----------------------------------------------------------------------------
// Scoring
// TODO: current implementaion of the playerList.gui score functionality
// will not work correctly without rewriting some core scripts.
// ----------------------------------------------------------------------------
function GameConnection::incKills(%this, %kill, %dontMessageAll)
{
%this.kills += %kill;
if(!%dontMessageAll)
messageAll('MsgClientScoreChanged', "", %this.score, %this.kills, %this.deaths, %this);
}
function GameConnection::incDeaths(%this, %death, %dontMessageAll)
{
%this.deaths += %death;
if(!%dontMessageAll)
messageAll('MsgClientScoreChanged', "", %this.score, %this.kills, %this.deaths, %this);
}
function GameConnection::incScore(%this, %score, %dontMessageAll)
{
%this.score += %score;
if(!%dontMessageAll)
messageAll('MsgClientScoreChanged', "", %this.score, %this.kills, %this.deaths, %this);
}
function GameConnection::getScore(%this) { return %this.score; }
function GameConnection::getKills(%this) { return %this.kills; }
function GameConnection::getDeaths(%this) { return %this.deaths; }
// ----------------------------------------------------------------------------
// weapon HUD
// ----------------------------------------------------------------------------
function GameConnection::setAmmoAmountHud(%client, %amount)
{
commandToClient(%client, 'SetAmmoAmountHud', %amount);
}
function GameConnection::RefreshWeaponHud(%client, %amount, %preview, %ret)
{
commandToClient(%client, 'RefreshWeaponHud', %amount, %preview, %ret);
}camera.cs
Modify function Observer::onTrigger so we can properly respawn after death
Change the "Corpse" case of the switch$ statement
case "Corpse": // Viewing dead corpse, so we probably want to respawn. %client.preparePlayer();
OUTSTANDING ISSUE:The current implementation of the playerList.gui is broken with the removal of the gameCore package. It was responsible for overriding several "core" script methods, one of which was GameConnection::onConnect() in order to initialize, reset, and pass some client scoring variables to the scoreHUD. Compare the overridden methods in the GameCore package with those in the Core package if you wish to restore this. Perhaps it would be better to implement a custom solution or revert back to the simple playerList.gui of TGE/TGEa - I'll let user feedback guide whether or not I update this Resource to "fix" this -- leaving room for a future Resource, hint hint for anyone who wants to do this ;)
Eventually I may even extend this Resource to remove several other unused/unnecessary features for a simpler more cleaner Template -- perhaps even a replacement Template.
About the author
Been dabbling with game-programming since the age of 10 when I got my first computer, a Commodore. Got serious about game-development after modding Tribes for several years. Doesn't sleep much. Drinks rum. Teaches guitar. Plays cello.
#2
08/23/2010 (5:13 pm)
@Donnie: Kaboom ports easily to Torque3D, only needing filepath corrections and minor changes to a couple of particles - the damage control methods work the same across all versions of the Torque engine (TGE, TGEa, T3D). I've not made an official port but did post instructions in a forum thread somewhere when this question arose awhile back, unfortunately I never kept a link to that thread. However, I'll probably be submitting a Torque3d version soon -- this will be a pure port with no additional art or features though.
#3
08/25/2010 (1:58 am)
Cool and thanks again for a great pack....
#4
I've always thought that the GameCore implementation was rotten. It was as if someone decided to give it a try, got it half working and then just abandoned it but left it in the codebase.
Thanks for a clean way to get rid of it.
11/17/2010 (2:31 pm)
Great job.I've always thought that the GameCore implementation was rotten. It was as if someone decided to give it a try, got it half working and then just abandoned it but left it in the codebase.
Thanks for a clean way to get rid of it.
#5
11/17/2010 (3:11 pm)
It was working at one point with several different gametypes, but was intended for use in the FPS Genre Kit project. Before release, the majority of it was stripped out and the basic example only retained the infrastructure that made the gametypes possible - but offered no example. Over time, compounding changes in the core scripts now cause problems with how the package is used. I offered to change/fix this months ago and TP didn't respond, but guess it's too late now.
#6
02/04/2011 (8:05 pm)
Are there other files besides game.cs which can impact the game's duration?
#7
02/05/2011 (10:50 am)
@Maria: hmm... there shouldn't be. There are a few globals at the top of game.cs that affect game duration:// Game duration in secs, no limit if the duration is set to 0 $Game::Duration = 20 * 60; // When a client score reaches this value, the game is ended. $Game::EndGameScore = 30;That being said it is possible that one of the gametype packages is overriding those values or introducing new ones - but that's what we're removing here ;)
#8
02/05/2011 (11:32 am)
@ Michael, saw that, changed that, no impact... I need to go back and see which files I edited over the last few weeks... still learning my way around all of these files... 
Torque Owner Donnie Hutson Jr