- party.jsonc's "members" is now a key/value object (e.g. "hero", "companion") instead of an array - partyInit seeds party_t.keys alongside members, and partyGetMemberIndexByKey/partyGetMemberKey do the lookups. Cutscene items (PARTY_ADD_MEMBER/PARTY_REMOVE_MEMBER/ PARTY_HEAL_MEMBER) now take a "key" JSON field, resolved to a roster index at load time, instead of a raw index. - Battle UI shows who's acting: uibattlemenu.c draws the current fighter's party key above the action menu, and uibattlehud.c draws each ally fighter's key as an extra line above their HP/MP. Co-Authored-By: Claude Sonnet 5 <[email protected]>
138 lines
3.6 KiB
C
138 lines
3.6 KiB
C
/**
|
|
* Copyright (c) 2026 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
// nftw()/FTW_DEPTH/FTW_PHYS are glibc extensions gated behind this.
|
|
#define _XOPEN_SOURCE 700
|
|
|
|
#include "savetestfixture.h"
|
|
#include "assert/assert.h"
|
|
#include "asset/asset.h"
|
|
#include "asset/loader/assetloader.h"
|
|
#include "asset/loader/assetentry.h"
|
|
#include "util/memory.h"
|
|
#include "util/string.h"
|
|
#include <ftw.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
static char_t SAVE_TEST_FIXTURE_HOME[PATH_MAX];
|
|
static char_t SAVE_TEST_FIXTURE_REAL_HOME[PATH_MAX];
|
|
static bool_t SAVE_TEST_FIXTURE_HAD_REAL_HOME;
|
|
|
|
static const char_t *SAVE_TEST_FIXTURE_PARTY_JSON =
|
|
"{\"members\":{\"hero\":{"
|
|
"\"attack\":10,\"defense\":5,\"magic\":0,\"speed\":10,\"luck\":0,"
|
|
"\"healthMax\":30,\"mpMax\":10"
|
|
"}}}";
|
|
|
|
static yyjson_doc *SAVE_TEST_FIXTURE_PARTY_DOC = NULL;
|
|
|
|
errorret_t saveTestFixturePartyJsonLoadSyncStub(assetloading_t *loading) {
|
|
loading->entry->data.json = SAVE_TEST_FIXTURE_PARTY_DOC;
|
|
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
|
|
errorOk();
|
|
}
|
|
|
|
errorret_t saveTestFixturePartyJsonDisposeStub(assetentry_t *entry) {
|
|
errorOk();
|
|
}
|
|
|
|
void saveTestFixtureStubPartyAsset(void) {
|
|
memoryZero(&ASSET, sizeof(ASSET));
|
|
for(size_t i = 0; i < ASSET_LOADING_COUNT_MAX; i++) {
|
|
threadMutexInit(&ASSET.loading[i].mutex);
|
|
}
|
|
|
|
if(SAVE_TEST_FIXTURE_PARTY_DOC == NULL) {
|
|
SAVE_TEST_FIXTURE_PARTY_DOC = yyjson_read(
|
|
SAVE_TEST_FIXTURE_PARTY_JSON, strlen(SAVE_TEST_FIXTURE_PARTY_JSON), 0
|
|
);
|
|
assertNotNull(
|
|
SAVE_TEST_FIXTURE_PARTY_DOC, "Failed to parse party JSON fixture"
|
|
);
|
|
}
|
|
|
|
ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_JSON].loadSync =
|
|
saveTestFixturePartyJsonLoadSyncStub;
|
|
ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_JSON].dispose =
|
|
saveTestFixturePartyJsonDisposeStub;
|
|
}
|
|
|
|
void saveTestFixtureUnstubPartyAsset(void) {
|
|
if(SAVE_TEST_FIXTURE_PARTY_DOC == NULL) return;
|
|
yyjson_doc_free(SAVE_TEST_FIXTURE_PARTY_DOC);
|
|
SAVE_TEST_FIXTURE_PARTY_DOC = NULL;
|
|
}
|
|
|
|
static int_t saveTestFixtureRemoveEntry(
|
|
const char *path,
|
|
const struct stat *statBuffer,
|
|
int typeFlag,
|
|
struct FTW *ftwBuffer
|
|
) {
|
|
return remove(path);
|
|
}
|
|
|
|
int saveTestFixtureSetup(void **state) {
|
|
const char_t *realHome = getenv("HOME");
|
|
SAVE_TEST_FIXTURE_HAD_REAL_HOME = realHome != NULL;
|
|
if(realHome != NULL) {
|
|
stringCopy(
|
|
SAVE_TEST_FIXTURE_REAL_HOME, realHome,
|
|
sizeof(SAVE_TEST_FIXTURE_REAL_HOME) - 1
|
|
);
|
|
}
|
|
|
|
stringCopy(
|
|
SAVE_TEST_FIXTURE_HOME, "/tmp/dusk-save-test-XXXXXX",
|
|
sizeof(SAVE_TEST_FIXTURE_HOME) - 1
|
|
);
|
|
assertNotNull(mkdtemp(SAVE_TEST_FIXTURE_HOME), "Failed to create temp $HOME");
|
|
|
|
assertTrue(setenv("HOME", SAVE_TEST_FIXTURE_HOME, 1) == 0, "setenv failed");
|
|
|
|
saveTestFixtureStubPartyAsset();
|
|
|
|
errorret_t ret = saveInit();
|
|
assertTrue(errorIsOk(ret), "saveInit failed in test fixture");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int saveTestFixtureTeardown(void **state) {
|
|
saveTestFixtureUnstubPartyAsset();
|
|
|
|
nftw(
|
|
SAVE_TEST_FIXTURE_HOME, saveTestFixtureRemoveEntry, 16,
|
|
FTW_DEPTH | FTW_PHYS
|
|
);
|
|
|
|
if(SAVE_TEST_FIXTURE_HAD_REAL_HOME) {
|
|
setenv("HOME", SAVE_TEST_FIXTURE_REAL_HOME, 1);
|
|
} else {
|
|
unsetenv("HOME");
|
|
}
|
|
|
|
memoryZero(&SAVE, sizeof(SAVE));
|
|
|
|
return 0;
|
|
}
|
|
|
|
void saveTestFixtureBlockSaveDirectory(void) {
|
|
const char_t *home = getenv("HOME");
|
|
assertNotNull(home, "Fixture $HOME must be set before blocking it");
|
|
|
|
char_t path[PATH_MAX];
|
|
stringFormat(path, sizeof(path), "%s/.dusk", home);
|
|
|
|
FILE *file = fopen(path, "w");
|
|
assertNotNull(file, "Failed to create blocking file");
|
|
fclose(file);
|
|
}
|