Help with File data when restarting games (SOLVED)

Started by geork, Sat 27/08/2011 20:26:01

Previous topic - Next topic

geork

Hey all!
I've got into a little trouble with .dat files (again), and I wondered if you could help me out. I have a chapter selection system (similar to the ones in the Half Life games), and what I want to do is to RESTART the game everytime the player decides to start from a different chapter, so that I don't have to reset ALL variables, just reset some of them aftwerwards depending on the chapter selected. So I decided to write a few crucial variables into a .dat file like so:
Code: ags

//This happens when a chapter is selected to be loaded
BegunFromCh1 = true;
BegunFromCh2 = false;
BegunFromCh3 = false;
InMenu = false; //This is set to true when the game is manually exited
InGame = true; 
File *k = File.Open("NewGame.dat",eFileWrite);
k.WriteInt(InMenu);
k.WriteInt(InGame);
k.WriteInt(BegunFromCh1);
k.WriteInt(BegunFromCh2);
k.WriteInt(BegunFromCh3);
k.Close();
RestartGame();

Then, at game_start(), I have the following code:
Code: ags

File *l = File.Open("NewGame.dat",eFileRead);
  InMenu = IntToBool(l.ReadInt());
  InGame = IntToBool(l.ReadInt());
  BegunFromCh1 = IntToBool(l.ReadInt());
  BegunFromCh2 = IntToBool(l.ReadInt());
  BegunFromCh3 = IntToBool(l.ReadInt());
  l.Close();
  if(InGame == true){
    Display("Working");
    if(BegunFromCh1 == true) CH1();
    else if(BegunFromCh2 == true) CH2();
    else if(BegunFromCh3 == true) CH3();
  }
  else{ //InGame is set to false and InMenu set to true when the game is manually exited, so that the character returns 
          //To menu screen (Room9) when game is first started.
    cEgo.ChangeRoom(9);
    cEgo.Transparency = 100;
    Menu_ClickingState = true;
  }

and CH1() Looks like this:
Code: ags

function CH1(){
  Display("Working");
  mainstory = 5;
  cEgo.ChangeRoom(1, 650, 405);
  cEgo.Transparency = 0;
}

However, my problem is that InGame seems to never be true, as both times I have called 'Display("Working")', neither ever show up! And the character ALWAYS goes to Room 9. There is nothing in eEventRestoreGame that should intefer with any of this code, if that is at all relevant...
Thanks in advance :)

Khris

What does IntToBool do, I mean, could you post its contents?

geork

#2
Code: ags

bool IntToBool(int Boolean){
  bool rValue;
  if(Boolean == 0) rValue = false;
  else rValue = true;
  return rValue;
}

I think this function works properly, as I use it when loading games...

Lt. Smash

#3
Normally you shouldn't have to convert an int to a bool, because internally 'false' is always equivalent to '0' and 'true' to '1'. Except you want numbers greater than 1 to also be an equivalent of 'true' ;)

Now to your problem. You don't need 5 different bools, it's enough to use one bool and one integer in your situation.
You use an int to store the chapter (1, 2 or 3) and a bool which saves if you were inMenu or not (inGame).

So you could simplify your code like:
Code: ags

// This happens when a chapter is selected to be loaded
int startingChapter = 1; // Change this number to the number of the chapter you want to start with
bool inMenu = false; // This is set to true when the game is manually exited

File *k = File.Open("NewGame.dat",eFileWrite);
k.WriteInt(startingChapter);
k.WriteInt(inMenu);
k.Close();
RestartGame();


Code: ags

File *l = File.Open("NewGame.dat",eFileRead);
int startingChapter = l.ReadInt();
int inMenu = l.ReadInt();
l.Close();

if (inMenu) {
  CH(startingChapter);
}
else{
        // Go To menu screen (Room9) when game is first started.
  cEgo.ChangeRoom(9);
  cEgo.Transparency = 100;
  Menu_ClickingState = true;
}


And then you have to merge your chapter functions (CH1,2,3) into one:
Code: ags

function CH (int chapter){
  if (chapter == 1) {
    Display("Working");
    mainstory = 5;
    cEgo.ChangeRoom(1, 650, 405);
    cEgo.Transparency = 0;
  }
  else if (chapter == 2) {
    // Things that happen when chapter 2 is loaded...
  }
  else if (chapter == 3) {
    // Things that happen when chapter 3 is loaded... 
  }
  ...
}

Untested but should work :)

geork

Thanks for the advice :) it simplifies everything! However, I'm still stuck with the problem. I simplifies all my code to:
Code: ags

 File *k = File.Open("NewGame.dat",eFileWrite);
 if(btn == 1) startingChapter = 1;
  else if(btn == 2)startingChapter = 2;
  else if(btn == 3)startingChapter = 3;
  k.Close();
  RestartGame();


Code: ags

File *l = File.Open("NewGame.dat",eFileRead);
  startingChapter = l.ReadInt();
  l.Close();
  if(startingChapter != 0) CH(startingChapter);
  else{ //startingChapter is set to 0 when manually exited
    cEgo.ChangeRoom(9);
    cEgo.Transparency = 100;
    Menu_ClickingState = true;
  }

and the CH which you reccommended. However, cEgo always gets teleported to Room 9. Maybe I should manually change a constant in NewGame.dat? I wouldn't know what value to put though (all in code ;) )
thanks

hedgefield

In the write function you don't actually write anything to the file. It should be this:

Code: ags

File *k = File.Open("NewGame.dat",eFileWrite);
  if (btn == 1) startingChapter = 1;
  else if (btn == 2)startingChapter = 2;
  else if (btn == 3)startingChapter = 3;
  k.WriteInt(startingChapter);
