Working backwards from the finished product to understand how it ticks.
Save File
The player save is stored in a file called PLAYER.SAV. It has 77 bytes in the following structure:
struct player_save { char name_length; char name[10]; char race; unsigned char level; // game max is 15 signed short max_life; signed short current_life; signed short gold; unsigned int xp; unsigned char max_magic; // note that life is signed but magic is not unsigned char current_magic; unsigned char physical_strength; unsigned char mental_strength; unsigned char dexterity; unsigned char constitution; unsigned char luck; // some spells buff stats so this tracks what to reset those stats to when the buff runs off char base_fighting_attack; char base_fighting_defense; char base_magic_offensive; char base_magic_defense; char fighting_attack; char fighting_defense; char magic_offensive; char magic_defense; char magic_miscellaneous; char lock_picking; char climbing; char stealing; char perception; char held_weapon; // weapons in range 0x00 to 0x35 char worn_armor; // armor in range 0x00 to 0x?? char mission_one_flag; // whether you spoke to the evil claric char mission_two_flag; // whether you talked to Tulik char mission_three_flag; // whether you beat the vampire in myron char mission_four_flag; // whether you have the talisman char mission_five_flag; // whether you've talked to the sage char mission_six_flag; // probably if the basement in calatiki is open char mission_one_complete; char mission_two_complete; char mission_three_complete; char mission_four_complete; char mission_five_complete; char unknown[4]; // no idea what these are char x; // x coord on overworld char y; // y coord on overworld char current_spell; // which buff is currently applied (e.g. wizard eye,etc) char mystery; // turn counter? goes up by one if you move 1 step on the overworld... // reset on escape from heaven? char weapon_inventory[5]; // restrictions on weapon range apply signed char held_weapon_durability; signed char weapon_inventory_durability[5]; // 1 to 1 map with weapon_inventory signed char worn_armor_durability; };
Name
The player name is split into two parts: the length, followed by that many characters in the name.
The player character generator will always set the length to 10. If the name has less than 10 letters, the name will be padded out with spaces (0x20). Regardless of the length, null characters (0x00) will prematurely terminate the string.
The letters of the name can be anything on Code page 437 . The null character will prematurely terminate the string. Alt codes will work for entering non-ASCII characters.
Race
A valid race is one of 6 values:
- 0 - Human
- 1 - Dwarf
- 2 - Elf
- 3 - Corintir
- 4 - Superior
- 5 - Superior
The lines up with the images in PLAYER.CON. Any value between 0x06 and 0x0E will change the player graphic to the appropriate sprite on that sprite sheet but will not show anything in the Race field of the character sheet.
Values of 0x0F and above make you look like a spider, but the game will crash trying to enter towns or dungeons. Saving will fail and erase your file so don't do that.
"Base" Stats VS Apparent Stats
Some spells buff a stat for a period of time. For example, Mystical Boost will raise your Magic Offensive stat until it wears off. The game tracks this temporarily augmented stat as a separate field in the save data from the actual value of that stat. The real value is tracked as the "base".
Current Buff
Along with the stats that are affected, the sve file tracks which buff is currently applied. It mostly likely uses this to know, when the time is up, which stat to reset. Each spell has it's own value:
00 (Normal) 01 Wizard Eye 02 Mystical Boost (buff to magic offensive, reflected on ztats screen) 03 Heroism 04 Spell Protection 05 Shield 06 Iron Skin ... ?
Weapon IDs
Each weapon is represented by a unique value. This includes not only items you can buy in store but also weapons that NPCs use:
00 Hands 01 Dagger 02 Staff 03 Mace 04 Morning star 05 Axe 06 Long Sword 07 2H Sword 08 Long Bow 09 Sling 0A Club 0B Dagger +1 0C Staff +1 0D Mace +1 0E Morning Star +1 0F Axe +1 10 Long Sword +1 11 2H Sword +1 12 Long Bow +1 13 Sling +1 14 Club +1 15 Dagger +2 16 Staff +2 17 Mace +2 18 Morn. Star +2 19 Axe +2 1A Long Sword +2 1B 2H Sword +2 1C Long Bow +2 1D Sling +2 1E Club +2 1F Dagger +3 20 Staff +3 21 Mace +3 22 Morn. Star +3 23 Axe +3 24 Long Sword +3 25 2H Sword +3 26 Long Bow +3 27 Sling +3 28 Club +3 29 Slime 2A Bite 2B Big Bite 2C Giant Bite 2D Fist 2E Big Fist 2F Giant Fist 30 Claws 31 Big Claws 32 Giant Claws 33 Vampire Touch 34 Vampyr Touch 35 Big Broom 36 255 ... ?
Armor IDs
Each armor in the game is represented with a unique ID. This includes things you can buy in-store as well as monster armor:
00 Nude 01 Cloth 02 Padded 03 Leather 04 Studded 05 Ring Mail 06 Scale Mail 07 Chain Mail 08 Splint Mail 09 Plate Mail 0A Full Plate 0B Cloth +1 ... 15 Cloth +2 ... 1F Cloth +3 .. 28 Full Plate +3 29 Bones 2A Thick Skin 2B Transparency 2C Fur 2D Thick Fur 2E Scales 2F Magic Robe 30 Hard Bark 31 Slime Coating ... ?
The "Mystery" Counter
There is one byte between the current buff and the player inventory whose use is unknown. My guess is that it's a turn counter because when save, take a single step in the overworld, and save again then it will increment by 1. Although, I'm not sure why the game cares. It seems to reset when you are deemed worthy in Heaven.
Unknown Bytes
Between the mission completion flags and the current overworld location are 4 bytes. They never seem to change.