-
Notifications
You must be signed in to change notification settings - Fork 0
Save File Format
This page will detail the save file format specification for SwashRL. This file format is defined by the methods used to save dungeon levels in savefile.d.
Although it is possible in theory to simply write structs directly to files in the D programming language, I had some issues actually getting this to work in practice; for some reason the compiler would complain any time I attempted to save Map
structs this way. As such, SwashRL saves level data by writing every variable pertinent to the level on a separate line (using the newline character as a delimiter).
For compatibility purposes, every save file starts with the version number as defined in global.d.
The version number is written as a floating-point number with three digits after the decimal place, following the V.RRR version numbering system that SwashRL uses, where V is the version number and RRR is the three-digit revision number. The commit number is not included.
The save data for each level is split up into sections separated by a Separator Marker represented by the ASCII value 20
. This is used to show where each section begins and ends as well as to keep track of where in the file we are while reading in the level data later on so that we can be sure that the level data is being read in properly. In the case of the list of monsters it is necessary to place the separator marker because the monsters are stored in a dynamically-sized array.
The way that map data is structured in SwashRL necessitates that the Map
struct contain a certain amount of Item
data, consisting of exactly one Item
per map tile. However, in the majority of cases, most of these Item
s will be the placeholder No_item
, indicating that there is in fact no item on this map tile. To save space, the ASCII value 19
is used to quickly determine that there is No_item
here, both to save space and to make reading and writing level data faster.
At time of writing, SwashRL is on version 0.032, and the save files only save data on the level that the player was on when they saved their game.
Dungeon levels are saved in the directory save/lev
.
The dungeon level file begins with the version number followed by a separator marker.
Each Tile
in the Map
struct is saved row-by-row in linear order. The data written in order to save each tile t
, in order, is as follows:
Variable | Data Type | Explanation |
---|---|---|
t.sym.ch |
char |
What character is used to represent t
|
t.sym.color ` |
Colors |
An enum used to select the appropriate color pair for t
|
t.block_cardinal_movement |
bool |
Whether or not t blocks movement in cardinal directions (i.e. north, south, east, or west) |
t.block_diagonal_movement |
bool |
Whether or not t blocks movement in diagonal directions (i.e. northwest, northeast, southeast, and southwest) |
t.block_vision |
bool |
Whether or not t blocks line-of-sight |
t.lit |
bool |
Whether or not t is illuminated |
t.seen |
bool |
Whether or not the player has seen t , or to put it another way whether or not t should be visible in the "fog of war" while the player does not have line-of-sight with it |
t.hazard |
uint |
A flag indicating special properties that the tile may have, such as traps, mold, or blood splatters |
After every map tile has been written, a separator is written to indicate that Tile
output has been completed.
Each Item
in the Map
struct is checked row-by-row in linear order. For each item i
in the map data, i.sym.ch
is checked first. If it is null (ASCII value 0
, indicated by the '\0'
character in the source code) then this is a sign that there is no item in this space and a placeholder marker is written to save space.
If a placeholder has not been written, the following data is written to the save file in order:
Variable | Data Type | Explanation |
---|---|---|
i.sym.ch |
char |
What character is used to represent i
|
i.sym.color |
Colors |
An enum used to select the appropriate color pair for i
|
i.name |
string |
i 's name |
i.type |
uint |
A flag indicating what type of item i is, e.g. weapon, armor, &c |
i.equip |
uint |
A flag indicating what equipment slot i goes into, if i is an equippable item |
i.addd |
int |
The number of dice to add (or subtract) from the player's dice rolls, if applicable |
i.addm |
int |
The number to add (or subtract) from modifiers to the player's dice rolls, if applicable |
After every item has been written, a separator is written to indicate that Item
output has been completed.
Each Monst
in the Map
struct is checked in linear order. If for whatever reason the monster has 0
hit points, it is not written into the save file. Otherwise, for each monster m
, the following data is written in order:
Variable | Data Type | Explanation |
---|---|---|
m.sym.ch |
char |
What character is used to represent m
|
m.sym.color |
Colors |
An enum used to select the appropriate color pair for m
|
m.name |
string |
m 's name |
m.hp |
int |
m 's hit points as of the creation of the save file |
m.fly |
uint |
A flag indicating whether or not m can fly |
m.swim |
uint |
A flag indicating whether or not m can swim |
m.x |
uint |
m 's x coordinate as of the creation of the save file |
m.y |
uint |
m 's y coordinate as of the creation of the save file |
m.attack_roll.dice |
uint |
The number of dice in m 's attack rolls |
m.attack_roll.modifier |
int |
The modifier to add (or subtract) from m 's attack rolls |
m.attack_roll.floor |
int |
The lowest possible number m can score on an attack roll |
m.attack_roll.ceiling |
int |
The highest possible number m can score on an attack roll |
The monster's inventory, m.inventory
, is not saved, because monster inventories have not been properly implemented as of version 0.031.
After all of the monsters have been written, a separator is written to indicate that Monst
output has been completed. This is necessary to the saving and loading process because all monsters in a Map
are stored in a dynamically-allocated array, rather than a fixed array like with Item
s and Tile
s.
As of version 0.032, the player character's x
and y
coordinates, stored as ubyte
s, is saved with the map data. This is because, as mentioned previously, the only save function currently available is the one which saves the map data. This means that any time the player saves their game, their inventory is lost and their hit points will have to be re-rolled.
There is no separator marker following the writing of the player, as it is the last piece of data written to the save file.