k.Close();
RestartGame();

geork

#6
oops. Yeah, that command is in there, it's just there's some other junk and I cut out sections (all which don't affect this dilemna, they're just writing other files).
But I did find that when I modified
Code: ags

if(startingChapter == 0) CH(startingChapter); //THIS!
  else{ 
    cEgo.ChangeRoom(9);
    cEgo.Transparency = 100;
    Menu_ClickingState = true;
  }

that when I started the game, it at first carried out CH(0) as usual (which I quickly programmed to make cEgo go to room1), but when I started a new game AGAIN, CH(0) was DEFINATELY not executed, but neither was cEgo sent to room 9. so it basically ignored game_start(), I think...??? Should I recall game_start from the event eEventRestoreGame?
thanks

EDIT: I should probabely also mention that the character seems to be sent back to the room he was in when the new game is started, as in this case he was sent back to room1, but not in the position CH(0) (or any other CH() ) commands him to

Lt. Smash

#7
Sorry but your if-statement doesn't make any sense.

You call function CH with startingChapter  ONLY when startingChapter is 0. So it will never call any different chapter. ;)

change this to:
Code: ags

if (startingChapter != 0) CH(startingChapter);
else {
...
}
[/s]
Sorry, just saw that you changed this only for testing reasons   ::)

Btw you can even more simplify your file writing:
Code: ags

File *k = File.Open("NewGame.dat",eFileWrite);
if (btn > 0)   // I'm not quite sure if this if-else-statement is needed because I don't know what ReadInt returns if there is no Int to read...
  k.WriteInt(btn);
else
  k.WriteInt(0);
k.Close();
RestartGame();


[EDIT:] Possibly you have some code that prevents RestartGame() from working correctly. Try to put SetRestartPoint(); somewhere in game_start...

geork

Teehee, thanks for the suggestion, the code is about half the lines now and much nicer! :D
Okay, I've tested definately and game_start is NOT executed when the game is restarted...even when I set the restart point there. Is there any command which will reset data easily other than restarting the game?
thanks :)

Calin Leafshade

Theres a game event for eRestoreGame (or something like that, i cant be arsed to look it up) When you restart the game it basically just loads an autosave so you can use that.

geork

Yay!! it's all working now :D thanks! I guess game_start() only works on the ACTUAL game start, and not when I trying to trick it ;D
thanks again guys

Khris

It should be possible to call game_start(); yourself, inside on_event/eEventRestoreGame for example.

monkey0506

Quote from: Lt. Smash on Sun 28/08/2011 12:40:22Normally you shouldn't have to convert an int to a bool, because internally 'false' is always equivalent to '0' and 'true' to '1'. Except you want numbers greater than 1 to also be an equivalent of 'true' ;)

You never have to "convert" an int to a bool in AGS, ever. In AGS true and false are the two values listed within the enumerated type (enum) bool. Since enumerated types are read-only at run-time they can reasonably be treated as constant values, so saying that false is 0 and true is 1 is not inaccurate.

However, it is important to note when you're saying this that conditions in AGS are not evaluated as matching zero (false) or one (true), they are evaluated as matching zero (false) or non-zero (true). For example:

Code: ags
function game_start()
{
  if (-1) AbortGame("-1 evaluates as true!");
}


If you run this code you'll find that in AGS, even negative values evaluate as true when passed as a condition to an if-statement or while-loop. Typically conditions will use a conditional operator such as ==, !=, or one of the several greater/less than (or equal to) operators, but that's not strictly required in AGS. The conditional operators return true or false (1 or 0), so they are correctly evaluated as non-zero or zero depending on the result of the values given, and the associated code is or isn't run as appropriate.

In short, what I'm getting at is that "numbers greater than [or equal to] 1" and numbers less than 0, are always evaluated as true by AGS.

Quote from: Lt. Smash on Sun 28/08/2011 19:31:37
Code: ags
// I'm not quite sure if this if-else-statement is needed because I don't know what ReadInt returns if there is no Int to read...

All of the File functions will abort the game if you read back the file in the incorrect order, just to be clear. The exception to that rule is File.ReadRawChar and File.ReadRawLineBack, which do not attempt to perform any type of conversion on the data, it just returns the raw data from the file. It's actually important to note that depending how the file was written that ReadRawLineBack may not be useful.

For example, integers are written (by File.WriteInt) by writing each of the int's four bytes (IIRC it is written in right-to-left byte order, inverting the bytes in the resultant file). If one (or more) of those bytes happened to be 0 (e.g., an integer value of 1 would have 3 bytes with a value of 0, and one byte with the value of 1), the file itself will be fine, and the integer can be read back by File.ReadInt.

However, strings in AGS are null-terminated (meaning they are terminated by the ASCII NUL character (decimal: 0, '\0')), so if you read a zero byte into a String (say, via ReadRawLineBack) then the String will be terminated at the zero byte. There's actually no way of getting around this short of inserting some replacement char or string in place of the zero bytes, by reading the file one byte at a time with ReadRawChar.

Also, File.ReadRawChar is a bit misleading in that the return type is actually int. The reason that's relevant is because the integer return value can occasionally be negative (I don't actually know why, it just can). The char type is unsigned, which means that an AGS char can never be negative. The easiest way of resolving the issue is to always store the result in a char variable before you use it:

Code: ags
char c = file.ReadRawChar();
// ..use c as needed..


So the File functions in AGS are pretty sensitive about things, but once you understand the "quirks" about them, they're not at all difficult to use.

Lt. Smash

Thanks for the clarification, monkey. It's good to know these things ;)

SMF spam blocked by CleanTalk