Files
dusk/.claude/rpg/story.md
T
2026-06-18 14:59:21 -05:00

3.7 KiB

Story, Items & Save


Story flags (src/dusk/rpg/story/)

Story flags are the primary mechanism for tracking game-world state (quest progress, one-time events, unlocks). Each flag is a uint8_t value (storyflagvalue_t), so they can hold booleans or small counts.

Defining flags

Flags are defined in src/dusk/rpg/story/storyflag.csv:

id,description,initial
test,"Test flag for debugging purposes",1

The build tool generates:

  • A storyflag_t enum (e.g. STORY_FLAG_TEST) in the generated header.
  • STORY_FLAG_VALUES[] — the runtime array, pre-populated with the initial column values.

To add a flag: add a row to the CSV. The build re-runs the Python tool automatically on the next CMake build.

Access

storyflagvalue_t v = storyFlagGet(STORY_FLAG_TEST);   // macro: array read
storyFlagSet(STORY_FLAG_TEST, 1);                      // function: also marks save dirty

storyFlagGet is a macro that directly indexes STORY_FLAG_VALUES[] — no function call overhead.


Items (src/dusk/rpg/item/)

Item definitions

Items are defined in src/dusk/rpg/item/item.csv. The build tool generates itemid_t enum values and item metadata. itemid_t is a generated uint8_t typedef.

Inventory (inventory.h)

inventory_t is a generic container backed by a caller-supplied inventorystack_t array:

typedef struct {
  itemid_t item;
  uint8_t quantity;        // max ITEM_STACK_QUANTITY_MAX (255)
} inventorystack_t;

typedef struct {
  inventorystack_t *storage;
  uint8_t storageSize;
} inventory_t;

Key operations:

inventoryInit(&inv, storageArray, size);
inventoryAdd(&inv, ITEM_POTION, 3);
inventoryRemove(&inv, ITEM_POTION);
inventorySet(&inv, ITEM_POTION, 10);
inventoryGetCount(&inv, ITEM_POTION);   // returns 0 if not present
inventoryItemExists(&inv, ITEM_POTION);
inventoryIsFull(&inv);
inventorySort(&inv, INVENTORY_SORT_BY_ID, false);

inventory_t itself holds no data — the backing array is always external. This avoids fixed-size struct limits and lets different inventories (backpack, shop, chest) share the same logic.

Backpack (backpack.h)

The player's inventory is the global BACKPACK instance:

extern inventorystack_t BACKPACK_STORAGE[BACKPACK_STORAGE_SIZE_MAX];  // 20 slots
extern inventory_t BACKPACK;

backpackInit();   // wires BACKPACK_STORAGE into BACKPACK

Save system (src/dusk/save/)

The save system is stubbed out — it exists and compiles but is commented out of engine init (engine.c). What follows describes the design as implemented.

Slots

save_t SAVE holds SAVE_FILE_COUNT_MAX slots:

typedef struct {
  savefile_t files[SAVE_FILE_COUNT_MAX];
  saveplatform_t platform;   // platform-specific state (paths, card handles)
} save_t;

Streams

savestream_t (save/savestream.h) is a raw byte cursor used to serialize/deserialize savefile_t. Platform backends in src/dusk{platform}/save/ implement the actual I/O:

  • Linux: filesystem files in a save directory.
  • GameCube/Wii: memory card via libogc.

API

saveInit();
saveLoad(slot);     // reads platform storage → savefile_t
saveWrite(slot);    // writes savefile_t → platform storage
saveDelete(slot);
saveExists(slot);   // bool
saveGet(slot);      // returns savefile_t *
saveDispose();

Locale / i18n (src/dusk/locale/)

Translations are loaded from .po files in assets/locale/ (e.g. en_US.po). localemanager.c manages the active locale and exposes a key→string lookup. assetlocaleloader.c parses the PO format via the asset system.

All player-visible strings must go through the locale system rather than being hardcoded. The locale is loaded asynchronously via the asset system so it is available before the first scene renders.