Add order-based party roster, back-navigation in battle selection, fix win-state cutscene leak

- Battle selection menu can now go back a fighter to re-decide their move,
  and battle win/loss no longer leaks the round's remaining queued move
  cutscenes into the ending state (cutsceneSystemDiscardQueued).
- Party roster is now driven by party.jsonc: every defined member always
  exists (partyInit), while party->order alone determines who's currently
  active - partyAddMember/partyRemoveMember (and their cutscene items) now
  activate/deactivate an existing roster member by index instead of
  creating/destroying one, with removal compacting order to stay packed.
- New PARTY_ADD_MEMBER/PARTY_REMOVE_MEMBER/PARTY_HEAL_MEMBER/PARTY_HEAL_ALL
  cutscene items.
- saveSlotInit/partyInit now return errorret_t and take their target
  struct directly (saveslot_t*/party_t*) instead of implicitly operating
  on SAVE.slot; saveSlotReadJSON resets through saveSlotInit rather than
  duplicating the reset logic.
- Test coverage: stub the JSON asset loader in save test fixtures so
  partyInit's real party.jsonc load runs against an in-memory doc instead
  of hanging with no asset system initialized.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
2026-09-21 22:27:46 -05:00
co-authored by Claude Sonnet 5
parent e8757c33e3
commit 7a9a2b9832
35 changed files with 838 additions and 99 deletions
+7 -7
View File
@@ -298,7 +298,7 @@ static void test_saveSaveSlot_invalidSlotIndexAsserts(void **state) {
static void test_saveSaveSlot_success(void **state) {
makeDeviceAvailable();
SAVE.slotCurrent = 1;
saveSlotInit();
saveSlotInit(&SAVE.slot);
stringCopy(
SAVE.slot.cachedData.name, "Hero", sizeof(SAVE.slot.cachedData.name)
);
@@ -336,7 +336,7 @@ static void test_saveLoadSlot_success(void **state) {
// ever operates on SAVE.slot now), then reset it again below - the
// write and the load-under-test don't overlap in time.
SAVE.slotCurrent = 2;
saveSlotInit();
saveSlotInit(&SAVE.slot);
stringCopy(
SAVE.slot.cachedData.name, "Zelda", sizeof(SAVE.slot.cachedData.name)
);
@@ -345,7 +345,7 @@ static void test_saveLoadSlot_success(void **state) {
assert_true(errorIsOk(writeRet));
SAVE.slotCurrent = 2;
saveSlotInit();
saveSlotInit(&SAVE.slot);
SAVE.slotDirty = true;
errorret_t ret = saveLoadSlot();
@@ -371,7 +371,7 @@ static void test_saveLoadSlot_deviceReadFails_cacheStaysStale(void **state) {
fclose(file);
SAVE.slotCurrent = 0;
saveSlotInit();
saveSlotInit(&SAVE.slot);
SAVE.caches[0].playerLevel = 77;// sentinel
errorret_t ret = saveLoadSlot();
@@ -391,7 +391,7 @@ static void test_saveLoadAllSlots_loadsAllAndEndsAtLastIndex(void **state) {
for(uint8_t i = 0; i < SAVE_SLOT_COUNT; i++) {
SAVE.slotCurrent = i;
saveSlotInit();
saveSlotInit(&SAVE.slot);
SAVE.slot.cachedData.playerLevel = (int32_t)(i + 1);
errorret_t saveRet = saveSaveSlot();
assert_true(errorIsOk(saveRet));
@@ -410,7 +410,7 @@ static void test_saveLoadAllSlots_missingFileLeavesInitDefaults(void **state) {
makeDeviceAvailable();
SAVE.slotCurrent = 0;
saveSlotInit();
saveSlotInit(&SAVE.slot);
SAVE.slot.cachedData.playerLevel = 5;
errorret_t saveRet = saveSaveSlot();
assert_true(errorIsOk(saveRet));
@@ -433,7 +433,7 @@ static void test_saveLoadAllSlots_middleSlotCorrupt_stopsAtFailingIndex(
makeDeviceAvailable();
SAVE.slotCurrent = 0;
saveSlotInit();
saveSlotInit(&SAVE.slot);
errorret_t saveRet = saveSaveSlot();
assert_true(errorIsOk(saveRet));