Game Development Community

dev|Pro Game Development Curriculum

Save/Load game system

by Skylar Kelty · 11/13/2006 (4:36 pm) · 11 comments

This is due to be re-done so that it isnt a checkpoint system and uses player.gettransforms to save the player position.

This version works, but only just.

It saves:
-> Mission name
-> Player Name
-> Checkpoint

It needs my UAC resource, if you don't want that im sure you can pull it out

It can be expanded by those who are experienced scriptors, I will also expand it later.

Script, client-side, lets say client/scripts/sgsys.cs (remember to exec):
function SGSYS::save(%this)
{
   if(!checkloggedin()){
      canvas.popdialog(savedlg);
      return;
   }
   
   %name = $savegamename;
   %username = $loggedinusername;
	%missionname =  $currentmissionname;
	%playername =  $pref::Player::Name;
	%checkpoint =  cp.getcurrent();
	
	%file = new FileObject();
	%file.openforwrite("~/data/users/" @ %username @ "/savedgames/" @ %name @ ".xml");
	%file.writeLine("Savedgame data for " @ %username);
	%file.writeLine("");
	%file.writeLine("<name>");
	%file.writeLine(%name);
	%file.writeLine("</name>");
	%file.writeLine("");
	%file.writeLine("<missionname>");
	%file.writeLine(%missionname);
	%file.writeLine("</missionname>");
	%file.writeLine("");
	%file.writeLine("<playername>");
	%file.writeLine(%playername);
	%file.writeLine("</playername>");
	%file.writeLine("");
	%file.writeLine("<checkpoint>");
	%file.writeLine(%checkpoint);
	%file.writeLine("</checkpoint>");
	%file.close();
	%file.delete();
	
	$savegamename = "";
	Canvas.popdialog(savedlg);
}

function SGSYS::load(%this)
{
   if(!checkloggedin()){
      canvas.popdialog(savedlg);
      return;
   }

   %name = $loadgamename;
   %username = $loggedinusername;

	%file = new FileObject();
	%file.openforRead("~/data/users/" @ %username @ "/savedgames/" @ %name @ ".xml");
	 while( !%file.isEOF() )
	 {
		 %line = %file.readline();
		 %sgsysresults = %sgsysresults @ %line;
	 }
	%file.close();
	%file.delete();

	%missionname = getxmldata(%sgsysresults, "missionname", 0);
	%playername = getxmldata(%sgsysresults, "playername", 0);
	%checkpoint = getxmldata(%sgsysresults, "checkpoint", 0);

        Canvas.popdialog(loaddlg);

	%this.startsavedgame(%name, %missionname, %playername, %checkpoint);
	
	$loadgamename = "";

}

function SGSYS::startsavedgame(%this, %name, %missionname, %playername, %checkpoint)
{
       if($Game::Running == true)
           disconnect();
   
        $checkpointtoload = %checkpoint;
   
	Canvas.setContent(LoadingGui);
	LOAD_MapName.setText( "Loading SavedGame" );
	LOAD_MapDescription.setText( "<font:Arial:16>Please wait while " @ %name @ " is loaded");
	Canvas.repaint();

	createServer("SinglePlayer", %missionname);
	%conn = new GameConnection(ServerConnection);
	RootGroup.add(ServerConnection);
	%conn.setConnectArgs(%playername);
	%conn.setJoinPassword($Client::Password);
	%conn.connectLocal();
}

Checkpoint stuff, also client side, lets say client/scripts/cp.cs (remember; exec!):
function cp::getcurrent(%this)
{
   return $lastcheckpoint;
}

function cp::setcurrent(%this, %name)
{
   $lastcheckpoint = %name;
}

triggers, server-side, triggers.cs:
datablock TriggerData(CheckpointTrigger)
{
   tickPeriodMS = 100;
};

function CheckpointTrigger::onEnterTrigger(%this,%trigger,%obj)
{
   //%this.getName
   %name = %trigger.getname();
   
   // Bad, should be a commandtoclient
   cp.setcurrent(%name);

   Parent::onEnterTrigger(%this,%trigger,%obj);
}

function CheckpointTrigger::onLeaveTrigger(%this,%trigger,%obj)
{
   Parent::onLeaveTrigger(%this,%trigger,%obj);
}

function CheckpointTrigger::onTickTrigger(%this,%trigger)
{
   Parent::onTickTrigger(%this,%trigger);
}

server-side game.cs changes:

add to the end of the file:
function getCheckPoint() 
{
   // No if's beacuse we assume everything is okay,
   // it saved okay so it must still be fine 
   %checkpoint = $checkpointtoload - 1;
   %groupName = "MissionGroup/CheckPoints";
   %group = nameToID(%groupName);
   %index = %checkpoint;
   %spawn = %group.getObject(%index);

   return %spawn.getTransform();
}

