Wire story flags into the save slot and add cutscene controls
Implements storyInit/storyGetFlagByName/storyGetFlagValue/etc, moves flag storage into saveslot_t (persisted keyed by name so save files survive flag reordering), and adds STORY_SET_FLAG/STORY_WAIT_FLAG cutscene items. Also removes the now-superseded storyflag.c/h stub. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -1,5 +1,10 @@
|
|||||||
{
|
{
|
||||||
"flags": {
|
"flags": {
|
||||||
"test": { "description": "Test flag for debugging purposes", "initial": true }
|
"test": {
|
||||||
|
"initial": true
|
||||||
|
},
|
||||||
|
"test2": {
|
||||||
|
"initial": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,3 +20,4 @@ add_subdirectory(battle)
|
|||||||
add_subdirectory(save)
|
add_subdirectory(save)
|
||||||
add_subdirectory(audio)
|
add_subdirectory(audio)
|
||||||
add_subdirectory(json)
|
add_subdirectory(json)
|
||||||
|
add_subdirectory(story)
|
||||||
|
|||||||
@@ -339,6 +339,19 @@ cutsceneitemcallbacks_t CUTSCENE_ITEM_CALLBACKS[CUTSCENE_ITEM_TYPE_COUNT] = {
|
|||||||
.update = cutsceneInsertUpdate,
|
.update = cutsceneInsertUpdate,
|
||||||
.jsonName = "INSERT",
|
.jsonName = "INSERT",
|
||||||
.load = cutsceneInsertLoad
|
.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
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,8 @@
|
|||||||
#include "audio/cutsceneaudiosetpan.h"
|
#include "audio/cutsceneaudiosetpan.h"
|
||||||
#include "audio/cutsceneaudiosetloop.h"
|
#include "audio/cutsceneaudiosetloop.h"
|
||||||
#include "audio/cutsceneaudioset.h"
|
#include "audio/cutsceneaudioset.h"
|
||||||
|
#include "story/cutscenestorysetflag.h"
|
||||||
|
#include "story/cutscenestorywaitflag.h"
|
||||||
|
|
||||||
typedef struct cutscene_s cutscene_t;
|
typedef struct cutscene_s cutscene_t;
|
||||||
|
|
||||||
@@ -138,6 +140,8 @@ struct cutsceneitem_s {
|
|||||||
cutsceneaudiosetloop_t audioSetLoop;
|
cutsceneaudiosetloop_t audioSetLoop;
|
||||||
cutsceneaudioset_t audioSet;
|
cutsceneaudioset_t audioSet;
|
||||||
cutsceneuishow_t uiShow;
|
cutsceneuishow_t uiShow;
|
||||||
|
cutscenestorysetflag_t storySetFlag;
|
||||||
|
cutscenestorywaitflag_t storyWaitFlag;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ typedef enum {
|
|||||||
CUTSCENE_ITEM_TYPE_UI_SHOW,
|
CUTSCENE_ITEM_TYPE_UI_SHOW,
|
||||||
CUTSCENE_ITEM_TYPE_REGULAR_BATTLE,
|
CUTSCENE_ITEM_TYPE_REGULAR_BATTLE,
|
||||||
CUTSCENE_ITEM_TYPE_INSERT,
|
CUTSCENE_ITEM_TYPE_INSERT,
|
||||||
|
CUTSCENE_ITEM_TYPE_STORY_SET_FLAG,
|
||||||
|
CUTSCENE_ITEM_TYPE_STORY_WAIT_FLAG,
|
||||||
|
|
||||||
CUTSCENE_ITEM_TYPE_COUNT
|
CUTSCENE_ITEM_TYPE_COUNT
|
||||||
} cutsceneitemtype_t;
|
} cutsceneitemtype_t;
|
||||||
|
|||||||
@@ -20,4 +20,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
cutscenejsonscenetype.c
|
cutscenejsonscenetype.c
|
||||||
cutscenejsonuiscreentype.c
|
cutscenejsonuiscreentype.c
|
||||||
cutscenejsonaudiochannel.c
|
cutscenejsonaudiochannel.c
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
);
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
);
|
||||||
@@ -22,7 +22,6 @@
|
|||||||
#include "error/error.h"
|
#include "error/error.h"
|
||||||
#include "scene/scene.h"
|
#include "scene/scene.h"
|
||||||
#include "ui/rpg/uiemoji.h"
|
#include "ui/rpg/uiemoji.h"
|
||||||
#include "rpg/story/storyflag.h"
|
|
||||||
#include "rpg/story/story.h"
|
#include "rpg/story/story.h"
|
||||||
|
|
||||||
errorret_t rpgInit(void) {
|
errorret_t rpgInit(void) {
|
||||||
|
|||||||
@@ -7,5 +7,4 @@
|
|||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
story.c
|
story.c
|
||||||
storyflag.c
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,8 +8,19 @@
|
|||||||
#include "story.h"
|
#include "story.h"
|
||||||
#include "asset/asset.h"
|
#include "asset/asset.h"
|
||||||
#include "asset/loader/assetloader.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) {
|
errorret_t storyInit(void) {
|
||||||
|
memoryZero(STORY_FLAG_MAP, sizeof(STORY_FLAG_MAP));
|
||||||
|
STORY_FLAG_COUNT = 0;
|
||||||
|
|
||||||
assetentry_t *entry = assetLock(
|
assetentry_t *entry = assetLock(
|
||||||
"story/storyflag.jsonc", ASSET_LOADER_TYPE_JSON, NULL
|
"story/storyflag.jsonc", ASSET_LOADER_TYPE_JSON, NULL
|
||||||
);
|
);
|
||||||
@@ -19,9 +30,58 @@ errorret_t storyInit(void) {
|
|||||||
errorChain(ret);
|
errorChain(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Parse entry->data.json into a storyflagid_t registry, the same
|
yyjson_val *root = yyjson_doc_get_root(entry->data.json);
|
||||||
// way item.c's itemInit() builds ITEM.defs from items/items.jsonc.
|
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);
|
assetUnlockEntry(entry);
|
||||||
errorOk();
|
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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,20 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "error/error.h"
|
#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.
|
* Initializes the story subsystem, loading assets/story/storyflag.jsonc.
|
||||||
@@ -15,3 +29,48 @@
|
|||||||
* @return Any error that occurs.
|
* @return Any error that occurs.
|
||||||
*/
|
*/
|
||||||
errorret_t storyInit(void);
|
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);
|
||||||
|
|||||||
@@ -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");
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
@@ -17,6 +17,10 @@ void saveSlotInit(void) {
|
|||||||
|
|
||||||
SAVE.slot.version = 1;
|
SAVE.slot.version = 1;
|
||||||
|
|
||||||
|
for(storyflag_t flag = 0; flag < STORY_FLAG_COUNT; flag++) {
|
||||||
|
SAVE.slot.storyFlags[flag] = STORY_FLAG_MAP[flag].initial;
|
||||||
|
}
|
||||||
|
|
||||||
partyInit();
|
partyInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "save/savedevice.h"
|
#include "save/savedevice.h"
|
||||||
#include "rpg/overworld/map.h"
|
#include "rpg/overworld/map.h"
|
||||||
#include "rpg/battle/party.h"
|
#include "rpg/battle/party.h"
|
||||||
|
#include "rpg/story/story.h"
|
||||||
#include "time/timeepoch.h"
|
#include "time/timeepoch.h"
|
||||||
#include "yyjson.h"
|
#include "yyjson.h"
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ typedef struct saveslot_s {
|
|||||||
uint8_t dataType;
|
uint8_t dataType;
|
||||||
saveslotcache_t cachedData;
|
saveslotcache_t cachedData;
|
||||||
party_t party;
|
party_t party;
|
||||||
|
storyflagval_t storyFlags[STORY_FLAG_COUNT_MAX];
|
||||||
} saveslot_t;
|
} saveslot_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ errorret_t saveSlotCurrentWriteJSON(
|
|||||||
);
|
);
|
||||||
memoryCopy(ver1.partyOrder, slot->party.order, sizeof(ver1.partyOrder));
|
memoryCopy(ver1.partyOrder, slot->party.order, sizeof(ver1.partyOrder));
|
||||||
|
|
||||||
|
memoryCopy(ver1.storyFlags, slot->storyFlags, sizeof(ver1.storyFlags));
|
||||||
|
|
||||||
errorChain(saveSlotVer1WriteJSON(&ver1, doc, object));
|
errorChain(saveSlotVer1WriteJSON(&ver1, doc, object));
|
||||||
errorOk();
|
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->party.order, ver1.partyOrder, sizeof(slot->party.order));
|
||||||
|
|
||||||
|
memoryCopy(slot->storyFlags, ver1.storyFlags, sizeof(slot->storyFlags));
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,13 @@ errorret_t saveSlotVer1WriteJSON(
|
|||||||
|
|
||||||
writeUInt8Array("partyOrder", slot->partyOrder, PARTY_ACTIVE_SIZE_MAX);
|
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();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,5 +109,18 @@ errorret_t saveSlotVer1ReadJSON(saveslotver1_t *slot, yyjson_val *object) {
|
|||||||
PARTY_ORDER_EMPTY
|
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();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "time/timeepoch.h"
|
#include "time/timeepoch.h"
|
||||||
#include "save/slot/saveslotver.h"
|
#include "save/slot/saveslotver.h"
|
||||||
#include "rpg/battle/party.h"
|
#include "rpg/battle/party.h"
|
||||||
|
#include "rpg/story/story.h"
|
||||||
#include "yyjson.h"
|
#include "yyjson.h"
|
||||||
|
|
||||||
// This schema's own frozen copies of save/slot/saveslot.h's field sizes -
|
// 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
|
// party arrays below are the exception - saveSlotVer1ReadJSON() checks their
|
||||||
// JSON array lengths explicitly, so PARTY_MEMBER_COUNT_MAX/
|
// JSON array lengths explicitly, so PARTY_MEMBER_COUNT_MAX/
|
||||||
// PARTY_ACTIVE_SIZE_MAX (rpg/battle/party.h) can be referenced directly.
|
// 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_NAME_LENGTH 8
|
||||||
#define SAVE_SLOT_VER1_MAP_NAME_MAX 10
|
#define SAVE_SLOT_VER1_MAP_NAME_MAX 10
|
||||||
|
|
||||||
@@ -35,6 +40,8 @@ typedef struct saveslotver1_s {
|
|||||||
|
|
||||||
battlefighter_t partyMembers[PARTY_MEMBER_COUNT_MAX];
|
battlefighter_t partyMembers[PARTY_MEMBER_COUNT_MAX];
|
||||||
uint8_t partyOrder[PARTY_ACTIVE_SIZE_MAX];
|
uint8_t partyOrder[PARTY_ACTIVE_SIZE_MAX];
|
||||||
|
|
||||||
|
storyflagval_t storyFlags[STORY_FLAG_COUNT_MAX];
|
||||||
} saveslotver1_t;
|
} saveslotver1_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user