Move global item collected-state, deadzone, and story flags into save file
Adds three new pieces of save-file state, all following the same shape: the save file is the single source of truth, not a separate live runtime copy that gets synced in/out. - globalitemstore.h/.c: per-global-entity-ID "collected" flags (savefile_t.globalItemCollected), so a global item entity's init callback can check whether it was already picked up in a prior session without needing to keep the entity itself alive to remember that. - Gamepad deadzone: removed input_t.deadzone entirely. The setting UI and every platform's actual deadzone-applying code (inputGetDeadzoneDolphin/ SDL2, previously hardcoded per-platform literals that the settings menu didn't actually affect) now read savefile_t.deadzone directly via saveGet(SAVE_ACTIVE_SLOT). Default lives in savefile.h (SAVE_DEADZONE_DEFAULT), stamped onto every slot in saveInit(). - Story flags: STORY_FLAG_VALUES (a live, codegen-initialized array) is replaced by savefile_t.storyFlags, read/written via the existing storyFlagGet()/storyFlagSet() call sites (now macros/functions over the active save file instead of a separate array). tools/story.py now generates STORY_FLAG_DEFAULTS (const) instead; storyFlagInitDefaults() stamps those onto a save the first time it's used (file->exists false), called from rpgInit(). Added SAVE_ACTIVE_SLOT (0) to savefile.h as the one shared "which slot is actually being played" constant, replacing three different local/implicit 0s (uigamemenu.c, rpg.c, and now the settings/input call sites). Verified round-trip on Linux (all three together in one save/load cycle); both Linux and PSP build clean.
This commit is contained in:
@@ -17,7 +17,6 @@ input_t INPUT;
|
||||
|
||||
errorret_t inputInit(void) {
|
||||
memoryZero(&INPUT, sizeof(input_t));
|
||||
INPUT.deadzone = INPUT_DEADZONE_DEFAULT;
|
||||
|
||||
for(uint8_t i = 0; i < INPUT_ACTION_COUNT; i++) {
|
||||
INPUT.actions[i].action = (inputaction_t)i;
|
||||
|
||||
@@ -12,15 +12,11 @@
|
||||
|
||||
#define INPUT_LISTENER_PRESSED_MAX 16
|
||||
#define INPUT_LISTENER_RELEASED_MAX INPUT_LISTENER_PRESSED_MAX
|
||||
#define INPUT_DEADZONE_DEFAULT 0.1f
|
||||
|
||||
typedef struct {
|
||||
inputactiondata_t actions[INPUT_ACTION_COUNT];
|
||||
|
||||
inputplatform_t platform;
|
||||
|
||||
/** User-configured gamepad axis deadzone (0.0f to 1.0f). */
|
||||
float_t deadzone;
|
||||
} input_t;
|
||||
|
||||
extern input_t INPUT;
|
||||
|
||||
@@ -6,4 +6,5 @@
|
||||
# Sources
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
globalitemstore.c
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "globalitemstore.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
bool_t globalItemStoreIsCollected(
|
||||
const savefile_t *file, const entityglobalid_t id
|
||||
) {
|
||||
assertNotNull(file, "Save file cannot be NULL");
|
||||
assertTrue(id < SAVE_GLOBAL_ITEM_COUNT_MAX, "Global item ID out of range");
|
||||
return file->globalItemCollected[id];
|
||||
}
|
||||
|
||||
void globalItemStoreSetCollected(
|
||||
savefile_t *file, const entityglobalid_t id, const bool_t collected
|
||||
) {
|
||||
assertNotNull(file, "Save file cannot be NULL");
|
||||
assertTrue(id < SAVE_GLOBAL_ITEM_COUNT_MAX, "Global item ID out of range");
|
||||
file->globalItemCollected[id] = collected;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "save/savefile.h"
|
||||
#include "rpg/entity/entity.h"
|
||||
|
||||
/**
|
||||
* Checks whether the global entity with the given ID has already been
|
||||
* marked collected in the given save file's data - e.g. so a global item
|
||||
* entity's init callback (see rpg/entity/global/entitygloballist.h) can
|
||||
* skip spawning itself if the player already picked it up in a prior
|
||||
* session, without needing to keep the entity itself alive to remember
|
||||
* that (which would need render/collision special-casing - this doesn't).
|
||||
*
|
||||
* @param file The save file to check.
|
||||
* @param id The global entity ID to check.
|
||||
* @return True if already marked collected.
|
||||
*/
|
||||
bool_t globalItemStoreIsCollected(
|
||||
const savefile_t *file, const entityglobalid_t id
|
||||
);
|
||||
|
||||
/**
|
||||
* Marks the global entity with the given ID as collected (or not) in the
|
||||
* given save file's data. Does not itself write the save to disk - call
|
||||
* saveWrite() separately once ready to persist it.
|
||||
*
|
||||
* @param file The save file to write into.
|
||||
* @param id The global entity ID to mark.
|
||||
* @param collected The new collected state.
|
||||
*/
|
||||
void globalItemStoreSetCollected(
|
||||
savefile_t *file, const entityglobalid_t id, const bool_t collected
|
||||
);
|
||||
+8
-2
@@ -22,6 +22,7 @@
|
||||
#include "error/error.h"
|
||||
|
||||
#include "ui/rpg/uiemoji.h"
|
||||
#include "rpg/story/storyflag.h"
|
||||
|
||||
static void rpgTestSaveComplete(errorret_t result, void *user) {
|
||||
if(errorIsNotOk(result)) errorCatch(errorPrint(result));
|
||||
@@ -31,6 +32,11 @@ errorret_t rpgInit(void) {
|
||||
memoryZero(ENTITIES, sizeof(ENTITIES));
|
||||
memoryZero(MAP_AREAS, sizeof(MAP_AREAS));
|
||||
|
||||
// Must run before any code reads a story flag - stamps CSV-defined
|
||||
// defaults onto the active save slot if it's never actually been
|
||||
// loaded from disk yet.
|
||||
storyFlagInitDefaults(saveGet(SAVE_ACTIVE_SLOT));
|
||||
|
||||
backpackInit();
|
||||
partyInit();
|
||||
cutsceneSystemInit();
|
||||
@@ -61,9 +67,9 @@ errorret_t rpgInit(void) {
|
||||
// header/version. Remove once there's an actual name-entry flow. On PSP
|
||||
// this shows the real native save dialog every boot - expected while
|
||||
// testing that path, not something to ship as-is.
|
||||
savefile_t *saveFile = saveGet(0);
|
||||
savefile_t *saveFile = saveGet(SAVE_ACTIVE_SLOT);
|
||||
stringCopy(saveFile->playerName, "Dusk", SAVE_PLAYER_NAME_MAX);
|
||||
saveWrite(0, rpgTestSaveComplete, NULL);
|
||||
saveWrite(SAVE_ACTIVE_SLOT, rpgTestSaveComplete, NULL);
|
||||
|
||||
// All Good!
|
||||
errorOk();
|
||||
|
||||
@@ -10,5 +10,17 @@
|
||||
|
||||
void storyFlagSet(const storyflag_t flag, const storyflagvalue_t value) {
|
||||
assertTrue(flag > STORY_FLAG_NULL && flag < STORY_FLAG_COUNT, "Bad Flag");
|
||||
STORY_FLAG_VALUES[flag] = value;
|
||||
saveGet(SAVE_ACTIVE_SLOT)->storyFlags[flag] = value;
|
||||
}
|
||||
|
||||
void storyFlagInitDefaults(savefile_t *file) {
|
||||
assertNotNull(file, "Save file cannot be NULL");
|
||||
if(file->exists) return;
|
||||
assertTrue(
|
||||
STORY_FLAG_COUNT <= SAVE_STORY_FLAG_COUNT_MAX,
|
||||
"Too many story flags for the save format - bump SAVE_STORY_FLAG_COUNT_MAX"
|
||||
);
|
||||
for(storyflag_t i = 0; i < STORY_FLAG_COUNT; i++) {
|
||||
file->storyFlags[i] = STORY_FLAG_DEFAULTS[i];
|
||||
}
|
||||
}
|
||||
@@ -7,19 +7,34 @@
|
||||
|
||||
#pragma once
|
||||
#include "rpg/story/storyflagvalue.h"
|
||||
#include "save/save.h"
|
||||
|
||||
/**
|
||||
* Gets the value of a story flag.
|
||||
* Gets the value of a story flag. Reads directly from the active save
|
||||
* file (see SAVE_ACTIVE_SLOT) - flag values have no separate live copy.
|
||||
*
|
||||
* @param flag The story flag to get.
|
||||
* @return The value of the story flag.
|
||||
*/
|
||||
#define storyFlagGet(flag) (STORY_FLAG_VALUES[(flag)])
|
||||
#define storyFlagGet(flag) (saveGet(SAVE_ACTIVE_SLOT)->storyFlags[(flag)])
|
||||
|
||||
/**
|
||||
* Sets the value of a story flag.
|
||||
* Sets the value of a story flag, directly in the active save file (see
|
||||
* SAVE_ACTIVE_SLOT). Does not itself write the save to disk - call
|
||||
* saveWrite() separately once ready to persist it.
|
||||
*
|
||||
* @param flag The story flag to set.
|
||||
* @param value The value to set the story flag to.
|
||||
*/
|
||||
void storyFlagSet(const storyflag_t flag, const storyflagvalue_t value);
|
||||
|
||||
/**
|
||||
* Stamps each story flag's CSV-defined default (STORY_FLAG_DEFAULTS) onto
|
||||
* the given save file, but only if it hasn't actually been loaded from
|
||||
* disk yet (file->exists is false) - otherwise leaves already-played
|
||||
* progress alone. Call once, e.g. during rpgInit(), before any gameplay
|
||||
* code reads a story flag.
|
||||
*
|
||||
* @param file The save file to stamp defaults onto.
|
||||
*/
|
||||
void storyFlagInitDefaults(savefile_t *file);
|
||||
|
||||
@@ -16,6 +16,13 @@ save_t SAVE;
|
||||
errorret_t saveInit(void) {
|
||||
memoryZero(&SAVE, sizeof(save_t));
|
||||
|
||||
// Establishes the default for a slot that hasn't actually been loaded
|
||||
// from disk yet - saveLoad() overwrites this the moment a real file is
|
||||
// found, so this only matters for a brand new save.
|
||||
for(uint8_t i = 0; i < SAVE_FILE_COUNT_MAX; i++) {
|
||||
SAVE.files[i].deadzone = SAVE_DEADZONE_DEFAULT;
|
||||
}
|
||||
|
||||
#ifdef saveInitPlatform
|
||||
// A missing/unreachable save medium is expected, recoverable state,
|
||||
// not a reason to fail booting the whole game - log it and carry on
|
||||
|
||||
@@ -20,9 +20,44 @@
|
||||
/** Maximum number of independent save slots supported. */
|
||||
#define SAVE_FILE_COUNT_MAX 3
|
||||
|
||||
/**
|
||||
* The save slot actually used for gameplay right now - there's no slot
|
||||
* select/multi-save UX yet (SAVE_FILE_COUNT_MAX > 1 exists for later), so
|
||||
* every part of the game that needs "the" save file (settings, the game
|
||||
* menu's Save button, etc.) reads/writes this one slot.
|
||||
*/
|
||||
#define SAVE_ACTIVE_SLOT 0
|
||||
|
||||
/** Maximum length of a saved player name, including the null terminator. */
|
||||
#define SAVE_PLAYER_NAME_MAX 32
|
||||
|
||||
/**
|
||||
* Maximum number of global entities whose "collected" state can be
|
||||
* tracked - see rpg/entity/global/globalitemstore.h. Bounded/fixed here
|
||||
* rather than tied to ENTITY_GLOBAL_LIST_COUNT, since savefile.h is a
|
||||
* leaf header with no dependency on the entity system (and no reason to
|
||||
* take one just for a size constant).
|
||||
*/
|
||||
#define SAVE_GLOBAL_ITEM_COUNT_MAX 64
|
||||
|
||||
/**
|
||||
* Default gamepad deadzone for a save slot that's never actually been
|
||||
* loaded from disk yet (see saveInit(), which stamps this onto every
|
||||
* slot up front) - defined here, rather than by the input system, since
|
||||
* the save file is now the single source of truth for this value (see
|
||||
* savefile_t.deadzone) - nothing else stores or defaults it.
|
||||
*/
|
||||
#define SAVE_DEADZONE_DEFAULT 0.1f
|
||||
|
||||
/**
|
||||
* Maximum number of story flags the save format can hold - see
|
||||
* rpg/story/storyflag.h. Bounded/fixed here (with real headroom over the
|
||||
* current flag count) rather than tied to STORY_FLAG_COUNT, since
|
||||
* savefile.h is a leaf header with no dependency on generated story
|
||||
* content, matching SAVE_GLOBAL_ITEM_COUNT_MAX's reasoning.
|
||||
*/
|
||||
#define SAVE_STORY_FLAG_COUNT_MAX 128
|
||||
|
||||
typedef struct {
|
||||
/** Magic header bytes read from the file; must equal SAVE_FILE_HEADER. */
|
||||
char_t header[SAVE_FILE_HEADER_SIZE];
|
||||
@@ -32,6 +67,20 @@ typedef struct {
|
||||
bool_t exists;
|
||||
/** The player's saved name. */
|
||||
char_t playerName[SAVE_PLAYER_NAME_MAX];
|
||||
/** Per-global-ID "already collected" flags - see globalitemstore.h. */
|
||||
bool_t globalItemCollected[SAVE_GLOBAL_ITEM_COUNT_MAX];
|
||||
/**
|
||||
* User-configured gamepad deadzone (0.0f-1.0f) - the save file is the
|
||||
* only place this lives; read it directly via saveGet(SAVE_ACTIVE_SLOT)
|
||||
* ->deadzone rather than caching it anywhere else.
|
||||
*/
|
||||
float_t deadzone;
|
||||
/**
|
||||
* Story flag values, indexed by storyflag_t - the save file is the only
|
||||
* place these live; read/write via storyFlagGet()/storyFlagSet() (see
|
||||
* rpg/story/storyflag.h), not directly.
|
||||
*/
|
||||
uint8_t storyFlags[SAVE_STORY_FLAG_COUNT_MAX];
|
||||
} savefile_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -331,6 +331,13 @@ errorret_t saveFileLoad(savestream_t *stream, savefile_t *file) {
|
||||
saveFileReadHeader(stream, file->header);
|
||||
saveFileReadVersion(stream, &file->version);
|
||||
saveFileReadString(stream, file->playerName, SAVE_PLAYER_NAME_MAX);
|
||||
for(size_t i = 0; i < SAVE_GLOBAL_ITEM_COUNT_MAX; i++) {
|
||||
saveFileReadBool(stream, &file->globalItemCollected[i]);
|
||||
}
|
||||
saveFileReadFloat(stream, &file->deadzone);
|
||||
for(size_t i = 0; i < SAVE_STORY_FLAG_COUNT_MAX; i++) {
|
||||
saveFileReadUInt8(stream, &file->storyFlags[i]);
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -338,5 +345,12 @@ errorret_t saveFileWrite(savestream_t *stream, savefile_t *file) {
|
||||
saveFileWriteHeader(stream, file->header);
|
||||
saveFileWriteVersion(stream, &file->version);
|
||||
saveFileWriteString(stream, file->playerName, SAVE_PLAYER_NAME_MAX);
|
||||
for(size_t i = 0; i < SAVE_GLOBAL_ITEM_COUNT_MAX; i++) {
|
||||
saveFileWriteBool(stream, &file->globalItemCollected[i]);
|
||||
}
|
||||
saveFileWriteFloat(stream, &file->deadzone);
|
||||
for(size_t i = 0; i < SAVE_STORY_FLAG_COUNT_MAX; i++) {
|
||||
saveFileWriteUInt8(stream, &file->storyFlags[i]);
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -26,10 +26,6 @@
|
||||
#define UI_GAME_MENU_INDEX_SETTINGS 2
|
||||
#define UI_GAME_MENU_INDEX_SAVE 3
|
||||
|
||||
// Only one save slot is exposed through this menu for now - there is no
|
||||
// slot-select UI yet, and SAVE_FILE_COUNT_MAX > 1 exists for later.
|
||||
#define UI_GAME_MENU_SAVE_SLOT 0
|
||||
|
||||
static void uiGameMenuSaveWriteComplete(errorret_t result, void *user) {
|
||||
if(errorIsNotOk(result)) {
|
||||
// Generously sized - stringFormat asserts (crashes) rather than
|
||||
@@ -53,7 +49,7 @@ static void uiGameMenuSaveCreateConfirmed(const bool_t confirmed, void *user) {
|
||||
return;
|
||||
}
|
||||
|
||||
saveWrite(UI_GAME_MENU_SAVE_SLOT, uiGameMenuSaveWriteComplete, NULL);
|
||||
saveWrite(SAVE_ACTIVE_SLOT, uiGameMenuSaveWriteComplete, NULL);
|
||||
}
|
||||
|
||||
// Determines whether there's actually save data to overwrite (not just
|
||||
@@ -72,8 +68,8 @@ static void uiGameMenuSaveCheckComplete(errorret_t result, void *user) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(saveExists(UI_GAME_MENU_SAVE_SLOT)) {
|
||||
saveWrite(UI_GAME_MENU_SAVE_SLOT, uiGameMenuSaveWriteComplete, NULL);
|
||||
if(saveExists(SAVE_ACTIVE_SLOT)) {
|
||||
saveWrite(SAVE_ACTIVE_SLOT, uiGameMenuSaveWriteComplete, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -91,7 +87,7 @@ static void uiGameMenuSave(void) {
|
||||
}
|
||||
if(saveIsBusy()) return;// A save/load dialog (e.g. on PSP) is already up.
|
||||
|
||||
saveLoad(UI_GAME_MENU_SAVE_SLOT, uiGameMenuSaveCheckComplete, NULL);
|
||||
saveLoad(SAVE_ACTIVE_SLOT, uiGameMenuSaveCheckComplete, NULL);
|
||||
}
|
||||
|
||||
uigamemenu_t UI_GAME_MENU;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "util/memory.h"
|
||||
#include "locale/localemanager.h"
|
||||
#include "asset/loader/locale/assetlocaleloader.h"
|
||||
#include "input/input.h"
|
||||
#include "save/save.h"
|
||||
|
||||
void uiSettingsInputSelected(
|
||||
const uimenu_t *menu,
|
||||
@@ -39,7 +39,7 @@ errorret_t uiSettingsInputInit(uisettingsdata_t *data) {
|
||||
UI_SETTINGS_INPUT_LABEL_MAX
|
||||
));
|
||||
MENU_SLIDER_FLOAT(
|
||||
input->deadzoneLabel, INPUT_DEADZONE_DEFAULT, 0.0f, 1.0f, 0.05f
|
||||
input->deadzoneLabel, SAVE_DEADZONE_DEFAULT, 0.0f, 1.0f, 0.05f
|
||||
);
|
||||
#else
|
||||
MENU_LABEL("No input settings yet");
|
||||
@@ -55,14 +55,14 @@ void uiSettingsInputLoad(void) {
|
||||
#ifdef DUSK_INPUT_GAMEPAD
|
||||
uiSliderSetFloat(
|
||||
&UI_SETTINGS.data.input.items[UI_SETTINGS_INPUT_INDEX_DEADZONE].slider,
|
||||
INPUT.deadzone
|
||||
saveGet(SAVE_ACTIVE_SLOT)->deadzone
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
void uiSettingsInputApply(void) {
|
||||
#ifdef DUSK_INPUT_GAMEPAD
|
||||
INPUT.deadzone = uiSliderGetFloat(
|
||||
saveGet(SAVE_ACTIVE_SLOT)->deadzone = uiSliderGetFloat(
|
||||
&UI_SETTINGS.data.input.items[UI_SETTINGS_INPUT_INDEX_DEADZONE].slider
|
||||
);
|
||||
#endif
|
||||
@@ -73,7 +73,7 @@ bool_t uiSettingsInputHasChanges(void) {
|
||||
#ifdef DUSK_INPUT_GAMEPAD
|
||||
if(uiSliderGetFloat(
|
||||
&UI_SETTINGS.data.input.items[UI_SETTINGS_INPUT_INDEX_DEADZONE].slider
|
||||
) != INPUT.deadzone) return true;
|
||||
) != saveGet(SAVE_ACTIVE_SLOT)->deadzone) return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "assert/assert.h"
|
||||
#include "log/log.h"
|
||||
#include "util/string.h"
|
||||
#include "save/save.h"
|
||||
|
||||
inputbuttondata_t INPUT_BUTTON_DATA[] = {
|
||||
#ifdef DUSK_INPUT_GAMEPAD
|
||||
@@ -187,5 +188,5 @@ float_t inputButtonGetValueDolphin(const inputbutton_t button) {
|
||||
}
|
||||
|
||||
float_t inputGetDeadzoneDolphin(const inputbutton_t button) {
|
||||
return 0.2f;
|
||||
return saveGet(SAVE_ACTIVE_SLOT)->deadzone;
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "input/input.h"
|
||||
#include "save/save.h"
|
||||
|
||||
inputbuttondata_t INPUT_BUTTON_DATA[] = {
|
||||
#ifdef DUSK_INPUT_GAMEPAD
|
||||
@@ -547,5 +548,5 @@ errorret_t inputInitLinux(void) {
|
||||
}
|
||||
|
||||
float_t inputGetDeadzoneSDL2(const inputbutton_t button) {
|
||||
return 0.17f;
|
||||
return saveGet(SAVE_ACTIVE_SLOT)->deadzone;
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "input/input.h"
|
||||
#include "save/save.h"
|
||||
|
||||
// #define INPUT_PSP_GAMEPAD_BUTTON_ACCEPT INPUT_SDL2_GAMEPAD_BUTTON_CUSTOM
|
||||
// #define INPUT_PSP_GAMEPAD_BUTTON_CANCEL INPUT_SDL2_GAMEPAD_BUTTON_CUSTOM
|
||||
@@ -94,5 +95,5 @@ errorret_t inputInitPSP(void) {
|
||||
}
|
||||
|
||||
float_t inputGetDeadzoneSDL2(const inputbutton_t button) {
|
||||
return 0.2f;
|
||||
return saveGet(SAVE_ACTIVE_SLOT)->deadzone;
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "input/input.h"
|
||||
#include "save/save.h"
|
||||
|
||||
inputbuttondata_t INPUT_BUTTON_DATA[] = {
|
||||
{ .name = "triangle", {
|
||||
@@ -83,5 +84,5 @@ inputbuttondata_t INPUT_BUTTON_DATA[] = {
|
||||
};
|
||||
|
||||
float_t inputGetDeadzoneSDL2(const inputbutton_t button) {
|
||||
return 0.17f;
|
||||
return saveGet(SAVE_ACTIVE_SLOT)->deadzone;
|
||||
}
|
||||
|
||||
+5
-1
@@ -38,7 +38,11 @@ out += [
|
||||
" STORY_FLAG_COUNT",
|
||||
"} storyflag_t;",
|
||||
"",
|
||||
"static storyflagvalue_t STORY_FLAG_VALUES[STORY_FLAG_COUNT] = {",
|
||||
"// Stamped onto a save file's storyFlags the first time it's used (see",
|
||||
"// storyFlagInitDefaults()) - not a live value array. Live flag state",
|
||||
"// lives entirely in the save file (savefile_t.storyFlags), read/written",
|
||||
"// via storyFlagGet()/storyFlagSet() - see storyflag.h.",
|
||||
"static const storyflagvalue_t STORY_FLAG_DEFAULTS[STORY_FLAG_COUNT] = {",
|
||||
]
|
||||
for flag in flags:
|
||||
out.append(f" [{flag_enum(flag['id'])}] = {flag['initial']},")
|
||||
|
||||
Reference in New Issue
Block a user