Coding story progression

Started by Snarky, Sun 06/11/2011 23:46:48

Previous topic - Next topic

Snarky

I have a game where the main character revisits the same rooms many time over the course of the story, and different things happen depending on how far along it is. What is the best way to write code to keep track of that, to make sure it does the right things at the right times?  I also want to be able to jump to various points in the game for debug/testing purposes.

I started with a global "GameChapter" variable that I defined as an enum. But I'm having some trouble deciding which developments should qualify as new chapters, and how to integrate this with other parameters (like carrying/not carrying an inventory object, or having talked to a certain character before or not) and with parallelism in the game design. And I feel like whenever I add logic for another stage of the game, I have to change a lot of the earlier code, too.

The other big problem is that I'm having trouble with debugging, since by jumping to different rooms and so on, I bypass a lot of the logic for setting the Game Chapter, and end up in strange states.

I probably haven't been as disciplined as I should about keeping all the code consistent, sometimes using the "on first entry" event to separate what happens, sometimes switching depending on the chapter (depending on how the room will appear in the game).

Is there a better way?

NickyNyce

I really can't wait to hear some of the ingenious ideas that will sprite from this thread. I ran into some problems myself when testing my own game. I ended up placing code around my scripts while testing and then deleting them afterwards. Add inventory, this or that variable to true, start here etc....

I would do this sometimes one room at a time to make sure things work right. I'm pretty sure thats not the greatest way of doing it, but I never really did much with the debug mode buttons. I Probably should have.

I do remember reading a thread around here that talks about this...but this should be interesting

selmiak

why not make a new room with same background and give it the differences it needs...
The only downside is a bigger gamesize but a lot more understandable code ;)

Rocco

#3
i do that with one variable and an enum for easier readability:

Code: ags

// globalscript.ash
import int gameprogress;

enum eGameprogress
{
  eProlog     = 0, 
  eMiddle     = 1,
  eFinal       = 2, 
};

Code: ags

// globalscript.asc

int gameprogress;
export gameprogress;

function game_start() 
{   

  gameprogress = eMiddle;
} 

Code: ags


// Roomsript
function room_Load()
{


  if(gameprogress == eProlog)
  {

      StartCutscene(eSkipESCOrRightButton);
      cKlaus.ChangeRoom(10, 423, 119);
      cMichael.EnterRoom(10,460, 139, eDirLeft);
      .
      .
      
    }
    else if(gameprogress == eMiddle)
    {
      //StartCutscene(eSkipESCOrRightButton);
      cKlaus.ChangeRoom(10, 423, 119);
      player.Walk(325, 127, eBlock, eWalkableAreas);
      .

   
    }
     
}


and so on.
And for debugging, set the gameprogress variable at gamestart and you can play the room at any state of the game.
(eventuelly need to give the player some inventory items manually, which he should have at this point)


Calin Leafshade

#4
one way might be to have a big global array of flags that denote game progression. Then you can use enums or DEFINEs to make it more readable.

Code: ags

bool Flags[1000];

#define EATENAPPLE 0
#define KILLEDMONKEY 1
#define DANCEDAJIG 2


//wrapped in functions in case we need to do any checking later on or something.

void SetFlag(int index, bool val)
{
   Flags[index] = val;

}

bool Flag(index)
{
 return Flags[index];
}



Now you can set the game at any point just by setting all the appropriate flags

Code: ags


void SetToStart()
{
   SetFlag(0,false); //use the number or the define
   SetFlag(EATENAPPLE,false);
   SetFlag(2,false);

}

void SetToMiddle()
{
   SetFlag(0,true);
}



Its not particularly good practice to have global state variables but the advantage here is that you can set the state of the game from any script in the game. If you use local flags inside rooms it becomes impossible to set their state from somewhere else in the game.

Also you dont have to group things into chapters, you can set *any* game state at any point.

If you wanted to get really complicated (and awesome) you could define a linked-list type tree structure where every event requires all the previous events to happen first so you can set the most recent event and the system will automatically set all the flags prior to it in the list like this:

                     /   - EVENT 4 - EVENT 5
EVENT 1 - EVENT 2 |
                     \     - EVENT 3 - EVENT 6 


if you set the flag associated with event 5 to true then the system would work backwards through the list setting Events 4, 2 and 1 to true but not affecting events 3 and 6. This allows for non-linear story progression too.


Snarky

Rocco, that's essentially my approach, except I don't have the method at the beginning to switch to the different chapters. That would probably do a lot to help. Individual flags like Calin proposes would be a good way to handle non-linear progression within chapters, but seems too involved as a way to handle the complete game logic.

I have defined about six acts in my game (essentially parts where you have access to different rooms, and room state is usually different for each), but one problem is that my plans for the structure are evolving, so I want to be able to add, split and merge the acts, which is a lot of work with this approach. (Also since I've named them like eAct01_Intro, so that they'll sort correctly in the autocomplete, it requires me to rename all of them if the ordering changes.)

Quote from: selmiak on Mon 07/11/2011 12:13:00
why not make a new room with same background and give it the differences it needs...
The only downside is a bigger gamesize but a lot more understandable code ;)

Because then I have to replicate every aspect of the room (walkable areas, walkbehinds, hotspots, regions, objects, exits and events, just to take the most obvious) in every copy, and keep them all up to date if I make any changes. And write special logic to go to the right version from any adjoining room. In short, it's a terrible idea in most cases. (I am in fact doing exactly that in some exceptional cases where I need special versions of the room for cut scenes and so on, though.)

selmiak

yeah, you're right, somehow I forgot about this stuff *whistles*

Wyz

#7
I use a very similar approach to yours but with a slight twist :D.
First I have about the same enumeration you use but I will not set it manually. Instead each Act has a function that needs to be called to change to it.

Code: ags

// module header:
void GotoAct0(); // Introduction
void GotoAct1(); // First act
void GotoAct2(); // etc
//...


Secondly I make a distinction between acts and chapters, well its not really important how you call them but they also differ in effect. While the act functions will set the complete game state to the beginning of that act, the chapter functions will only set things that have changed subsequently from the last character. That way say you want to switch to the 3th chapter of the second act you'd call:
Code: ags

GotoAct2();
Chapter1();
Chapter2();
Chapter3();


Still the difficulty is in keeping track of everything that can change, yes you need to have a spider-sense for that. Dividing the game up in smaller parts may help keep the amount of things that may have changed to a conveniently small. Though, here is a general check list from the top of my head:

  • check all global variables
  • check all room variables
  • check all character positions and in what room they are
  • check all object positions and visibility
  • check the inventory (what can be in it, what not, the quantity)
  • check regions and walkable areas and their settings.


  • edit:
    Oh, and I forgot to mention, I do call the GotoAct functions when the player reaches that point in the game. This to be absolutely sure the game will act the same reaching that point and skipping to that point. Though, the Chapter functions are only for testing and simply emulate whatever happens when you would play the game till that point.
Life is like an adventure without the pixel hunts.

SMF spam blocked by CleanTalk