2
\$\begingroup\$

I am trying to figure out what is the best way to handle persistent changes in game world. Metroidvania games keep track of the player actions within a level, for example "this boss has been defeated", "this power up has been taken", "this button has been pushed", etc. In "Cave Story", some completed actions will change the state of the main village, and some NPCs will have dialog specific to what the player has done, before.

I can think of two different ways to handle this functionality:

  1. Having a copy of the same map, for each state; this sounds resource heavy, and inefficient.
  2. Using some kind of "chapter" definition that allows me to load/initialise each object of the map in regards to the current chapter. For example, multiple versions of the same NPC would exist, on the map, at various locations, with a chapter attribute. The map can start to get messy, and it can be hard to test.

In regards to the second option, different behaviors and the object loading code would look like this:

if (object.currentChapter == currentChapter) {
    object.load()
}

I am using Tiled, and as proposed, it can then be possible to load the object layer corresponding to the current game state or chapter:

loadMap(map, chapter) {
    ...
    createObjects(map, objectLayerName)
}

layered map

As an example, in object_chapter_1, the wizard is outside of a laboratory. In object_chapter_2, the wizard is inside of the laboratory, preparing some magic potions.

I can't really figure out what is the best solution, and what techniques can be used to deal with this problem. What are the differant techniques to deal with different world and story states, in metroidvanias?

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

Today computers and consoles have lots of storage. Storing multiple maps may be more efficient if there are very significant changes. The issue is, what if you wanted to change something minor in all the maps at once?

This is where layers come in. You can design your level editor to support layering. This means each change to the world is saved as a "delta" like a layer you apply on top of the existing world (think of an image editor). However, this layer may also move characters into new position if needed.

However, you'll need to make sure layers do not conflict. If change a does one thing and change b does another contradicting thing (non-linear game) than if both a and b happen you need to apply layer c that merges these changes in a desired manner.

You need to maintain a data-structure with relevant info about items like relics and bosses with states that need to be maintained. There is no magical way around it. This data-structure could be a large array that stores objects with info regarding each of these entities.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ the layer idea sounds really interesting. I am using Tiled as a map editor and all my interactive entities are described in an object layer (baddies, items, light sources, npc etc...), it would be easy to just load the correct object layer based on a "chapter" reference, ie: mapLoader.load('map.tmx', 'chapter1') and then loading the corresponding object layer. \$\endgroup\$ Commented Dec 13, 2016 at 1:07
1
\$\begingroup\$

Store persistent game events somewhere, say in a GameState object or the like. This object would provide methods like setState(name, value) and getState(name).

For dialogs, put conditionals on each branch of the tree or option. e.g. a dialog tree data file might look something like:

<dialog start="hello">
   <entry id="hello">
      <say who="npc">Greetings good person!</say>
      <option text="Bye" goto="goodbye"/>
      <option text="It is done" goto="quest_complete" requires="state['quest_147'] == 'complete'/>
   </entry>
   <entry id="goodbye">
      <say who="npc">Bye now!</say>
      <quit/>
   </entry>
   <entry id="goodbye">
      <say who="npc">That is fantastic! Here's your reward, as agreed.</say>
      <award type="gold" amount="1000"/>
      <set state="quest_147" value="closed"/>
      <quit/>
   </entry>
 </dialog>

For other map elements you might define the requirements in different ways. For example, an item spawner might have conditions to only spawn the item if a particular quest is complete and the player hasn't already acquired the item.

While the big bag of state is the simplest approach to implement, I might suggest you distinct types of state and track those explicitly. e.g., instead of checking if the player ever picked up a particular spawn of an item, check if the player currently needs the item and doesn't currently have it. The reason is that it will help avoid bugs; you generally want to depend directly on the game state (quest status, inventory, etc.) rather than a derived state (like the big state key/value map) to reduce the chance of state desync's breaking your game.

\$\endgroup\$
1
  • \$\begingroup\$ thanks for the idea, I have the feeling that a mix between a global GameState and an object layer per chapter will do the trick in my case. \$\endgroup\$ Commented Dec 13, 2016 at 1:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .