//----------------------------------------------------------------------------- // 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($alwaysDisqualify) { messageClient(%this, 'MsgItemPickup', '\c2Easter Eggs are disabled when you use the editor...'); return; } 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; }