Change function GameConnection::spawnPlayer(%this) to this:
function GameConnection::spawnPlayer(%this)
{
   // Combination create player and drop him somewhere
   if($issavedgame == "1"){
      %spawnPoint = getCheckPoint();
   } else {
      %spawnPoint = pickSpawnPoint();
   }

   %this.createPlayer(%spawnPoint);
}

Gui, client-side, say client/ui/sgsys.gui (exec!):
//--- OBJECT WRITE BEGIN ---
new GuiControl(savedlg) {
   Profile = "GuiDefaultProfile";
   HorizSizing = "right";
   VertSizing = "bottom";
   Position = "0 0";
   Extent = "640 480";
   MinExtent = "8 8";
   Visible = "1";
      helpTag = "0";

   new GuiWindowCtrl() {
      Profile = "GuiWindowProfile";
      HorizSizing = "right";
      VertSizing = "bottom";
      Position = "178 104";
      Extent = "343 238";
      MinExtent = "8 2";
      Visible = "1";
      text = "Save Game";
      maxLength = "255";
      resizeWidth = "1";
      resizeHeight = "1";
      canMove = "1";
      canClose = "1";
      canMinimize = "1";
      canMaximize = "1";
      minSize = "50 50";

      new GuiMLTextCtrl() {
         Profile = "GuiMLTextProfile";
         HorizSizing = "right";
         VertSizing = "bottom";
         Position = "149 63";
         Extent = "64 14";
         MinExtent = "8 2";
         Visible = "1";
         lineSpacing = "2";
         allowColorChars = "0";
         maxChars = "-1";
         text = "Name:";
      };
      new GuiTextEditCtrl() {
         Profile = "GuiTextEditProfile";
         HorizSizing = "right";
         VertSizing = "bottom";
         Position = "113 83";
         Extent = "119 18";
         MinExtent = "8 2";
         Visible = "1";
         Variable = "$savegamename";
         maxLength = "255";
         historySize = "0";
         password = "0";
         tabComplete = "0";
         sinkAllKeyEvents = "0";
         password = "0";
         passwordMask = "*";
      };
      new GuiButtonCtrl() {
         Profile = "GuiButtonProfile";
         HorizSizing = "right";
         VertSizing = "bottom";
         Position = "103 154";
         Extent = "140 30";
         MinExtent = "8 2";
         Visible = "1";
         text = "Save";
         groupNum = "-1";
         buttonType = "PushButton";
         command = "SGSYS.save();";
      };
   };
};

new GuiControl(loaddlg) {
   Profile = "GuiDefaultProfile";
   HorizSizing = "right";
   VertSizing = "bottom";
   Position = "0 0";
   Extent = "640 480";
   MinExtent = "8 8";
   Visible = "1";
      helpTag = "0";

   new GuiWindowCtrl() {
      Profile = "GuiWindowProfile";
      HorizSizing = "right";
      VertSizing = "bottom";
      Position = "178 104";
      Extent = "343 238";
      MinExtent = "8 2";
      Visible = "1";
      text = "Load Game";
      maxLength = "255";
      resizeWidth = "1";
      resizeHeight = "1";
      canMove = "1";
      canClose = "1";
      canMinimize = "1";
      canMaximize = "1";
      minSize = "50 50";

      new GuiMLTextCtrl() {
         Profile = "GuiMLTextProfile";
         HorizSizing = "right";
         VertSizing = "bottom";
         Position = "149 63";
         Extent = "64 14";
         MinExtent = "8 2";
         Visible = "1";
         lineSpacing = "2";
         allowColorChars = "0";
         maxChars = "-1";
         text = "Name:";
      };
      new GuiTextEditCtrl() {
         Profile = "GuiTextEditProfile";
         HorizSizing = "right";
         VertSizing = "bottom";
         Position = "113 83";
         Extent = "119 18";
         MinExtent = "8 2";
         Visible = "1";
         Variable = "$loadgamename";
         maxLength = "255";
         historySize = "0";
         password = "0";
         tabComplete = "0";
         sinkAllKeyEvents = "0";
         password = "0";
         passwordMask = "*";
      };
      new GuiButtonCtrl() {
         Profile = "GuiButtonProfile";
         HorizSizing = "right";
         VertSizing = "bottom";
         Position = "103 154";
         Extent = "140 30";
         MinExtent = "8 2";
         Visible = "1";
         text = "Save";
         groupNum = "-1";
         buttonType = "PushButton";
         command = "SGSYS.load();";
      };
   };
};
//--- OBJECT WRITE END ---

getxmldata, client side, anywhere really say client/scripts/anywhere.cs (exec?):
function getxmldata(%string, %tag, %startChar)
{
	%startTag = "<" @ %tag @ ">";
	%endTag   = "</" @ %tag @ ">";
	%startTagOffset = strpos(%string, %startTag, %startChar);
	%startOffset = %startTagOffset + strlen(%startTag);
	%endTagOffset = strpos(%string, %endTag, %startOffset - 1);

	if(%endTagOffset < 0)
    		return "";

	%this.lastOffset = %endTagOffset;
	%result = getSubStr(%string, %startOffset, %endTagOffset - %startOffset);
	%result = strreplace(%result, "\"", "\"");
	%result = strreplace(%result, "&",  "&");
	return %result;
}

