diff --git a/assets/story/storyflag.jsonc b/assets/story/storyflag.jsonc index 6a487f71..f2738e1b 100644 --- a/assets/story/storyflag.jsonc +++ b/assets/story/storyflag.jsonc @@ -1,5 +1,10 @@ { "flags": { - "test": { "description": "Test flag for debugging purposes", "initial": true } + "test": { + "initial": true + }, + "test2": { + "initial": 1 + } } } diff --git a/src/dusk/rpg/cutscene/item/CMakeLists.txt b/src/dusk/rpg/cutscene/item/CMakeLists.txt index 9e6cf392..42868c1b 100755 --- a/src/dusk/rpg/cutscene/item/CMakeLists.txt +++ b/src/dusk/rpg/cutscene/item/CMakeLists.txt @@ -20,3 +20,4 @@ add_subdirectory(battle) add_subdirectory(save) add_subdirectory(audio) add_subdirectory(json) +add_subdirectory(story) diff --git a/src/dusk/rpg/cutscene/item/cutsceneitem.c b/src/dusk/rpg/cutscene/item/cutsceneitem.c index ece89a4a..130b0757 100644 --- a/src/dusk/rpg/cutscene/item/cutsceneitem.c +++ b/src/dusk/rpg/cutscene/item/cutsceneitem.c @@ -339,6 +339,19 @@ cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = { .update = cutsceneInsertUpdate, .jsonName = "INSERT", .load = cutsceneInsertLoad + }, + + [CUTSCENE_ITEM_TYPE_STORY_SET_FLAG] = { + .init = cutsceneStorySetFlagStart, + .update = cutsceneStorySetFlagUpdate, + .jsonName = "STORY_SET_FLAG", + .load = cutsceneStorySetFlagLoad + }, + + [CUTSCENE_ITEM_TYPE_STORY_WAIT_FLAG] = { + .update = cutsceneStoryWaitFlagUpdate, + .jsonName = "STORY_WAIT_FLAG", + .load = cutsceneStoryWaitFlagLoad } }; diff --git a/src/dusk/rpg/cutscene/item/cutsceneitem.h b/src/dusk/rpg/cutscene/item/cutsceneitem.h index 215c12f8..5d04d431 100644 --- a/src/dusk/rpg/cutscene/item/cutsceneitem.h +++ b/src/dusk/rpg/cutscene/item/cutsceneitem.h @@ -55,6 +55,8 @@ #include "audio/cutsceneaudiosetpan.h" #include "audio/cutsceneaudiosetloop.h" #include "audio/cutsceneaudioset.h" +#include "story/cutscenestorysetflag.h" +#include "story/cutscenestorywaitflag.h" typedef struct cutscene_s cutscene_t; @@ -138,6 +140,8 @@ struct cutsceneitem_s { cutsceneaudiosetloop_t audioSetLoop; cutsceneaudioset_t audioSet; cutsceneuishow_t uiShow; + cutscenestorysetflag_t storySetFlag; + cutscenestorywaitflag_t storyWaitFlag; }; }; diff --git a/src/dusk/rpg/cutscene/item/cutsceneitembase.h b/src/dusk/rpg/cutscene/item/cutsceneitembase.h index ad0a9711..c19815a4 100644 --- a/src/dusk/rpg/cutscene/item/cutsceneitembase.h +++ b/src/dusk/rpg/cutscene/item/cutsceneitembase.h @@ -64,6 +64,8 @@ typedef enum { CUTSCENE_ITEM_TYPE_UI_SHOW, CUTSCENE_ITEM_TYPE_REGULAR_BATTLE, CUTSCENE_ITEM_TYPE_INSERT, + CUTSCENE_ITEM_TYPE_STORY_SET_FLAG, + CUTSCENE_ITEM_TYPE_STORY_WAIT_FLAG, CUTSCENE_ITEM_TYPE_COUNT } cutsceneitemtype_t; diff --git a/src/dusk/rpg/cutscene/item/json/CMakeLists.txt b/src/dusk/rpg/cutscene/item/json/CMakeLists.txt index b69ad77a..a6a8e175 100644 --- a/src/dusk/rpg/cutscene/item/json/CMakeLists.txt +++ b/src/dusk/rpg/cutscene/item/json/CMakeLists.txt @@ -20,4 +20,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} cutscenejsonscenetype.c cutscenejsonuiscreentype.c cutscenejsonaudiochannel.c + cutscenejsonstoryflag.c ) diff --git a/src/dusk/rpg/cutscene/item/json/cutscenejsonstoryflag.c b/src/dusk/rpg/cutscene/item/json/cutscenejsonstoryflag.c new file mode 100644 index 00000000..2eb14384 --- /dev/null +++ b/src/dusk/rpg/cutscene/item/json/cutscenejsonstoryflag.c @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "cutscenejsonstoryflag.h" + +bool_t cutsceneJsonGetStoryFlag(yyjson_val *val, storyflag_t *outFlag) { + if(!yyjson_is_str(val)) return false; + + storyflag_t flag = storyGetFlagByName(yyjson_get_str(val)); + if(flag >= STORY_FLAG_COUNT) return false; + + *outFlag = flag; + return true; +} diff --git a/src/dusk/rpg/cutscene/item/json/cutscenejsonstoryflag.h b/src/dusk/rpg/cutscene/item/json/cutscenejsonstoryflag.h new file mode 100644 index 00000000..d5152fde --- /dev/null +++ b/src/dusk/rpg/cutscene/item/json/cutscenejsonstoryflag.h @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" +#include "yyjson.h" +#include "rpg/story/story.h" + +/** + * Resolves a JSON string value naming a story flag (via storyGetFlagByName) + * into a storyflag_t. + * + * @param val JSON value to resolve (a string). + * @param outFlag Set to the resolved story flag id on success. + * @return true if val resolved to a known story flag, false otherwise. + */ +bool_t cutsceneJsonGetStoryFlag(yyjson_val *val, storyflag_t *outFlag); diff --git a/src/dusk/rpg/cutscene/item/story/CMakeLists.txt b/src/dusk/rpg/cutscene/item/story/CMakeLists.txt new file mode 100644 index 00000000..c84faff0 --- /dev/null +++ b/src/dusk/rpg/cutscene/item/story/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + cutscenestorysetflag.c + cutscenestorywaitflag.c +) diff --git a/src/dusk/rpg/cutscene/item/story/cutscenestorysetflag.c b/src/dusk/rpg/cutscene/item/story/cutscenestorysetflag.c new file mode 100644 index 00000000..0939f7bc --- /dev/null +++ b/src/dusk/rpg/cutscene/item/story/cutscenestorysetflag.c @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "rpg/cutscene/item/cutsceneitem.h" +#include "rpg/cutscene/item/json/cutscenejsonstoryflag.h" +#include "rpg/cutscene/item/json/cutscenejsonfield.h" + +void cutsceneStorySetFlagStart( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +) { + storySetFlagValue(item->storySetFlag.flag, item->storySetFlag.value); +} + +bool_t cutsceneStorySetFlagUpdate( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +) { + return true; +} + +errorret_t cutsceneStorySetFlagLoad( + yyjson_val *itemObj, + cutsceneitem_t *item +) { + yyjson_val *flagVal = yyjson_obj_get(itemObj, "flag"); + if(!cutsceneJsonGetStoryFlag(flagVal, &item->storySetFlag.flag)) { + errorThrow("Cutscene item (STORY_SET_FLAG): invalid or missing 'flag'"); + } + + item->storySetFlag.value = + (storyflagval_t)cutsceneJsonGetInt(itemObj, "value", 1); + + errorOk(); +} diff --git a/src/dusk/rpg/cutscene/item/story/cutscenestorysetflag.h b/src/dusk/rpg/cutscene/item/story/cutscenestorysetflag.h new file mode 100644 index 00000000..8fceb9d8 --- /dev/null +++ b/src/dusk/rpg/cutscene/item/story/cutscenestorysetflag.h @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "rpg/cutscene/item/cutsceneitembase.h" +#include "rpg/story/story.h" + +typedef struct { + storyflag_t flag; + storyflagval_t value; +} cutscenestorysetflag_t; + +/** + * Starts a story set-flag step, immediately writing the given value to the + * given story flag. + * + * @param item The cutscene item. + * @param data Runtime data storage. + */ +void cutsceneStorySetFlagStart( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +); + +/** + * Updates a story set-flag step (always completes immediately). + * + * @param item The cutscene item. + * @param data Runtime data storage. + * @returns true always. + */ +bool_t cutsceneStorySetFlagUpdate( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +); + +/** + * Parses a STORY_SET_FLAG cutscene item's JSON fields into item. + * + * @param itemObj The item's JSON object. + * @param item Destination item, already zeroed by the caller. + * @return Error code indicating success or failure of the parse. + */ +errorret_t cutsceneStorySetFlagLoad( + yyjson_val *itemObj, + cutsceneitem_t *item +); diff --git a/src/dusk/rpg/cutscene/item/story/cutscenestorywaitflag.c b/src/dusk/rpg/cutscene/item/story/cutscenestorywaitflag.c new file mode 100644 index 00000000..df7f65e7 --- /dev/null +++ b/src/dusk/rpg/cutscene/item/story/cutscenestorywaitflag.c @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "rpg/cutscene/item/cutsceneitem.h" +#include "rpg/cutscene/item/json/cutscenejsonstoryflag.h" +#include "rpg/cutscene/item/json/cutscenejsonfield.h" + +bool_t cutsceneStoryWaitFlagUpdate( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +) { + return storyGetFlagValue(item->storyWaitFlag.flag) == item->storyWaitFlag.value; +} + +errorret_t cutsceneStoryWaitFlagLoad( + yyjson_val *itemObj, + cutsceneitem_t *item +) { + yyjson_val *flagVal = yyjson_obj_get(itemObj, "flag"); + if(!cutsceneJsonGetStoryFlag(flagVal, &item->storyWaitFlag.flag)) { + errorThrow("Cutscene item (STORY_WAIT_FLAG): invalid or missing 'flag'"); + } + + item->storyWaitFlag.value = + (storyflagval_t)cutsceneJsonGetInt(itemObj, "value", 1); + + errorOk(); +} diff --git a/src/dusk/rpg/cutscene/item/story/cutscenestorywaitflag.h b/src/dusk/rpg/cutscene/item/story/cutscenestorywaitflag.h new file mode 100644 index 00000000..d31eeaa0 --- /dev/null +++ b/src/dusk/rpg/cutscene/item/story/cutscenestorywaitflag.h @@ -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 "rpg/cutscene/item/cutsceneitembase.h" +#include "rpg/story/story.h" + +typedef struct { + storyflag_t flag; + storyflagval_t value; +} cutscenestorywaitflag_t; + +/** + * Updates a story wait-flag step, completing once the given story flag's + * current value matches the given value. + * + * @param item The cutscene item. + * @param data Runtime data storage. + * @returns true once the flag's current value matches, false otherwise. + */ +bool_t cutsceneStoryWaitFlagUpdate( + const cutsceneitem_t *item, + cutsceneitemdata_t *data +); + +/** + * Parses a STORY_WAIT_FLAG cutscene item's JSON fields into item. + * + * @param itemObj The item's JSON object. + * @param item Destination item, already zeroed by the caller. + * @return Error code indicating success or failure of the parse. + */ +errorret_t cutsceneStoryWaitFlagLoad( + yyjson_val *itemObj, + cutsceneitem_t *item +); diff --git a/src/dusk/rpg/rpg.c b/src/dusk/rpg/rpg.c index 66400a45..d500dcda 100644 --- a/src/dusk/rpg/rpg.c +++ b/src/dusk/rpg/rpg.c @@ -22,7 +22,6 @@ #include "error/error.h" #include "scene/scene.h" #include "ui/rpg/uiemoji.h" -#include "rpg/story/storyflag.h" #include "rpg/story/story.h" errorret_t rpgInit(void) { diff --git a/src/dusk/rpg/story/CMakeLists.txt b/src/dusk/rpg/story/CMakeLists.txt index a3c89a28..8a945222 100644 --- a/src/dusk/rpg/story/CMakeLists.txt +++ b/src/dusk/rpg/story/CMakeLists.txt @@ -7,5 +7,4 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC story.c - storyflag.c ) diff --git a/src/dusk/rpg/story/story.c b/src/dusk/rpg/story/story.c index a3179e24..007b1ff7 100644 --- a/src/dusk/rpg/story/story.c +++ b/src/dusk/rpg/story/story.c @@ -8,8 +8,19 @@ #include "story.h" #include "asset/asset.h" #include "asset/loader/assetloader.h" +#include "assert/assert.h" +#include "save/save.h" +#include "util/memory.h" +#include "util/string.h" +#include "yyjson.h" + +storyflagmap_t STORY_FLAG_MAP[STORY_FLAG_COUNT_MAX]; +storyflag_t STORY_FLAG_COUNT; errorret_t storyInit(void) { + memoryZero(STORY_FLAG_MAP, sizeof(STORY_FLAG_MAP)); + STORY_FLAG_COUNT = 0; + assetentry_t *entry = assetLock( "story/storyflag.jsonc", ASSET_LOADER_TYPE_JSON, NULL ); @@ -19,9 +30,58 @@ errorret_t storyInit(void) { errorChain(ret); } - // TODO: Parse entry->data.json into a storyflagid_t registry, the same - // way item.c's itemInit() builds ITEM.defs from items/items.jsonc. + yyjson_val *root = yyjson_doc_get_root(entry->data.json); + yyjson_val *flags = yyjson_obj_get(root, "flags"); + + yyjson_obj_iter iter = yyjson_obj_iter_with(flags); + yyjson_val *key; + while((key = yyjson_obj_iter_next(&iter))) { + assertTrue( + STORY_FLAG_COUNT < STORY_FLAG_COUNT_MAX, "Too many story flags defined." + ); + + yyjson_val *val = yyjson_obj_iter_get_val(key); + yyjson_val *initial = yyjson_obj_get(val, "initial"); + + storyflag_t flag = STORY_FLAG_COUNT++; + stringCopy(STORY_FLAG_MAP[flag].name, yyjson_get_str(key), STORY_FLAG_NAME_MAX); + STORY_FLAG_MAP[flag].initial = yyjson_is_bool(initial) ? + (storyflagval_t)yyjson_get_bool(initial) : + (storyflagval_t)yyjson_get_int(initial); + } assetUnlockEntry(entry); errorOk(); } + +storyflag_t storyGetFlagByName(const char_t *name) { + assertNotNull(name, "name must not be NULL"); + + for(storyflag_t flag = 0; flag < STORY_FLAG_COUNT; flag++) { + if(stringEquals(STORY_FLAG_MAP[flag].name, name)) return flag; + } + + return STORY_FLAG_COUNT_MAX; +} + +const char_t * storyGetNameByFlag(storyflag_t flag) { + if(flag >= STORY_FLAG_COUNT) return NULL; + return STORY_FLAG_MAP[flag].name; +} + +storyflagval_t storyGetFlagValue(storyflag_t flag) { + if(flag >= STORY_FLAG_COUNT) return 0; + return SAVE.slot.storyFlags[flag]; +} + +void storySetFlagValue(storyflag_t flag, storyflagval_t value) { + assertTrue(flag < STORY_FLAG_COUNT, "Bad story flag"); + SAVE.slot.storyFlags[flag] = value; + SAVE.slotDirty = true; +} + +void storySetFlagValueByName(const char_t *name, storyflagval_t value) { + storyflag_t flag = storyGetFlagByName(name); + assertTrue(flag < STORY_FLAG_COUNT, "Unknown story flag"); + storySetFlagValue(flag, value); +} diff --git a/src/dusk/rpg/story/story.h b/src/dusk/rpg/story/story.h index 463ea7f2..108fe0fe 100644 --- a/src/dusk/rpg/story/story.h +++ b/src/dusk/rpg/story/story.h @@ -8,6 +8,20 @@ #pragma once #include "error/error.h" +#define STORY_FLAG_COUNT_MAX 256 +#define STORY_FLAG_NAME_MAX 16 + +typedef uint16_t storyflag_t; +typedef uint8_t storyflagval_t; + +typedef struct { + char_t name[STORY_FLAG_NAME_MAX]; + storyflagval_t initial; +} storyflagmap_t; + +extern storyflagmap_t STORY_FLAG_MAP[STORY_FLAG_COUNT_MAX]; +extern storyflag_t STORY_FLAG_COUNT; + /** * Initializes the story subsystem, loading assets/story/storyflag.jsonc. @@ -15,3 +29,48 @@ * @return Any error that occurs. */ errorret_t storyInit(void); + +/** + * Gets the ID of a story flag by name. + * + * @param name The name of the story flag to get. + * @return The ID of the story flag, or STORY_FLAG_COUNT_MAX if not found. + */ +storyflag_t storyGetFlagByName(const char_t *name); + +/** + * Gets the name of a story flag by ID. + * + * @param flag The ID of the story flag to get. + * @return The name of the story flag, or NULL if not found. + */ +const char_t * storyGetNameByFlag(storyflag_t flag); + +/** + * Gets the value of a story flag. Reads directly from the active save + * slot (SAVE.slot.storyFlags) - flag values have no separate live copy. + * + * @param flag The ID of the story flag to get. + * @return The value of the story flag, or 0 if not found. + */ +storyflagval_t storyGetFlagValue(storyflag_t flag); + +/** + * Sets the value of a story flag. Writes directly to the active save + * slot (SAVE.slot.storyFlags) - flag values have no separate live copy. + * Marks the save slot dirty. + * + * @param flag The ID of the story flag to set. + * @param value The value to set the story flag to. + */ +void storySetFlagValue(storyflag_t flag, storyflagval_t value); + +/** + * Sets the value of a story flag by name. Writes directly to the active save + * slot (SAVE.slot.storyFlags) - flag values have no separate live copy. + * Marks the save slot dirty. + * + * @param name The name of the story flag to set. + * @param value The value to set the story flag to. + */ +void storySetFlagValueByName(const char_t *name, storyflagval_t value); diff --git a/src/dusk/rpg/story/storyflag.c b/src/dusk/rpg/story/storyflag.c deleted file mode 100644 index 0e40f705..00000000 --- a/src/dusk/rpg/story/storyflag.c +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "storyflag.h" -#include "assert/assert.h" -#include "console/console.h" - -bool_t storyFlagGet(const storyflag_t flag) { - assertTrue(flag < STORY_FLAG_COUNT_MAX, "Bad Flag"); - consolePrint("Story flag get without save implementation"); - return false; -} - -void storyFlagSet(const storyflag_t flag, const bool_t value) { - assertTrue(flag < STORY_FLAG_COUNT_MAX, "Bad Flag"); - - // TODO: Dirty savefile - consolePrint("Story flag set without save implementation"); -} \ No newline at end of file diff --git a/src/dusk/rpg/story/storyflag.h b/src/dusk/rpg/story/storyflag.h deleted file mode 100644 index 842a11a3..00000000 --- a/src/dusk/rpg/story/storyflag.h +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" - -#define STORY_FLAG_BASE_TYPE uint32_t -#define STORY_FLAG_BASE_BITS (sizeof(STORY_FLAG_BASE_TYPE) * 8) -#define STORY_FLAG_COUNT_MAX 1024 -#define STORY_FLAG_BASE_COUNT \ - ((STORY_FLAG_COUNT_MAX + STORY_FLAG_BASE_BITS - 1) / STORY_FLAG_BASE_BITS) - -typedef STORY_FLAG_BASE_TYPE storyflag_t; -typedef STORY_FLAG_BASE_TYPE storyflaglist_t[STORY_FLAG_BASE_COUNT]; -typedef uint16_t storyflagid_t; - - -/** - * Gets the value of a story flag. Reads directly from the active save - * slot (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. - */ -bool_t storyFlagGet(const storyflag_t flag); - -/** - * Sets the value of a story flag. Will dirty the savefile. - * - * @param flag The story flag to set. - * @param value The value to set the story flag to. - */ -void storyFlagSet(const storyflag_t flag, const bool_t value); diff --git a/src/dusk/save/slot/saveslot.c b/src/dusk/save/slot/saveslot.c index 7c0d1b3f..9051883e 100644 --- a/src/dusk/save/slot/saveslot.c +++ b/src/dusk/save/slot/saveslot.c @@ -17,6 +17,10 @@ void saveSlotInit(void) { SAVE.slot.version = 1; + for(storyflag_t flag = 0; flag < STORY_FLAG_COUNT; flag++) { + SAVE.slot.storyFlags[flag] = STORY_FLAG_MAP[flag].initial; + } + partyInit(); } diff --git a/src/dusk/save/slot/saveslot.h b/src/dusk/save/slot/saveslot.h index 4bbeaef1..b4fa58ff 100644 --- a/src/dusk/save/slot/saveslot.h +++ b/src/dusk/save/slot/saveslot.h @@ -9,6 +9,7 @@ #include "save/savedevice.h" #include "rpg/overworld/map.h" #include "rpg/battle/party.h" +#include "rpg/story/story.h" #include "time/timeepoch.h" #include "yyjson.h" @@ -32,6 +33,7 @@ typedef struct saveslot_s { uint8_t dataType; saveslotcache_t cachedData; party_t party; + storyflagval_t storyFlags[STORY_FLAG_COUNT_MAX]; } saveslot_t; /** diff --git a/src/dusk/save/slot/saveslotcurrent.c b/src/dusk/save/slot/saveslotcurrent.c index 094d2fbb..64d4b84c 100644 --- a/src/dusk/save/slot/saveslotcurrent.c +++ b/src/dusk/save/slot/saveslotcurrent.c @@ -35,6 +35,8 @@ errorret_t saveSlotCurrentWriteJSON( ); memoryCopy(ver1.partyOrder, slot->party.order, sizeof(ver1.partyOrder)); + memoryCopy(ver1.storyFlags, slot->storyFlags, sizeof(ver1.storyFlags)); + errorChain(saveSlotVer1WriteJSON(&ver1, doc, object)); errorOk(); } @@ -62,5 +64,7 @@ errorret_t saveSlotCurrentReadJSON(saveslot_t *slot, yyjson_val *object) { ); memoryCopy(slot->party.order, ver1.partyOrder, sizeof(slot->party.order)); + memoryCopy(slot->storyFlags, ver1.storyFlags, sizeof(slot->storyFlags)); + errorOk(); } diff --git a/src/dusk/save/slot/saveslotver1.c b/src/dusk/save/slot/saveslotver1.c index efb5cd9f..15b0b912 100644 --- a/src/dusk/save/slot/saveslotver1.c +++ b/src/dusk/save/slot/saveslotver1.c @@ -46,6 +46,13 @@ errorret_t saveSlotVer1WriteJSON( writeUInt8Array("partyOrder", slot->partyOrder, PARTY_ACTIVE_SIZE_MAX); + yyjson_mut_val *storyFlagsObj = yyjson_mut_obj_add_obj(doc, object, "storyFlags"); + for(storyflag_t flag = 0; flag < STORY_FLAG_COUNT; flag++) { + yyjson_mut_obj_add_uint( + doc, storyFlagsObj, storyGetNameByFlag(flag), slot->storyFlags[flag] + ); + } + errorOk(); } @@ -102,5 +109,18 @@ errorret_t saveSlotVer1ReadJSON(saveslotver1_t *slot, yyjson_val *object) { PARTY_ORDER_EMPTY ); + yyjson_val *storyFlagsObj = yyjson_obj_get(object, "storyFlags"); + if(storyFlagsObj != NULL) { + yyjson_obj_iter iter = yyjson_obj_iter_with(storyFlagsObj); + yyjson_val *flagKey; + while((flagKey = yyjson_obj_iter_next(&iter))) { + storyflag_t flag = storyGetFlagByName(yyjson_get_str(flagKey)); + if(flag >= STORY_FLAG_COUNT) continue;// Flag no longer exists, ignore + + slot->storyFlags[flag] = + (storyflagval_t)yyjson_get_uint(yyjson_obj_iter_get_val(flagKey)); + } + } + errorOk(); } diff --git a/src/dusk/save/slot/saveslotver1.h b/src/dusk/save/slot/saveslotver1.h index 6ad8fd01..0433c8e2 100644 --- a/src/dusk/save/slot/saveslotver1.h +++ b/src/dusk/save/slot/saveslotver1.h @@ -10,6 +10,7 @@ #include "time/timeepoch.h" #include "save/slot/saveslotver.h" #include "rpg/battle/party.h" +#include "rpg/story/story.h" #include "yyjson.h" // This schema's own frozen copies of save/slot/saveslot.h's field sizes - @@ -19,6 +20,10 @@ // party arrays below are the exception - saveSlotVer1ReadJSON() checks their // JSON array lengths explicitly, so PARTY_MEMBER_COUNT_MAX/ // PARTY_ACTIVE_SIZE_MAX (rpg/battle/party.h) can be referenced directly. +// storyFlags is a further exception, for a different reason - it's written +// keyed by flag name (not position), so its on-disk shape is decoupled from +// STORY_FLAG_COUNT_MAX entirely; a save simply loses whatever flags no +// longer resolve by name (see storyGetFlagByName) if that cap ever shrinks. #define SAVE_SLOT_VER1_NAME_LENGTH 8 #define SAVE_SLOT_VER1_MAP_NAME_MAX 10 @@ -35,6 +40,8 @@ typedef struct saveslotver1_s { battlefighter_t partyMembers[PARTY_MEMBER_COUNT_MAX]; uint8_t partyOrder[PARTY_ACTIVE_SIZE_MAX]; + + storyflagval_t storyFlags[STORY_FLAG_COUNT_MAX]; } saveslotver1_t; /**