KevinBlast/marble/server/scripts/game.cs

642 lines
17 KiB
C#

//-----------------------------------------------------------------------------
// Torque Game Engine
//
// Copyright (c) 2001 GarageGames.Com
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Penalty and bonus times.
$Game::TimeTravelBonus = 5000;
// Item respawn values, only powerups currently respawn
$Item::RespawnTime = 7 * 1000;
$Item::PopTime = 10 * 1000;
// Game duration in secs, no limit if the duration is set to 0
$Game::Duration = 0;
// Pause while looking over the end game screen (in secs)
$Game::EndGamePause = 5;
//-----------------------------------------------------------------------------
// Variables extracted from the mission
$Game::GemCount = 0;
$Game::StartPad = 0;
$Game::EndPad = 0;
//-----------------------------------------------------------------------------
// Functions that implement game-play
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
function onServerCreated()
{
// Server::GameType is sent to the master server.
// This variable should uniquely identify your game and/or mod.
$Server::GameType = "Marble Game";
// 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;
// Load up all datablocks, objects etc. This function is called when
// a server is constructed.
exec("./audioProfiles.cs");
exec("./camera.cs");
exec("./markers.cs");
exec("./triggers.cs");
exec("./inventory.cs");
exec("./shapeBase.cs");
exec("./staticShape.cs");
exec("./item.cs");
// Basic items
exec("./marble.cs");
exec("./gems.cs");
exec("./powerUps.cs");
exec("./buttons.cs");
exec("./hazards.cs");
exec("./pads.cs");
exec("./bumpers.cs");
exec("./signs.cs");
exec("./fireworks.cs");
//modded items
exec("./easterEgg.cs");
// Platforms and interior doors
exec("./pathedInteriors.cs");
// Keep track of when the game started
$Game::StartTime = $Sim::Time;
}
function onServerDestroyed()
{
// Perform any game cleanup without actually ending the game
destroyGame();
}
//-----------------------------------------------------------------------------
function onMissionLoaded()
{
// Called by loadMission() once the mission is finished loading.
// Nothing special for now, just start up the game play.
$Game::GemCount = countGems(MissionGroup);
setGravityDir("1 0 0 0 -1 0 0 0 -1",true);
// Start the game here if multiplayer...
if ($Server::ServerType $= "MultiPlayer")
startGame();
%shape = $pref::marbleshape;
//checking for null....
if(%shape $= "")
%shape = "ball-superball";
defaultmarble.shapeFile = "marble/data/shapes/balls/" @ %shape @ ".dts";
}
function onMissionEnded()
{
// Called by endMission(), right before the mission is destroyed
// This part of a normal mission cycling or end.
endGame();
}
function onMissionReset()
{
setGravityDir("1 0 0 0 -1 0 0 0 -1",true);
endFireWorks();
// Reset the players and inform them we're starting
for( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ ) {
%cl = ClientGroup.getObject( %clientIndex );
commandToClient(%cl, 'GameStart');
%cl.resetStats();
}
// Start the game duration timer
if ($Game::Duration)
$Game::CycleSchedule = schedule($Game::Duration * 1000, 0, "onGameDurationEnd" );
$Game::Running = true;
ServerGroup.onMissionReset();
// Set the initial state
setGameState("Start");
}
function SimGroup::onMissionReset(%this)
{
for(%i = 0; %i < %this.getCount(); %i++)
%this.getObject(%i).onMissionReset();
}
function SimObject::onMissionReset(%this)
{
}
function GameBase::onMissionReset(%this)
{
%this.getDataBlock().onMissionReset(%this);
}
//-----------------------------------------------------------------------------
function startGame()
{
if ($Game::Running) {
error("startGame: End the game first!");
return;
}
$Game::Running = true;
$Game::Qualified = false;
onMissionReset();
}
function endGame()
{
if (!$Game::Running) {
error("endGame: No game running!");
return;
}
destroyGame();
// Inform the clients the game is over
for (%index = 0; %index < ClientGroup.getCount(); %index++) {
%client = ClientGroup.getObject(%index);
commandToClient(%client, 'GameEnd');
}
// Single player... grab the playgui as the elapsed time and
// roll in clients penalty and bonus
PlayGUI.stopTimer();
$Game::ScoreTime = PlayGui.elapsedTime;
$Game::ElapsedTime = PlayGui.elapsedTime - %client.penaltyTime + PlayGui.totalBonus;
$Game::PenaltyTime = %client.penaltyTime;
$Game::BonusTime = PlayGui.totalBonus;
// Not all missions have time qualifiers
$Game::Qualified = MissionInfo.time? $Game::ScoreTime < MissionInfo.time: true;
// Bump up the max level
if (!$playingDemo && $Game::Qualified && (MissionInfo.level + 1) > $Pref::QualifiedLevel[MissionInfo.type])
$Pref::QualifiedLevel[MissionInfo.type] = MissionInfo.level + 1;
}
function pauseGame()
{
if ($Server::ServerType $= "SinglePlayer")
$gamePaused = true;
}
function resumeGame()
{
$gamePaused = false;
}
function destroyGame()
{
// Cancel any client timers
for (%index = 0; %index < ClientGroup.getCount(); %index++)
cancel(ClientGroup.getObject(%index).respawnSchedule);
// Perform cleanup to reset the game.
cancel($Game::CycleSchedule);
cancel($Game::StateSchedule);
$Game::Running = false;
}
//-----------------------------------------------------------------------------
function setGameState(%state)
{
cancel($Game::StateSchedule);
$Game::State = %state;
eval("State::"@%state@"();");
echo("State "@%state@"");
}
function State::start()
{
PlayGui.resetTimer();
PlayGui.setMessage("");
PlayGui.setGemCount(0);
PlayGui.setMaxGems($Game::GemCount);
$Game::StateSchedule = schedule( 500, 0, "setGameState", "Ready");
if(MissionInfo.startHelpText !$= "")
{
addHelpLine(MissionInfo.startHelpText, false);
}
}
function State::ready()
{
serverPlay2d(ReadyVoiceSfx);
PlayGui.setMessage("ready");
$Game::StateSchedule = schedule( 1500, 0, "setGameState", "set");
}
function State::set()
{
serverPlay2d(SetVoiceSfx);
PlayGui.setMessage("set");
$Game::StateSchedule = schedule( 1500, 0, "setGameState", "Go");
}
function State::go()
{
serverPlay2d(GetRollingVoiceSfx);
PlayGui.setMessage("go");
PlayGui.startTimer();
$Game::StateSchedule = schedule( 2000, 0, "setGameState", "Play");
// Target the players to the end pad and let them lose
for( %index = 0; %index < ClientGroup.getCount(); %index++ ) {
%player = ClientGroup.getObject(%index).player;
%player.setPad($Game::EndPad);
%player.setMode(Normal);
}
}
function State::play()
{
// Normaly play mode
PlayGui.setMessage("");
}
function State::end()
{
// Do score calculations, messages to winner, losers, etc.
PlayGUI.stopTimer();
serverplay2d(WonRaceSfx);
startFireWorks(EndPoint);
$Game::StateSchedule = schedule( 2000, 0, "endGame");
}
//-----------------------------------------------------------------------------
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 cycleGame()
{
// This is setup as a schedule so that this function can be called
// directly from object callbacks. Object callbacks have to be
// carefull about invoking server functions that could cause
// their object to be deleted.
if (!$Game::Cycling) {
$Game::Cycling = true;
$Game::CycleSchedule = 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::CycleSchedule = schedule($Game::EndGamePause * 1000, 0, "onCyclePauseEnd");
}
function onCyclePauseEnd()
{
$Game::Cycling = false;
loadNextMission();
}
function loadNextMission()
{
%nextMission = "";
// Cycle to the next level, or back to the start if there aren't
// any more levels.
for (%file = findFirstFile($Server::MissionFileSpec);
%file !$= ""; %file = findNextFile($Server::MissionFileSpec))
if (strStr(%file, "CVS/") == -1 && strStr(%file, "common/") == -1)
{
%mission = getMissionObject(%file);
if (%mission.type $= MissionInfo.type) {
if (%mission.level == 1)
%nextMission = %file;
if ((%mission.level + 0) == MissionInfo.level + 1) {
echo("Found one!");
%nextMission = %file;
break;
}
}
}
loadMission(%nextMission);
}
//-----------------------------------------------------------------------------
// GameConnection Methods
// These methods are extensions to the GameConnection class. Extending
// GameConnection make is easier to deal with some of this functionality,
// but these could also be implemented as stand-alone functions.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
function GameConnection::incPenaltyTime(%this,%dt)
{
PlayGui.adjustTimer(%dt);
%this.penaltyTime += %dt;
}
function GameConnection::incBonusTime(%this,%dt)
{
PlayGui.addBonusTime(%dt);
%this.bonusTime += %dt;
}
//-----------------------------------------------------------------------------
function GameConnection::onClientEnterGame(%this)
{
// Create a new camera object.
%this.camera = new Camera() {
dataBlock = Observer;
};
MissionCleanup.add( %this.camera );
%this.camera.scopeToClient(%this);
// Setup game parameters and create the player
%this.resetStats();
%this.spawnPlayer();
// Anchor the player to the start pad
%this.player.setMode(Start);
// Start the game here for single player
if ($Server::ServerType $= "SinglePlayer")
startGame();
}
function GameConnection::onClientLeaveGame(%this)
{
if (isObject(%this.camera))
%this.camera.delete();
if (isObject(%this.player))
%this.player.delete();
}
function GameConnection::resetStats(%this)
{
// Reset game stats
%this.bonusTime = 0;
%this.penaltyTime = 0;
%this.gemCount = 0;
// Reset the checkpoint
if (isObject(%this.checkPoint))
%this.checkPoint.delete();
%this.checkPoint = new ScriptObject() {
pad = $Game::StartPad;
time = 0;
gemCount = 0;
penaltyTime = 0;
bonusTime = 0;
powerUp = 0;
};
}
//-----------------------------------------------------------------------------
function GameConnection::onEnterPad(%this)
{
if (%this.player.getPad() == $Game::EndPad) {
if ($Game::GemCount && %this.gemCount < $Game::GemCount) {
%this.play2d(MissingGemsSfx);
messageClient(%this, 'MsgMissingGems', '\c0You can\'t finish without all the gems!!');
}
else {
%this.player.setMode(Victory);
messageClient(%this, 'MsgRaceOver', '\c0Congratulations! You\'ve finished!');
setGameState("End");
}
}
}
function GameConnection::onLeavePad(%this)
{
// Don't care if the leave
}
//-----------------------------------------------------------------------------
function GameConnection::onOutOfBounds(%this)
{
if ($Game::State $= "End")
return;
// Reset the player back to the last checkpoint
PlayGui.setMessage("outOfBounds",2000);
%this.play2d(OutOfBoundsVoiceSfx);
%this.player.setOOB(true);
if(!isEventPending(%this.respawnSchedule))
%this.respawnSchedule = %this.schedule(2500, respawnPlayer);
}
function Marble::onOOBClick(%this)
{
if($Game::State $= "Play")
ClientGroup.getObject(0).respawnPlayer();
}
function GameConnection::onDestroyed(%this)
{
if ($Game::State $= "End")
return;
// Reset the player back to the last checkpoint
PlayGui.setMessage("destroyed",2000);
%client.play2d(DestroyedVoiceSfx);
%this.player.setOOB(true);
if(!isEventPending(%this.respawnSchedule))
%this.respawnSchedule = %this.schedule(2500, respawnPlayer);
}
function GameConnection::onFoundGem(%this,%amount)
{
%this.gemCount += %amount;
%remaining = $Game::gemCount - %this.gemCount;
if (%remaining <= 0) {
messageClient(%this, 'MsgHaveAllGems', '\c0You have all the gems, head for the finish!');
%this.play2d(GotAllGemsSfx);
%this.gemCount = $Game::GemCount;
}
else
{
if(%remaining == 1)
%msg = '\c0You picked up a gem. Only one gem to go!';
else
%msg = '\c0You picked up a gem. %1 gems to go!';
messageClient(%this, 'MsgItemPickup', %msg, %remaining);
%this.play2d(GotGemSfx);
}
PlayGui.setGemCount(%this.gemCount);
}
function GameConnection::onFoundEasterEgg(%this, %amount)
{
if($pref::easterEggCollected[$Server::MissionFile] == 1) {
messageClient(%this, 'MsgItemPickup', '\c0You already have this Easter Egg.');
%this.play2d(GotThatEggAlready);
}
else
{
$pref::easterEggCollected[$Server::MissionFile] = 1;
messageClient(%this, 'MsgItemPickup', '\c0You got the Easter Egg!');
%this.play2d(GotEggSfx);
}
}
//-----------------------------------------------------------------------------
function GameConnection::spawnPlayer(%this)
{
// Combination create player and drop him somewhere
%spawnPoint = %this.getCheckpointPos();
%this.createPlayer(%spawnPoint);
serverPlay2d(spawnSfx);
}
function restartLevel()
{
LocalClientConnection.respawnPlayer();
}
function GameConnection::respawnPlayer(%this)
{
// Reset the player back to the last checkpoint
cancel(%this.respawnSchedule);
onMissionReset();
%this.player.setOOB(false);
%this.player.setMode(Start);
%this.player.setPosition(%this.getCheckpointPos(), 0.45);
%this.player.setPowerUp(%this.checkPoint.powerUp,true);
%this.gemCount = %this.checkPoint.gemCount;
%this.penaltyTime = %this.checkPoint.penaltyTime;
%this.bonusTime = %this.checkPoint.bonusTime;
PlayGUI.setTime(%this.checkPoint.time);
PlayGui.setGemCount(%this.gemCount);
serverPlay2d(spawnSfx);
}
//-----------------------------------------------------------------------------
function GameConnection::createPlayer(%this, %spawnPoint)
{
if (%this.player > 0) {
// The client should not have a player currently
// assigned. Assigning a new one could result in
// a player ghost.
error( "Attempting to create an angus ghost!" );
}
%player = new Marble() {
dataBlock = DefaultMarble;
client = %this;
};
MissionCleanup.add(%player);
// Player setup...
%player.setPosition(%spawnPoint, 0.45);
%player.setEnergyLevel(60);
%player.setShapeName(%this.name);
%player.client.status = 1;
// Update the camera to start with the player
%this.camera.setTransform(%player.getEyeTransform());
// Give the client control of the player
%this.player = %player;
%this.setControlObject(%player);
}
//-----------------------------------------------------------------------------
function GameConnection::setCheckpoint(%this,%object)
{
// Store the last checkpoint which will be used to restore
// the player when he goes out of bounds.
if (%object != %this.checkPoint.pad) {
%this.checkPoint.delete();
%this.checkPoint = new ScriptObject() {
pad = %object;
time = PlayGUI.elapsedTime;
gemCount = %this.gemCount;
penaltyTime = %this.penaltyTime;
bonusTime = %this.bonusTime;
powerUp = %this.player.getPowerUp();
};
messageClient(%this, 'MsgCheckPoint', "\c0Check Point " @ %object.number @ " reached!");
}
}
function GameConnection::getCheckpointPos(%this,%num)
{
// Return the point a little above the object's center
if (!isObject(%this.checkPoint.pad))
return "0 0 300 1 0 0 0";
return vectorAdd(%this.checkPoint.pad.getTransform(),"0 0 3") SPC
getWords(%this.checkPoint.pad.getTransform(), 3);
}
//-----------------------------------------------------------------------------
// Support functions
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
function countGems(%group)
{
// Count up all gems out there are in the world
%gems = 0;
for (%i = 0; %i < %group.getCount(); %i++)
{
%object = %group.getObject(%i);
%type = %object.getClassName();
if (%type $= "SimGroup")
%gems += countGems(%object);
else
if (%type $= "Item" &&
%object.getDatablock().classname $= "Gem")
%gems++;
}
return %gems;
}