Engine stuff:

game/sgsys.cpp:
// Includes
#include "game/sgsys.h"
#include "console/console.h"

F32 SGSYS::currentversion = 1.1;

IMPLEMENT_CO_DATABLOCK_V1(SGSYS);
IMPLEMENT_CONSOLETYPE(SGSYS)
IMPLEMENT_GETDATATYPE(SGSYS)
IMPLEMENT_SETDATATYPE(SGSYS)

SGSYS::SGSYS()
{

}

void SGSYS::consoleInit()
{
	Con::addVariable("$pref::SGSYS::currentversion", TypeF32, &currentversion);
}

sgsys.h:
#include "game/gameBase.h"

class SGSYS : public GameBaseData 
{
   typedef GameBaseData Parent;

  public:

   SGSYS();
   DECLARE_CONOBJECT(SGSYS);

   static void consoleInit();
   static F32 currentversion;
};

DECLARE_CONSOLETYPE(SGSYS)

game/cp.cpp:
// Includes
#include "game/cp.h"
#include "console/console.h"

IMPLEMENT_CO_DATABLOCK_V1(cp);
IMPLEMENT_CONSOLETYPE(cp)
IMPLEMENT_GETDATATYPE(cp)
IMPLEMENT_SETDATATYPE(cp)

cp::cp()
{

}

game/cp.h:
#include "game/gameBase.h"

class cp : public GameBaseData 
{
   typedef GameBaseData Parent;

  public:

   cp();
   DECLARE_CONOBJECT(cp);
};

DECLARE_CONSOLETYPE(cp)

recompile!

You need a checkpoints simgroup in the mission, with checkpoints as spawnspheres and separate checkpoint triggers like this:

new SimGroup(CheckPoints) {

      new SpawnSphere(1) {
         Position = "34.1473 -152.619 108.67";
         rotation = "0 0 1 130.062";
         scale = "0.940827 1.97505 1";
         dataBlock = "SpawnSphereMarker";
         Radius = "10";
         sphereWeight = "1";
         indoorWeight = "1";
         outdoorWeight = "1";
            locked = "False";
            homingCount = "0";
            lockCount = "0";
      };
      new SpawnSphere(2) {
         Position = "42.7715 -225.175 165.694";
         rotation = "0 0 1 130.062";
         scale = "0.940827 1.97505 1";
         dataBlock = "SpawnSphereMarker";
         Radius = "10";
         sphereWeight = "1";
         indoorWeight = "1";
         outdoorWeight = "1";
            locked = "False";
            homingCount = "0";
            lockCount = "0";
      };
   };
   new Trigger(1) {
      Position = "23.0141 -134.828 102.986";
      rotation = "1 0 0 0";
      scale = "1 1 1";
      dataBlock = "CheckpointTrigger";
      polyhedron = "0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000";
   };
   new Trigger(2) {
      Position = "23.0141 -134.828 102.986";
      rotation = "1 0 0 0";
      scale = "1 1 1";
      dataBlock = "CheckpointTrigger";
      polyhedron = "0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000";
   };

#1
11/14/2006 (8:07 am)
good stuff. i've been looking for a res like this.
#2
11/30/2006 (5:55 pm)
Hi James,

Just got the UAC and Save\Load in but I have console error:

Compiling starter.fps/client/scripts/anywhere.cs...
starter.fps/client/scripts/anywhere.cs Line: 14 - Syntax error.


This is line 14:
%result = strreplace(%result, """, "\"");

Still working on it, could be something I've done wrong somewhere else. I'll post if I find my mistake elsewhere.

Edited because what I'd written didn't make sense. Not sure if this helps...
#3
12/01/2006 (7:39 am)
Try:

%result = strreplace(%result, "\"", "\"");
#4
05/23/2007 (12:32 am)
Updated One bug fix
#5
07/29/2007 (10:17 am)
Heyas,
So I was wondering if anyone has adapted this to the RTS Kit at all. If anyone can PM me what they did or provide some instruction on the RTS Resource forum I would appreciate it.

Thanks
Nicholas
#6
01/12/2008 (10:30 pm)
Cool! :-)
#7
07/27/2008 (11:13 am)
Hey has this been updated yet with the player.gettransform ?
#8
07/27/2008 (11:46 am)
Nope, havnt looked at this for a while.
Will be soon though for an rpg.
#9
08/09/2008 (4:30 pm)
cool
#10
01/05/2009 (10:20 am)
has anyone converted this to SQlite or integrated this with the UAC tutorial?

www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=11552
#11
03/02/2009 (11:34 pm)
Cool!..I can I can use this with my special project at school..tnx 4 this wonderful stuff.