Player progression
Source: src/dungeon_player.c · 257 lines · 22 functions.
Module responsibility
Owns player statistics, saved progress, experience requirements, levelling choices and validation.
Module entry points
These functions are exposed to the rest of the Dungeon system. Open one to follow its decisions and helper calls.
How to read these diagrams
Each function has an expandable structural diagram derived from the current C source. Decisions split into True and False, loop bodies are indented, and arrows show statement order.
These are source diagrams before preprocessing, not runtime traces. Callback execution, task scheduling and macro expansion are not inferred.
Functions in this file
updateChecksum() · source line 18
static void updateChecksum(uint32_t *checksum, uint8_t value)
- Execute
*checksum = (*checksum ^ value) * CHECKSUM_PRIME;
Read original function source
static void updateChecksum(uint32_t *checksum, uint8_t value) { *checksum = (*checksum ^ value) * CHECKSUM_PRIME; }updateChecksumU16() · source line 19
static void updateChecksumU16(uint32_t *checksum, uint16_t value)
Calls into this documentation:
- Execute
updateChecksum(checksum, (uint8_t)value); - Execute
updateChecksum(checksum, (uint8_t)(value >> 8));
Read original function source
static void updateChecksumU16(uint32_t *checksum, uint16_t value) { updateChecksum(checksum, (uint8_t)value); updateChecksum(checksum, (uint8_t)(value >> 8)); }updateChecksumU32() · source line 20
static void updateChecksumU32(uint32_t *checksum, uint32_t value)
Calls into this documentation:
- Execute
updateChecksum(checksum, (uint8_t)value); - Execute
updateChecksum(checksum, (uint8_t)(value >> 8)); - Execute
updateChecksum(checksum, (uint8_t)(value >> 16)); - Execute
updateChecksum(checksum, (uint8_t)(value >> 24));
Read original function source
static void updateChecksumU32(uint32_t *checksum, uint32_t value)
{
updateChecksum(checksum, (uint8_t)value);
updateChecksum(checksum, (uint8_t)(value >> 8));
updateChecksum(checksum, (uint8_t)(value >> 16));
updateChecksum(checksum, (uint8_t)(value >> 24));
}writeByte() · source line 28
static bool writeByte(FILE *file, uint8_t value, DungeonPlayerResult *result)
- Decision
fwrite(&value, 1, 1, file) != 1UTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
++result->bytes; - Exit this function
return true;
Read original function source
static bool writeByte(FILE *file, uint8_t value, DungeonPlayerResult *result)
{
if (fwrite(&value, 1, 1, file) != 1U) return false;
++result->bytes;
return true;
}writeU16() · source line 35
static bool writeU16(FILE *file, uint16_t value, DungeonPlayerResult *result)
Calls into this documentation:
- Exit this function
return writeByte(file, (uint8_t)value, result) && writeByte(file, (uint8_t)(value >> 8), result);
Read original function source
static bool writeU16(FILE *file, uint16_t value, DungeonPlayerResult *result)
{
return writeByte(file, (uint8_t)value, result) && writeByte(file, (uint8_t)(value >> 8), result);
}writeU32() · source line 40
static bool writeU32(FILE *file, uint32_t value, DungeonPlayerResult *result)
Calls into this documentation:
- Exit this function
return writeByte(file, (uint8_t)value, result) && writeByte(file, (uint8_t)(value >> 8), result) && writeByte(file, (uint8_t)(value >> 16), result) && writeByte(file, (uint8_t)(value >> 24), result);
Read original function source
static bool writeU32(FILE *file, uint32_t value, DungeonPlayerResult *result)
{
return writeByte(file, (uint8_t)value, result) && writeByte(file, (uint8_t)(value >> 8), result) &&
writeByte(file, (uint8_t)(value >> 16), result) && writeByte(file, (uint8_t)(value >> 24), result);
}readByte() · source line 46
static bool readByte(FILE *file, uint8_t *value, DungeonPlayerResult *result)
- Decision
fread(value, 1, 1, file) != 1UTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
++result->bytes; - Exit this function
return true;
Read original function source
static bool readByte(FILE *file, uint8_t *value, DungeonPlayerResult *result)
{
if (fread(value, 1, 1, file) != 1U) return false;
++result->bytes;
return true;
}readU16() · source line 53
static bool readU16(FILE *file, uint16_t *value, DungeonPlayerResult *result)
Calls into this documentation:
- Execute
uint8_t low, high; - Decision
!readByte(file, &low, result) || !readByte(file, &high, result)True- Exit this function
return false;
FalseContinue after this decision.
- Execute
*value = (uint16_t)low | ((uint16_t)high << 8); - Exit this function
return true;
Read original function source
static bool readU16(FILE *file, uint16_t *value, DungeonPlayerResult *result)
{
uint8_t low, high;
if (!readByte(file, &low, result) || !readByte(file, &high, result)) return false;
*value = (uint16_t)low | ((uint16_t)high << 8);
return true;
}readU32() · source line 61
static bool readU32(FILE *file, uint32_t *value, DungeonPlayerResult *result)
Calls into this documentation:
- Execute
uint8_t b0, b1, b2, b3; - Decision
!readByte(file, &b0, result) || !readByte(file, &b1, result) || !readByte(file, &b2, result) || !readByte(file, &b3, result)True- Exit this function
return false;
FalseContinue after this decision.
- Execute
*value = (uint32_t)b0 | ((uint32_t)b1 << 8) | ((uint32_t)b2 << 16) | ((uint32_t)b3 << 24); - Exit this function
return true;
Read original function source
static bool readU32(FILE *file, uint32_t *value, DungeonPlayerResult *result)
{
uint8_t b0, b1, b2, b3;
if (!readByte(file, &b0, result) || !readByte(file, &b1, result) || !readByte(file, &b2, result) || !readByte(file, &b3, result)) return false;
*value = (uint32_t)b0 | ((uint32_t)b1 << 8) | ((uint32_t)b2 << 16) | ((uint32_t)b3 << 24);
return true;
}setDefaults() · source line 69
static void setDefaults(void)
Calls into this documentation:
- Execute
const DungeonConfig *config = getDungeonConfig(); - Execute
memset(&player, 0, sizeof(player)); - Execute
player.level = 1U; - Execute
player.health = config->startHealth; - Execute
player.maxHealth = config->startHealth; - Execute
player.attack = config->startAttack; - Execute
player.defence = config->startDefence; - Execute
player.position = getDungeonWorld()->entrance; - Execute
player.enteredFrom = DUNGEON_EXIT_NONE;
Read original function source
static void setDefaults(void)
{
const DungeonConfig *config = getDungeonConfig();
memset(&player, 0, sizeof(player));
player.level = 1U;
player.health = config->startHealth;
player.maxHealth = config->startHealth;
player.attack = config->startAttack;
player.defence = config->startDefence;
player.position = getDungeonWorld()->entrance;
player.enteredFrom = DUNGEON_EXIT_NONE;
}validPlayer() · source line 82
static bool validPlayer(void)
Calls into this documentation:
- Execute
const DungeonConfig *config = getDungeonConfig(); - Decision
player.position.floor >= DUNGEON_FLOOR_COUNT || player.position.x >= DUNGEON_WIDTH || player.position.y >= DUNGEON_HEIGHTTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
const DungeonCell *cell = &getDungeonWorld()->floors[player.position.floor].cells[player.position.y][player.position.x]; - Exit this function
return cell->tileType != DUNGEON_TILE_EMPTY && cell->exits != DUNGEON_EXIT_NONE && player.level >= 1U && player.maxHealth >= config->startHealth && player.health <= player.maxHealth && player.attack >= config->startAttack && player.defence >= config->startDefence;
Read original function source
static bool validPlayer(void)
{
const DungeonConfig *config = getDungeonConfig();
if (player.position.floor >= DUNGEON_FLOOR_COUNT || player.position.x >= DUNGEON_WIDTH || player.position.y >= DUNGEON_HEIGHT) return false;
const DungeonCell *cell = &getDungeonWorld()->floors[player.position.floor].cells[player.position.y][player.position.x];
return cell->tileType != DUNGEON_TILE_EMPTY && cell->exits != DUNGEON_EXIT_NONE && player.level >= 1U &&
player.maxHealth >= config->startHealth && player.health <= player.maxHealth &&
player.attack >= config->startAttack && player.defence >= config->startDefence;
}playerChecksum() · source line 92
static uint32_t playerChecksum(uint32_t world_checksum, const uint8_t *reserved)
Calls into this documentation:
- Execute
uint32_t checksum = CHECKSUM_START; - Execute
updateChecksumU32(&checksum, world_checksum); - Execute
updateChecksum(&checksum, player.position.floor); - Execute
updateChecksum(&checksum, player.position.x); - Execute
updateChecksum(&checksum, player.position.y); - Execute
updateChecksum(&checksum, player.enteredFrom); - Execute
updateChecksumU16(&checksum, player.level); - Execute
updateChecksumU32(&checksum, player.experience); - Execute
updateChecksumU16(&checksum, player.health); - Execute
updateChecksumU16(&checksum, player.maxHealth); - Execute
updateChecksumU16(&checksum, player.attack); - Execute
updateChecksumU16(&checksum, player.defence); - Execute
updateChecksumU32(&checksum, player.gold); - Execute
updateChecksumU16(&checksum, player.keys); - Execute
updateChecksumU16(&checksum, player.healthPotions); - Execute
updateChecksumU16(&checksum, player.unspentStatPoints); - Execute
updateChecksumU32(&checksum, player.inventoryFlags); - Execute
updateChecksumU16(&checksum, player.shopPurchaseCount); - Execute
updateChecksumU32(&checksum, player.runFlags); - Execute
updateChecksumU32(&checksum, player.moves); - Execute
updateChecksumU32(&checksum, player.timePlayedSeconds); - FOR loop
unsigned index = 0; index < PLAYER_RESERVED_SIZE; ++indexRepeat body while the loop condition permits; then continue below.
- Execute
updateChecksum(&checksum, reserved[index]);
- Exit this function
return checksum;
Read original function source
static uint32_t playerChecksum(uint32_t world_checksum, const uint8_t *reserved)
{
uint32_t checksum = CHECKSUM_START;
updateChecksumU32(&checksum, world_checksum);
updateChecksum(&checksum, player.position.floor); updateChecksum(&checksum, player.position.x); updateChecksum(&checksum, player.position.y); updateChecksum(&checksum, player.enteredFrom);
updateChecksumU16(&checksum, player.level); updateChecksumU32(&checksum, player.experience);
updateChecksumU16(&checksum, player.health); updateChecksumU16(&checksum, player.maxHealth); updateChecksumU16(&checksum, player.attack); updateChecksumU16(&checksum, player.defence);
updateChecksumU32(&checksum, player.gold); updateChecksumU16(&checksum, player.keys); updateChecksumU16(&checksum, player.healthPotions); updateChecksumU16(&checksum, player.unspentStatPoints);
updateChecksumU32(&checksum, player.inventoryFlags); updateChecksumU16(&checksum, player.shopPurchaseCount); updateChecksumU32(&checksum, player.runFlags);
updateChecksumU32(&checksum, player.moves); updateChecksumU32(&checksum, player.timePlayedSeconds);
for (unsigned index = 0; index < PLAYER_RESERVED_SIZE; ++index) updateChecksum(&checksum, reserved[index]);
return checksum;
}saveDungeonPlayer() · source line 106
bool saveDungeonPlayer(DungeonPlayerResult *result)
Calls into this documentation:
- Decision
result == NULLTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
memset(result, 0, sizeof(*result)); - Execute
result->error = DUNGEON_PLAYER_OPEN_WRITE_FAILED; - Execute
errno = 0; - Execute
FILE *file = fopen(PLAYER_FILE, "wb"); - Decision
file == NULLTrue- Execute
result->errno_value = errno; - Exit this function
return false;
FalseContinue after this decision.
- Execute
const uint8_t magic[] = { 'M', 'P', 'L', 'Y' }; - Execute
uint8_t reserved[PLAYER_RESERVED_SIZE] = {0}; - Execute
reserved[0] = (uint8_t)player.startedAtEpoch; - Execute
reserved[1] = (uint8_t)(player.startedAtEpoch >> 8); - Execute
reserved[2] = (uint8_t)(player.startedAtEpoch >> 16); - Execute
reserved[3] = (uint8_t)(player.startedAtEpoch >> 24); - Execute
reserved[4] = (uint8_t)player.finishedAtEpoch; - Execute
reserved[5] = (uint8_t)(player.finishedAtEpoch >> 8); - Execute
reserved[6] = (uint8_t)(player.finishedAtEpoch >> 16); - Execute
reserved[7] = (uint8_t)(player.finishedAtEpoch >> 24); - Execute
const uint32_t world_checksum = getDungeonWorldChecksum(); - Execute
const uint32_t checksum = playerChecksum(world_checksum, reserved); - Execute
bool written = fwrite(magic, 1, sizeof(magic), file) == sizeof(magic); - Execute
result->bytes += written ? sizeof(magic) : 0U; - Execute
written = written && writeU16(file, PLAYER_VERSION, result) && writeU16(file, PLAYER_PAYLOAD_SIZE, result) && writeU32(file, checksum, result) && writeU32(file, world_checksum, result) && writeByte(file, player.position.floor, result) && writeByte(file, player.position.x, result) && writeByte(file, player.position.y, result) && writeByte(file, player.enteredFrom, result) && writeU16(file, player.level, result) && writeU32(file, player.experience, result) && writeU16(file, player.health, result) && writeU16(file, player.maxHealth, result) && writeU16(file, player.attack, result) && writeU16(file, player.defence, result) && writeU32(file, player.gold, result) && writeU16(file, player.keys, result) && writeU16(file, player.healthPotions, result) && writeU16(file, player.unspentStatPoints, result) && writeU32(file, player.inventoryFlags, result) && writeU16(file, player.shopPurchaseCount, result) && writeU32(file, player.runFlags, result) && writeU32(file, player.moves, result) && writeU32(file, player.timePlayedSeconds, result) && fwrite(reserved, 1, sizeof(reserved), file) == sizeof(reserved); - Decision
writtenTrue- Execute
result->bytes += sizeof(reserved);
FalseContinue after this decision.
- Execute
const int close_result = fclose(file); - Decision
!written || close_result != 0 || result->bytes != PLAYER_FILE_SIZETrue- Execute
result->error = DUNGEON_PLAYER_WRITE_FAILED; - Execute
result->errno_value = errno; - Exit this function
return false;
FalseContinue after this decision.
- Execute
result->error = DUNGEON_PLAYER_OK; - Exit this function
return true;
Read original function source
bool saveDungeonPlayer(DungeonPlayerResult *result)
{
if (result == NULL) return false;
memset(result, 0, sizeof(*result));
result->error = DUNGEON_PLAYER_OPEN_WRITE_FAILED;
errno = 0;
FILE *file = fopen(PLAYER_FILE, "wb");
if (file == NULL) { result->errno_value = errno; return false; }
const uint8_t magic[] = { 'M', 'P', 'L', 'Y' };
uint8_t reserved[PLAYER_RESERVED_SIZE] = {0};
reserved[0] = (uint8_t)player.startedAtEpoch; reserved[1] = (uint8_t)(player.startedAtEpoch >> 8);
reserved[2] = (uint8_t)(player.startedAtEpoch >> 16); reserved[3] = (uint8_t)(player.startedAtEpoch >> 24);
reserved[4] = (uint8_t)player.finishedAtEpoch; reserved[5] = (uint8_t)(player.finishedAtEpoch >> 8);
reserved[6] = (uint8_t)(player.finishedAtEpoch >> 16); reserved[7] = (uint8_t)(player.finishedAtEpoch >> 24);
const uint32_t world_checksum = getDungeonWorldChecksum();
const uint32_t checksum = playerChecksum(world_checksum, reserved);
bool written = fwrite(magic, 1, sizeof(magic), file) == sizeof(magic);
result->bytes += written ? sizeof(magic) : 0U;
written = written && writeU16(file, PLAYER_VERSION, result) && writeU16(file, PLAYER_PAYLOAD_SIZE, result) &&
writeU32(file, checksum, result) && writeU32(file, world_checksum, result) &&
writeByte(file, player.position.floor, result) && writeByte(file, player.position.x, result) && writeByte(file, player.position.y, result) && writeByte(file, player.enteredFrom, result) &&
writeU16(file, player.level, result) && writeU32(file, player.experience, result) && writeU16(file, player.health, result) && writeU16(file, player.maxHealth, result) &&
writeU16(file, player.attack, result) && writeU16(file, player.defence, result) && writeU32(file, player.gold, result) &&
writeU16(file, player.keys, result) && writeU16(file, player.healthPotions, result) && writeU16(file, player.unspentStatPoints, result) &&
writeU32(file, player.inventoryFlags, result) && writeU16(file, player.shopPurchaseCount, result) && writeU32(file, player.runFlags, result) &&
writeU32(file, player.moves, result) && writeU32(file, player.timePlayedSeconds, result) &&
fwrite(reserved, 1, sizeof(reserved), file) == sizeof(reserved);
if (written) result->bytes += sizeof(reserved);
const int close_result = fclose(file);
if (!written || close_result != 0 || result->bytes != PLAYER_FILE_SIZE) { result->error = DUNGEON_PLAYER_WRITE_FAILED; result->errno_value = errno; return false; }
result->error = DUNGEON_PLAYER_OK;
return true;
}loadPlayer() · source line 140
static bool loadPlayer(DungeonPlayerResult *result)
Calls into this documentation:
- Execute
memset(result, 0, sizeof(*result)); - Execute
result->error = DUNGEON_PLAYER_OPEN_READ_FAILED; - Execute
FILE *file = fopen(PLAYER_FILE, "rb"); - Decision
file == NULLTrue- Exit this function
return false;
FalseContinue after this decision.
- Decision
fseek(file, 0L, SEEK_END) != 0 || ftell(file) != (long)PLAYER_FILE_SIZETrue- Execute
result->error = DUNGEON_PLAYER_BAD_FILE_SIZE; - Execute
fclose(file); - Exit this function
return false;
FalseContinue after this decision.
- Execute
rewind(file); - Execute
uint8_t magic[4], reserved[PLAYER_RESERVED_SIZE]; - Execute
uint16_t version, payload; - Execute
uint32_t checksum, world_checksum; - Execute
bool read = fread(magic, 1, sizeof(magic), file) == sizeof(magic); - Execute
result->bytes += read ? sizeof(magic) : 0U; - Execute
read = read && readU16(file, &version, result) && readU16(file, &payload, result) && readU32(file, &checksum, result) && readU32(file, &world_checksum, result) && readByte(file, &player.position.floor, result) && readByte(file, &player.position.x, result) && readByte(file, &player.position.y, result) && readByte(file, &player.enteredFrom, result) && readU16(file, &player.level, result) && readU32(file, &player.experience, result) && readU16(file, &player.health, result) && readU16(file, &player.maxHealth, result) && readU16(file, &player.attack, result) && readU16(file, &player.defence, result) && readU32(file, &player.gold, result) && readU16(file, &player.keys, result) && readU16(file, &player.healthPotions, result) && readU16(file, &player.unspentStatPoints, result) && readU32(file, &player.inventoryFlags, result) && readU16(file, &player.shopPurchaseCount, result) && readU32(file, &player.runFlags, result) && readU32(file, &player.moves, result) && readU32(file, &player.timePlayedSeconds, result) && fread(reserved, 1, sizeof(reserved), file) == sizeof(reserved); - Decision
readTrue- Execute
result->bytes += sizeof(reserved);
FalseContinue after this decision.
- Execute
fclose(file); - Decision
!read || result->bytes != PLAYER_FILE_SIZETrue- Execute
result->error = DUNGEON_PLAYER_READ_FAILED; - Exit this function
return false;
FalseContinue after this decision.
- Decision
memcmp(magic, "MPLY", sizeof(magic)) != 0True- Execute
result->error = DUNGEON_PLAYER_BAD_SIGNATURE; - Exit this function
return false;
FalseContinue after this decision.
- Decision
version != PLAYER_VERSIONTrue- Execute
result->error = DUNGEON_PLAYER_BAD_VERSION; - Exit this function
return false;
FalseContinue after this decision.
- Decision
payload != PLAYER_PAYLOAD_SIZETrue- Execute
result->error = DUNGEON_PLAYER_BAD_FILE_SIZE; - Exit this function
return false;
FalseContinue after this decision.
- Decision
world_checksum != getDungeonWorldChecksum()True- Execute
result->error = DUNGEON_PLAYER_WRONG_DUNGEON; - Exit this function
return false;
FalseContinue after this decision.
- Decision
checksum != playerChecksum(world_checksum, reserved)True- Execute
result->error = DUNGEON_PLAYER_CHECKSUM_FAILED; - Exit this function
return false;
FalseContinue after this decision.
- Execute
player.startedAtEpoch = (uint32_t)reserved[0] | ((uint32_t)reserved[1] << 8) | ((uint32_t)reserved[2] << 16) | ((uint32_t)reserved[3] << 24); - Execute
player.finishedAtEpoch = (uint32_t)reserved[4] | ((uint32_t)reserved[5] << 8) | ((uint32_t)reserved[6] << 16) | ((uint32_t)reserved[7] << 24); - Decision
!validPlayer()True- Execute
result->error = DUNGEON_PLAYER_INVALID_STATE; - Exit this function
return false;
FalseContinue after this decision.
- Execute
result->error = DUNGEON_PLAYER_OK; - Exit this function
return true;
Read original function source
static bool loadPlayer(DungeonPlayerResult *result)
{
memset(result, 0, sizeof(*result));
result->error = DUNGEON_PLAYER_OPEN_READ_FAILED;
FILE *file = fopen(PLAYER_FILE, "rb");
if (file == NULL) return false;
if (fseek(file, 0L, SEEK_END) != 0 || ftell(file) != (long)PLAYER_FILE_SIZE) { result->error = DUNGEON_PLAYER_BAD_FILE_SIZE; fclose(file); return false; }
rewind(file);
uint8_t magic[4], reserved[PLAYER_RESERVED_SIZE];
uint16_t version, payload;
uint32_t checksum, world_checksum;
bool read = fread(magic, 1, sizeof(magic), file) == sizeof(magic);
result->bytes += read ? sizeof(magic) : 0U;
read = read && readU16(file, &version, result) && readU16(file, &payload, result) && readU32(file, &checksum, result) && readU32(file, &world_checksum, result) &&
readByte(file, &player.position.floor, result) && readByte(file, &player.position.x, result) && readByte(file, &player.position.y, result) && readByte(file, &player.enteredFrom, result) &&
readU16(file, &player.level, result) && readU32(file, &player.experience, result) && readU16(file, &player.health, result) && readU16(file, &player.maxHealth, result) &&
readU16(file, &player.attack, result) && readU16(file, &player.defence, result) && readU32(file, &player.gold, result) &&
readU16(file, &player.keys, result) && readU16(file, &player.healthPotions, result) && readU16(file, &player.unspentStatPoints, result) &&
readU32(file, &player.inventoryFlags, result) && readU16(file, &player.shopPurchaseCount, result) && readU32(file, &player.runFlags, result) &&
readU32(file, &player.moves, result) && readU32(file, &player.timePlayedSeconds, result) && fread(reserved, 1, sizeof(reserved), file) == sizeof(reserved);
if (read) result->bytes += sizeof(reserved);
fclose(file);
if (!read || result->bytes != PLAYER_FILE_SIZE) { result->error = DUNGEON_PLAYER_READ_FAILED; return false; }
if (memcmp(magic, "MPLY", sizeof(magic)) != 0) { result->error = DUNGEON_PLAYER_BAD_SIGNATURE; return false; }
if (version != PLAYER_VERSION) { result->error = DUNGEON_PLAYER_BAD_VERSION; return false; }
if (payload != PLAYER_PAYLOAD_SIZE) { result->error = DUNGEON_PLAYER_BAD_FILE_SIZE; return false; }
if (world_checksum != getDungeonWorldChecksum()) { result->error = DUNGEON_PLAYER_WRONG_DUNGEON; return false; }
if (checksum != playerChecksum(world_checksum, reserved)) { result->error = DUNGEON_PLAYER_CHECKSUM_FAILED; return false; }
player.startedAtEpoch = (uint32_t)reserved[0] | ((uint32_t)reserved[1] << 8) | ((uint32_t)reserved[2] << 16) | ((uint32_t)reserved[3] << 24);
player.finishedAtEpoch = (uint32_t)reserved[4] | ((uint32_t)reserved[5] << 8) | ((uint32_t)reserved[6] << 16) | ((uint32_t)reserved[7] << 24);
if (!validPlayer()) { result->error = DUNGEON_PLAYER_INVALID_STATE; return false; }
result->error = DUNGEON_PLAYER_OK;
return true;
}initDungeonPlayer() · source line 175
void initDungeonPlayer(void)
- Execute
memset(&player, 0, sizeof(player));
Read original function source
void initDungeonPlayer(void) { memset(&player, 0, sizeof(player)); }prepareDungeonPlayer() · source line 177
bool prepareDungeonPlayer(DungeonPlayerResult *result)
Calls into this documentation:
- Decision
result == NULLTrue- Exit this function
return false;
FalseContinue after this decision.
- Decision
loadPlayer(result)True- Exit this function
return true;
FalseContinue after this decision.
- Decision
result->error != DUNGEON_PLAYER_OPEN_READ_FAILEDTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
setDefaults(); - Decision
!saveDungeonPlayer(result)True- Exit this function
return false;
FalseContinue after this decision.
- Execute
result->created = true; - Exit this function
return true;
Read original function source
bool prepareDungeonPlayer(DungeonPlayerResult *result)
{
if (result == NULL) return false;
if (loadPlayer(result)) return true;
if (result->error != DUNGEON_PLAYER_OPEN_READ_FAILED) return false;
setDefaults();
if (!saveDungeonPlayer(result)) return false;
result->created = true;
return true;
}resetDungeonPlayer() · source line 188
bool resetDungeonPlayer(DungeonPlayerResult *result)
Calls into this documentation:
- Decision
result == NULLTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
setDefaults(); - Exit this function
return saveDungeonPlayer(result);
Read original function source
bool resetDungeonPlayer(DungeonPlayerResult *result)
{
if (result == NULL) return false;
setDefaults();
return saveDungeonPlayer(result);
}getDungeonPlayerState() · source line 195
DungeonPlayerState *getDungeonPlayerState(void)
- Exit this function
return &player;
Read original function source
DungeonPlayerState *getDungeonPlayerState(void) { return &player; }dungeonExperienceNeeded() · source line 197
uint32_t dungeonExperienceNeeded(uint16_t level)
Calls into this documentation:
- Execute
const DungeonConfig *config = getDungeonConfig(); - Execute
uint32_t required = config->firstLevelXp; - FOR loop
uint16_t current = 1U; current < level; ++currentRepeat body while the loop condition permits; then continue below.
- Execute
const uint64_t next = ((uint64_t)required * (1000U + config->xpGrowthTenthsPercent) + 500U) / 1000U; - Decision
next > UINT32_MAXTrue- Exit this function
return UINT32_MAX;
FalseContinue after this decision.
- Execute
required = (uint32_t)next;
- Exit this function
return required;
Read original function source
uint32_t dungeonExperienceNeeded(uint16_t level)
{
const DungeonConfig *config = getDungeonConfig();
uint32_t required = config->firstLevelXp;
for (uint16_t current = 1U; current < level; ++current) {
const uint64_t next = ((uint64_t)required * (1000U + config->xpGrowthTenthsPercent) + 500U) / 1000U;
if (next > UINT32_MAX) return UINT32_MAX;
required = (uint32_t)next;
}
return required;
}addDungeonPlayerExperience() · source line 209
uint16_t addDungeonPlayerExperience(uint32_t amount)
Calls into this documentation:
- Execute
const DungeonConfig *config = getDungeonConfig(); - Execute
uint64_t experience = (uint64_t)player.experience + amount; - Execute
uint16_t gained = 0U; - WHILE loop
player.level < UINT16_MAX && experience >= dungeonExperienceNeeded(player.level)Repeat body while the loop condition permits; then continue below.
- Execute
experience -= dungeonExperienceNeeded(player.level); - Execute
++player.level; - Execute
player.maxHealth = player.maxHealth <= UINT16_MAX - config->levelHealthGain ? player.maxHealth + config->levelHealthGain : UINT16_MAX; - Execute
player.attack = player.attack <= UINT16_MAX - config->levelAttackGain ? player.attack + config->levelAttackGain : UINT16_MAX; - Execute
player.defence = player.defence <= UINT16_MAX - config->levelDefenceGain ? player.defence + config->levelDefenceGain : UINT16_MAX; - Decision
player.unspentStatPoints < UINT16_MAXTrue- Execute
++player.unspentStatPoints;
FalseContinue after this decision.
- Decision
gained < UINT16_MAXTrue- Execute
++gained;
FalseContinue after this decision.
- Execute
player.experience = experience > UINT32_MAX ? UINT32_MAX : (uint32_t)experience; - Exit this function
return gained;
Read original function source
uint16_t addDungeonPlayerExperience(uint32_t amount)
{
const DungeonConfig *config = getDungeonConfig();
uint64_t experience = (uint64_t)player.experience + amount;
uint16_t gained = 0U;
while (player.level < UINT16_MAX && experience >= dungeonExperienceNeeded(player.level)) {
experience -= dungeonExperienceNeeded(player.level);
++player.level;
player.maxHealth = player.maxHealth <= UINT16_MAX - config->levelHealthGain ? player.maxHealth + config->levelHealthGain : UINT16_MAX;
player.attack = player.attack <= UINT16_MAX - config->levelAttackGain ? player.attack + config->levelAttackGain : UINT16_MAX;
player.defence = player.defence <= UINT16_MAX - config->levelDefenceGain ? player.defence + config->levelDefenceGain : UINT16_MAX;
if (player.unspentStatPoints < UINT16_MAX) ++player.unspentStatPoints;
if (gained < UINT16_MAX) ++gained;
}
player.experience = experience > UINT32_MAX ? UINT32_MAX : (uint32_t)experience;
return gained;
}applyDungeonPlayerStatChoice() · source line 227
bool applyDungeonPlayerStatChoice(uint8_t choice)
Calls into this documentation:
- Decision
player.unspentStatPoints == 0UTrue- Exit this function
return false;
FalseContinue after this decision.
- Execute
const DungeonConfig *config = getDungeonConfig(); - Decision
choice == 0UTrue- Execute
player.maxHealth = player.maxHealth <= UINT16_MAX - config->chosenHealthGain ? player.maxHealth + config->chosenHealthGain : UINT16_MAX;
False- Decision
choice == 1UTrue- Execute
player.attack = player.attack <= UINT16_MAX - config->chosenAttackGain ? player.attack + config->chosenAttackGain : UINT16_MAX;
False- Decision
choice == 2UTrue- Execute
player.defence = player.defence <= UINT16_MAX - config->chosenDefenceGain ? player.defence + config->chosenDefenceGain : UINT16_MAX;
False- Exit this function
return false;
- Execute
--player.unspentStatPoints; - Execute
player.health = player.maxHealth; - Exit this function
return true;
Read original function source
bool applyDungeonPlayerStatChoice(uint8_t choice)
{
if (player.unspentStatPoints == 0U) return false;
const DungeonConfig *config = getDungeonConfig();
if (choice == 0U) player.maxHealth = player.maxHealth <= UINT16_MAX - config->chosenHealthGain ? player.maxHealth + config->chosenHealthGain : UINT16_MAX;
else if (choice == 1U) player.attack = player.attack <= UINT16_MAX - config->chosenAttackGain ? player.attack + config->chosenAttackGain : UINT16_MAX;
else if (choice == 2U) player.defence = player.defence <= UINT16_MAX - config->chosenDefenceGain ? player.defence + config->chosenDefenceGain : UINT16_MAX;
else return false;
--player.unspentStatPoints;
player.health = player.maxHealth;
return true;
}dungeonPlayerErrorText() · source line 240
const char *dungeonPlayerErrorText(DungeonPlayerError error)
- Select a case
errorEnter matching case; follow source fall-through until a break, return or end.
- Case / label
case DUNGEON_PLAYER_OK: - Exit this function
return "OK"; - Case / label
case DUNGEON_PLAYER_OPEN_READ_FAILED: - Exit this function
return "Cannot open player"; - Case / label
case DUNGEON_PLAYER_OPEN_WRITE_FAILED: - Exit this function
return "Cannot create player"; - Case / label
case DUNGEON_PLAYER_READ_FAILED: - Exit this function
return "Player read failed"; - Case / label
case DUNGEON_PLAYER_WRITE_FAILED: - Exit this function
return "Player save failed"; - Case / label
case DUNGEON_PLAYER_BAD_SIGNATURE: - Exit this function
return "Bad player signature"; - Case / label
case DUNGEON_PLAYER_BAD_VERSION: - Exit this function
return "Bad player version"; - Case / label
case DUNGEON_PLAYER_BAD_FILE_SIZE: - Exit this function
return "Bad player file size"; - Case / label
case DUNGEON_PLAYER_CHECKSUM_FAILED: - Exit this function
return "Player checksum failed"; - Case / label
case DUNGEON_PLAYER_WRONG_DUNGEON: - Exit this function
return "Player is for another dungeon"; - Case / label
case DUNGEON_PLAYER_INVALID_POSITION: - Exit this function
return "Bad player position"; - Case / label
case DUNGEON_PLAYER_INVALID_STATE: - Exit this function
return "Bad player state"; - Case / label
default: - Exit this function
return "Player error";
Read original function source
const char *dungeonPlayerErrorText(DungeonPlayerError error)
{
switch (error) {
case DUNGEON_PLAYER_OK: return "OK";
case DUNGEON_PLAYER_OPEN_READ_FAILED: return "Cannot open player";
case DUNGEON_PLAYER_OPEN_WRITE_FAILED: return "Cannot create player";
case DUNGEON_PLAYER_READ_FAILED: return "Player read failed";
case DUNGEON_PLAYER_WRITE_FAILED: return "Player save failed";
case DUNGEON_PLAYER_BAD_SIGNATURE: return "Bad player signature";
case DUNGEON_PLAYER_BAD_VERSION: return "Bad player version";
case DUNGEON_PLAYER_BAD_FILE_SIZE: return "Bad player file size";
case DUNGEON_PLAYER_CHECKSUM_FAILED: return "Player checksum failed";
case DUNGEON_PLAYER_WRONG_DUNGEON: return "Player is for another dungeon";
case DUNGEON_PLAYER_INVALID_POSITION: return "Bad player position";
case DUNGEON_PLAYER_INVALID_STATE: return "Bad player state";
default: return "Player error";
}
}Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "dungeon_player.h"
#include "dungeon_config.h"
#include "dungeon_storage.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#define PLAYER_FILE "/spiffs/dungeon_player.dat"
#define PLAYER_VERSION 6U
#define PLAYER_PAYLOAD_SIZE 114U
#define PLAYER_FILE_SIZE 126U
#define PLAYER_RESERVED_SIZE 64U
#define CHECKSUM_START 2166136261UL
#define CHECKSUM_PRIME 16777619UL
static DungeonPlayerState player;
static void updateChecksum(uint32_t *checksum, uint8_t value) { *checksum = (*checksum ^ value) * CHECKSUM_PRIME; }
static void updateChecksumU16(uint32_t *checksum, uint16_t value) { updateChecksum(checksum, (uint8_t)value); updateChecksum(checksum, (uint8_t)(value >> 8)); }
static void updateChecksumU32(uint32_t *checksum, uint32_t value)
{
updateChecksum(checksum, (uint8_t)value);
updateChecksum(checksum, (uint8_t)(value >> 8));
updateChecksum(checksum, (uint8_t)(value >> 16));
updateChecksum(checksum, (uint8_t)(value >> 24));
}
static bool writeByte(FILE *file, uint8_t value, DungeonPlayerResult *result)
{
if (fwrite(&value, 1, 1, file) != 1U) return false;
++result->bytes;
return true;
}
static bool writeU16(FILE *file, uint16_t value, DungeonPlayerResult *result)
{
return writeByte(file, (uint8_t)value, result) && writeByte(file, (uint8_t)(value >> 8), result);
}
static bool writeU32(FILE *file, uint32_t value, DungeonPlayerResult *result)
{
return writeByte(file, (uint8_t)value, result) && writeByte(file, (uint8_t)(value >> 8), result) &&
writeByte(file, (uint8_t)(value >> 16), result) && writeByte(file, (uint8_t)(value >> 24), result);
}
static bool readByte(FILE *file, uint8_t *value, DungeonPlayerResult *result)
{
if (fread(value, 1, 1, file) != 1U) return false;
++result->bytes;
return true;
}
static bool readU16(FILE *file, uint16_t *value, DungeonPlayerResult *result)
{
uint8_t low, high;
if (!readByte(file, &low, result) || !readByte(file, &high, result)) return false;
*value = (uint16_t)low | ((uint16_t)high << 8);
return true;
}
static bool readU32(FILE *file, uint32_t *value, DungeonPlayerResult *result)
{
uint8_t b0, b1, b2, b3;
if (!readByte(file, &b0, result) || !readByte(file, &b1, result) || !readByte(file, &b2, result) || !readByte(file, &b3, result)) return false;
*value = (uint32_t)b0 | ((uint32_t)b1 << 8) | ((uint32_t)b2 << 16) | ((uint32_t)b3 << 24);
return true;
}
static void setDefaults(void)
{
const DungeonConfig *config = getDungeonConfig();
memset(&player, 0, sizeof(player));
player.level = 1U;
player.health = config->startHealth;
player.maxHealth = config->startHealth;
player.attack = config->startAttack;
player.defence = config->startDefence;
player.position = getDungeonWorld()->entrance;
player.enteredFrom = DUNGEON_EXIT_NONE;
}
static bool validPlayer(void)
{
const DungeonConfig *config = getDungeonConfig();
if (player.position.floor >= DUNGEON_FLOOR_COUNT || player.position.x >= DUNGEON_WIDTH || player.position.y >= DUNGEON_HEIGHT) return false;
const DungeonCell *cell = &getDungeonWorld()->floors[player.position.floor].cells[player.position.y][player.position.x];
return cell->tileType != DUNGEON_TILE_EMPTY && cell->exits != DUNGEON_EXIT_NONE && player.level >= 1U &&
player.maxHealth >= config->startHealth && player.health <= player.maxHealth &&
player.attack >= config->startAttack && player.defence >= config->startDefence;
}
static uint32_t playerChecksum(uint32_t world_checksum, const uint8_t *reserved)
{
uint32_t checksum = CHECKSUM_START;
updateChecksumU32(&checksum, world_checksum);
updateChecksum(&checksum, player.position.floor); updateChecksum(&checksum, player.position.x); updateChecksum(&checksum, player.position.y); updateChecksum(&checksum, player.enteredFrom);
updateChecksumU16(&checksum, player.level); updateChecksumU32(&checksum, player.experience);
updateChecksumU16(&checksum, player.health); updateChecksumU16(&checksum, player.maxHealth); updateChecksumU16(&checksum, player.attack); updateChecksumU16(&checksum, player.defence);
updateChecksumU32(&checksum, player.gold); updateChecksumU16(&checksum, player.keys); updateChecksumU16(&checksum, player.healthPotions); updateChecksumU16(&checksum, player.unspentStatPoints);
updateChecksumU32(&checksum, player.inventoryFlags); updateChecksumU16(&checksum, player.shopPurchaseCount); updateChecksumU32(&checksum, player.runFlags);
updateChecksumU32(&checksum, player.moves); updateChecksumU32(&checksum, player.timePlayedSeconds);
for (unsigned index = 0; index < PLAYER_RESERVED_SIZE; ++index) updateChecksum(&checksum, reserved[index]);
return checksum;
}
bool saveDungeonPlayer(DungeonPlayerResult *result)
{
if (result == NULL) return false;
memset(result, 0, sizeof(*result));
result->error = DUNGEON_PLAYER_OPEN_WRITE_FAILED;
errno = 0;
FILE *file = fopen(PLAYER_FILE, "wb");
if (file == NULL) { result->errno_value = errno; return false; }
const uint8_t magic[] = { 'M', 'P', 'L', 'Y' };
uint8_t reserved[PLAYER_RESERVED_SIZE] = {0};
reserved[0] = (uint8_t)player.startedAtEpoch; reserved[1] = (uint8_t)(player.startedAtEpoch >> 8);
reserved[2] = (uint8_t)(player.startedAtEpoch >> 16); reserved[3] = (uint8_t)(player.startedAtEpoch >> 24);
reserved[4] = (uint8_t)player.finishedAtEpoch; reserved[5] = (uint8_t)(player.finishedAtEpoch >> 8);
reserved[6] = (uint8_t)(player.finishedAtEpoch >> 16); reserved[7] = (uint8_t)(player.finishedAtEpoch >> 24);
const uint32_t world_checksum = getDungeonWorldChecksum();
const uint32_t checksum = playerChecksum(world_checksum, reserved);
bool written = fwrite(magic, 1, sizeof(magic), file) == sizeof(magic);
result->bytes += written ? sizeof(magic) : 0U;
written = written && writeU16(file, PLAYER_VERSION, result) && writeU16(file, PLAYER_PAYLOAD_SIZE, result) &&
writeU32(file, checksum, result) && writeU32(file, world_checksum, result) &&
writeByte(file, player.position.floor, result) && writeByte(file, player.position.x, result) && writeByte(file, player.position.y, result) && writeByte(file, player.enteredFrom, result) &&
writeU16(file, player.level, result) && writeU32(file, player.experience, result) && writeU16(file, player.health, result) && writeU16(file, player.maxHealth, result) &&
writeU16(file, player.attack, result) && writeU16(file, player.defence, result) && writeU32(file, player.gold, result) &&
writeU16(file, player.keys, result) && writeU16(file, player.healthPotions, result) && writeU16(file, player.unspentStatPoints, result) &&
writeU32(file, player.inventoryFlags, result) && writeU16(file, player.shopPurchaseCount, result) && writeU32(file, player.runFlags, result) &&
writeU32(file, player.moves, result) && writeU32(file, player.timePlayedSeconds, result) &&
fwrite(reserved, 1, sizeof(reserved), file) == sizeof(reserved);
if (written) result->bytes += sizeof(reserved);
const int close_result = fclose(file);
if (!written || close_result != 0 || result->bytes != PLAYER_FILE_SIZE) { result->error = DUNGEON_PLAYER_WRITE_FAILED; result->errno_value = errno; return false; }
result->error = DUNGEON_PLAYER_OK;
return true;
}
static bool loadPlayer(DungeonPlayerResult *result)
{
memset(result, 0, sizeof(*result));
result->error = DUNGEON_PLAYER_OPEN_READ_FAILED;
FILE *file = fopen(PLAYER_FILE, "rb");
if (file == NULL) return false;
if (fseek(file, 0L, SEEK_END) != 0 || ftell(file) != (long)PLAYER_FILE_SIZE) { result->error = DUNGEON_PLAYER_BAD_FILE_SIZE; fclose(file); return false; }
rewind(file);
uint8_t magic[4], reserved[PLAYER_RESERVED_SIZE];
uint16_t version, payload;
uint32_t checksum, world_checksum;
bool read = fread(magic, 1, sizeof(magic), file) == sizeof(magic);
result->bytes += read ? sizeof(magic) : 0U;
read = read && readU16(file, &version, result) && readU16(file, &payload, result) && readU32(file, &checksum, result) && readU32(file, &world_checksum, result) &&
readByte(file, &player.position.floor, result) && readByte(file, &player.position.x, result) && readByte(file, &player.position.y, result) && readByte(file, &player.enteredFrom, result) &&
readU16(file, &player.level, result) && readU32(file, &player.experience, result) && readU16(file, &player.health, result) && readU16(file, &player.maxHealth, result) &&
readU16(file, &player.attack, result) && readU16(file, &player.defence, result) && readU32(file, &player.gold, result) &&
readU16(file, &player.keys, result) && readU16(file, &player.healthPotions, result) && readU16(file, &player.unspentStatPoints, result) &&
readU32(file, &player.inventoryFlags, result) && readU16(file, &player.shopPurchaseCount, result) && readU32(file, &player.runFlags, result) &&
readU32(file, &player.moves, result) && readU32(file, &player.timePlayedSeconds, result) && fread(reserved, 1, sizeof(reserved), file) == sizeof(reserved);
if (read) result->bytes += sizeof(reserved);
fclose(file);
if (!read || result->bytes != PLAYER_FILE_SIZE) { result->error = DUNGEON_PLAYER_READ_FAILED; return false; }
if (memcmp(magic, "MPLY", sizeof(magic)) != 0) { result->error = DUNGEON_PLAYER_BAD_SIGNATURE; return false; }
if (version != PLAYER_VERSION) { result->error = DUNGEON_PLAYER_BAD_VERSION; return false; }
if (payload != PLAYER_PAYLOAD_SIZE) { result->error = DUNGEON_PLAYER_BAD_FILE_SIZE; return false; }
if (world_checksum != getDungeonWorldChecksum()) { result->error = DUNGEON_PLAYER_WRONG_DUNGEON; return false; }
if (checksum != playerChecksum(world_checksum, reserved)) { result->error = DUNGEON_PLAYER_CHECKSUM_FAILED; return false; }
player.startedAtEpoch = (uint32_t)reserved[0] | ((uint32_t)reserved[1] << 8) | ((uint32_t)reserved[2] << 16) | ((uint32_t)reserved[3] << 24);
player.finishedAtEpoch = (uint32_t)reserved[4] | ((uint32_t)reserved[5] << 8) | ((uint32_t)reserved[6] << 16) | ((uint32_t)reserved[7] << 24);
if (!validPlayer()) { result->error = DUNGEON_PLAYER_INVALID_STATE; return false; }
result->error = DUNGEON_PLAYER_OK;
return true;
}
void initDungeonPlayer(void) { memset(&player, 0, sizeof(player)); }
bool prepareDungeonPlayer(DungeonPlayerResult *result)
{
if (result == NULL) return false;
if (loadPlayer(result)) return true;
if (result->error != DUNGEON_PLAYER_OPEN_READ_FAILED) return false;
setDefaults();
if (!saveDungeonPlayer(result)) return false;
result->created = true;
return true;
}
bool resetDungeonPlayer(DungeonPlayerResult *result)
{
if (result == NULL) return false;
setDefaults();
return saveDungeonPlayer(result);
}
DungeonPlayerState *getDungeonPlayerState(void) { return &player; }
uint32_t dungeonExperienceNeeded(uint16_t level)
{
const DungeonConfig *config = getDungeonConfig();
uint32_t required = config->firstLevelXp;
for (uint16_t current = 1U; current < level; ++current) {
const uint64_t next = ((uint64_t)required * (1000U + config->xpGrowthTenthsPercent) + 500U) / 1000U;
if (next > UINT32_MAX) return UINT32_MAX;
required = (uint32_t)next;
}
return required;
}
uint16_t addDungeonPlayerExperience(uint32_t amount)
{
const DungeonConfig *config = getDungeonConfig();
uint64_t experience = (uint64_t)player.experience + amount;
uint16_t gained = 0U;
while (player.level < UINT16_MAX && experience >= dungeonExperienceNeeded(player.level)) {
experience -= dungeonExperienceNeeded(player.level);
++player.level;
player.maxHealth = player.maxHealth <= UINT16_MAX - config->levelHealthGain ? player.maxHealth + config->levelHealthGain : UINT16_MAX;
player.attack = player.attack <= UINT16_MAX - config->levelAttackGain ? player.attack + config->levelAttackGain : UINT16_MAX;
player.defence = player.defence <= UINT16_MAX - config->levelDefenceGain ? player.defence + config->levelDefenceGain : UINT16_MAX;
if (player.unspentStatPoints < UINT16_MAX) ++player.unspentStatPoints;
if (gained < UINT16_MAX) ++gained;
}
player.experience = experience > UINT32_MAX ? UINT32_MAX : (uint32_t)experience;
return gained;
}
bool applyDungeonPlayerStatChoice(uint8_t choice)
{
if (player.unspentStatPoints == 0U) return false;
const DungeonConfig *config = getDungeonConfig();
if (choice == 0U) player.maxHealth = player.maxHealth <= UINT16_MAX - config->chosenHealthGain ? player.maxHealth + config->chosenHealthGain : UINT16_MAX;
else if (choice == 1U) player.attack = player.attack <= UINT16_MAX - config->chosenAttackGain ? player.attack + config->chosenAttackGain : UINT16_MAX;
else if (choice == 2U) player.defence = player.defence <= UINT16_MAX - config->chosenDefenceGain ? player.defence + config->chosenDefenceGain : UINT16_MAX;
else return false;
--player.unspentStatPoints;
player.health = player.maxHealth;
return true;
}
const char *dungeonPlayerErrorText(DungeonPlayerError error)
{
switch (error) {
case DUNGEON_PLAYER_OK: return "OK";
case DUNGEON_PLAYER_OPEN_READ_FAILED: return "Cannot open player";
case DUNGEON_PLAYER_OPEN_WRITE_FAILED: return "Cannot create player";
case DUNGEON_PLAYER_READ_FAILED: return "Player read failed";
case DUNGEON_PLAYER_WRITE_FAILED: return "Player save failed";
case DUNGEON_PLAYER_BAD_SIGNATURE: return "Bad player signature";
case DUNGEON_PLAYER_BAD_VERSION: return "Bad player version";
case DUNGEON_PLAYER_BAD_FILE_SIZE: return "Bad player file size";
case DUNGEON_PLAYER_CHECKSUM_FAILED: return "Player checksum failed";
case DUNGEON_PLAYER_WRONG_DUNGEON: return "Player is for another dungeon";
case DUNGEON_PLAYER_INVALID_POSITION: return "Bad player position";
case DUNGEON_PLAYER_INVALID_STATE: return "Bad player state";
default: return "Player error";
}
}