Compare commits
2 Commits
aa0180571e
...
1ddc298a74
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ddc298a74 | |||
| e2a9442aa6 |
@@ -53,7 +53,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
|||||||
consolePrint("Assertions real");
|
consolePrint("Assertions real");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
sceneSet(SCENE_TYPE_OVERWORLD);
|
sceneSet(SCENE_TYPE_INITIAL);
|
||||||
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
|
|||||||
+7
-13
@@ -16,18 +16,14 @@
|
|||||||
#include "time/time.h"
|
#include "time/time.h"
|
||||||
#include "rpgcamera.h"
|
#include "rpgcamera.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "util/string.h"
|
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "save/save.h"
|
#include "save/save.h"
|
||||||
#include "error/error.h"
|
#include "error/error.h"
|
||||||
|
#include "scene/scene.h"
|
||||||
|
|
||||||
#include "ui/rpg/uiemoji.h"
|
#include "ui/rpg/uiemoji.h"
|
||||||
#include "rpg/story/storyflag.h"
|
#include "rpg/story/storyflag.h"
|
||||||
|
|
||||||
static void rpgTestSaveComplete(errorret_t result, void *user) {
|
|
||||||
if(errorIsNotOk(result)) errorCatch(errorPrint(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t rpgInit(void) {
|
errorret_t rpgInit(void) {
|
||||||
memoryZero(ENTITIES, sizeof(ENTITIES));
|
memoryZero(ENTITIES, sizeof(ENTITIES));
|
||||||
memoryZero(MAP_AREAS, sizeof(MAP_AREAS));
|
memoryZero(MAP_AREAS, sizeof(MAP_AREAS));
|
||||||
@@ -71,13 +67,6 @@ errorret_t rpgInit(void) {
|
|||||||
backpackAdd(ITEM_ID_POTATO, 3);
|
backpackAdd(ITEM_ID_POTATO, 3);
|
||||||
backpackAdd(ITEM_ID_APPLE, 8);
|
backpackAdd(ITEM_ID_APPLE, 8);
|
||||||
|
|
||||||
// TEST: Verify the save system round-trips real game data, not just the
|
|
||||||
// header/version. Remove once there's an actual name-entry flow. On PSP
|
|
||||||
// this shows the real native save dialog every boot - expected while
|
|
||||||
// testing that path, not something to ship as-is.
|
|
||||||
stringCopy(saveSlot->playerName, "Dusk", SAVE_PLAYER_NAME_MAX);
|
|
||||||
saveWriteSlot(SAVE_ACTIVE_SLOT, rpgTestSaveComplete, NULL);
|
|
||||||
|
|
||||||
// All Good!
|
// All Good!
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
@@ -92,12 +81,17 @@ errorret_t rpgUpdate(void) {
|
|||||||
// TODO: Do not update if the scene is not the map scene?
|
// TODO: Do not update if the scene is not the map scene?
|
||||||
errorChain(mapUpdate());
|
errorChain(mapUpdate());
|
||||||
|
|
||||||
// Update overworld ents.
|
// Update overworld ents - only while actually in the overworld. Entities
|
||||||
|
// (the player among them) keep existing across scene changes, but their
|
||||||
|
// input/movement/animation logic doesn't make sense to run mid-battle or
|
||||||
|
// before the initial scene has handed off to the overworld.
|
||||||
|
if(SCENE.current == SCENE_TYPE_OVERWORLD) {
|
||||||
entity_t *ent = &ENTITIES[0];
|
entity_t *ent = &ENTITIES[0];
|
||||||
do {
|
do {
|
||||||
if(ent->type == ENTITY_TYPE_NULL) continue;
|
if(ent->type == ENTITY_TYPE_NULL) continue;
|
||||||
entityUpdate(ent);
|
entityUpdate(ent);
|
||||||
} while(++ent < &ENTITIES[ENTITY_COUNT]);
|
} while(++ent < &ENTITIES[ENTITY_COUNT]);
|
||||||
|
}
|
||||||
|
|
||||||
cutsceneSystemUpdate();
|
cutsceneSystemUpdate();
|
||||||
errorChain(rpgCameraUpdate());
|
errorChain(rpgCameraUpdate());
|
||||||
|
|||||||
@@ -51,7 +51,15 @@ errorret_t saveInit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool_t saveIsAvailable(void) {
|
bool_t saveIsAvailable(void) {
|
||||||
return SAVE.available;
|
return SAVE.available && !SAVE.temporary;
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveMarkTemporary(void) {
|
||||||
|
SAVE.temporary = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t saveIsTemporary(void) {
|
||||||
|
return SAVE.temporary;
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t saveDispose(void) {
|
errorret_t saveDispose(void) {
|
||||||
|
|||||||
+34
-4
@@ -26,6 +26,15 @@ typedef struct {
|
|||||||
* conditions here, not fatal errors - see saveIsAvailable().
|
* conditions here, not fatal errors - see saveIsAvailable().
|
||||||
*/
|
*/
|
||||||
bool_t available;
|
bool_t available;
|
||||||
|
/**
|
||||||
|
* True once the player has explicitly acknowledged there's no save
|
||||||
|
* device and chosen to continue anyway (see the initial scene's "no
|
||||||
|
* card" prompt) - sticky for the rest of the session, folded into
|
||||||
|
* saveIsAvailable() so every existing/future call site that already
|
||||||
|
* gates on that automatically refuses to save/load from here on. See
|
||||||
|
* saveMarkTemporary()/saveIsTemporary().
|
||||||
|
*/
|
||||||
|
bool_t temporary;
|
||||||
/**
|
/**
|
||||||
* Scratch error state used by platforms whose save/load completes
|
* Scratch error state used by platforms whose save/load completes
|
||||||
* asynchronously (see saveIsBusy()) to construct a result to hand to a
|
* asynchronously (see saveIsBusy()) to construct a result to hand to a
|
||||||
@@ -60,14 +69,35 @@ errorret_t saveInit(void);
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the save medium was reachable as of the last load/write
|
* Checks whether the save medium was reachable as of the last load/write
|
||||||
* attempt (or saveInit(), if none has been attempted yet). Intended for UI
|
* attempt (or saveInit(), if none has been attempted yet), AND the
|
||||||
* to decide whether to offer saving/loading at all, or to explain why it
|
* session hasn't been marked temporary (see saveMarkTemporary()).
|
||||||
* isn't available right now - e.g. "No memory card inserted".
|
* Intended for UI to decide whether to offer saving/loading at all, or to
|
||||||
|
* explain why it isn't available right now - e.g. "No memory card
|
||||||
|
* inserted".
|
||||||
*
|
*
|
||||||
* @return true if the save medium was available last time it was checked.
|
* @return true if saving/loading can be attempted right now.
|
||||||
*/
|
*/
|
||||||
bool_t saveIsAvailable(void);
|
bool_t saveIsAvailable(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks this session as temporary - the player explicitly acknowledged
|
||||||
|
* there's no save device and chose to continue anyway. One-way: nothing
|
||||||
|
* currently re-probes the save medium mid-session (see saveInit()'s doc
|
||||||
|
* comment), so there's no legitimate way to un-stick this once set short
|
||||||
|
* of restarting the game.
|
||||||
|
*/
|
||||||
|
void saveMarkTemporary(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True once saveMarkTemporary() has been called this session. Distinct
|
||||||
|
* from saveIsAvailable() so UI can give a more specific message (e.g.
|
||||||
|
* "this session is temporary" rather than the generic "no save device
|
||||||
|
* found") once the player has already made that choice.
|
||||||
|
*
|
||||||
|
* @return true if this session has been marked temporary.
|
||||||
|
*/
|
||||||
|
bool_t saveIsTemporary(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposes of the save system.
|
* Disposes of the save system.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -10,5 +10,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Subdirs
|
# Subdirs
|
||||||
|
add_subdirectory(initial)
|
||||||
add_subdirectory(overworld)
|
add_subdirectory(overworld)
|
||||||
add_subdirectory(battle)
|
add_subdirectory(battle)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# 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
|
||||||
|
sceneinitial.c
|
||||||
|
)
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "scene/scene.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "error/error.h"
|
||||||
|
#include "save/save.h"
|
||||||
|
#include "ui/frame/initial/uiinitialnocard.h"
|
||||||
|
#include "ui/frame/initial/uiinitialcreatesave.h"
|
||||||
|
|
||||||
|
static void sceneInitialCheckSave(void);
|
||||||
|
|
||||||
|
static void sceneInitialCreateSaveWriteComplete(errorret_t result, void *user) {
|
||||||
|
if(errorIsNotOk(result)) errorCatch(errorPrint(result));
|
||||||
|
sceneSet(SCENE_TYPE_OVERWORLD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sceneInitialCreateSaveResult(const bool_t create, void *user) {
|
||||||
|
if(!create) {
|
||||||
|
sceneSet(SCENE_TYPE_OVERWORLD);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveWriteSlot(SAVE_ACTIVE_SLOT, sceneInitialCreateSaveWriteComplete, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sceneInitialNoCardResult(const bool_t retry, void *user) {
|
||||||
|
if(retry) {
|
||||||
|
sceneInitialCheckSave();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The player explicitly acknowledged there's no save device and chose
|
||||||
|
// to proceed anyway - stick with that for the rest of the session (see
|
||||||
|
// saveMarkTemporary()'s doc comment for why this can't be un-set later).
|
||||||
|
saveMarkTemporary();
|
||||||
|
sceneSet(SCENE_TYPE_OVERWORLD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sceneInitialLoadComplete(errorret_t result, void *user) {
|
||||||
|
if(errorIsNotOk(result) || !saveIsAvailable()) {
|
||||||
|
errorCatch(result);
|
||||||
|
uiInitialNoCardOpen(sceneInitialNoCardResult, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(saveSlotExists(SAVE_ACTIVE_SLOT)) {
|
||||||
|
sceneSet(SCENE_TYPE_OVERWORLD);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uiInitialCreateSaveOpen(sceneInitialCreateSaveResult, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sceneInitialCheckSave(void) {
|
||||||
|
if(!saveIsAvailable()) {
|
||||||
|
uiInitialNoCardOpen(sceneInitialNoCardResult, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(saveIsBusy()) return;
|
||||||
|
|
||||||
|
saveLoadSlot(SAVE_ACTIVE_SLOT, sceneInitialLoadComplete, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t sceneInitialInit(scenedata_t *sceneData) {
|
||||||
|
assertNotNull(sceneData, "Scene data cannot be null");
|
||||||
|
memoryZero(&sceneData->initial, sizeof(sceneinitial_t));
|
||||||
|
|
||||||
|
sceneInitialCheckSave();
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t sceneInitialUpdate(scenedata_t *sceneData) {
|
||||||
|
assertNotNull(sceneData, "Scene data cannot be null");
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t sceneInitialRender(scenedata_t *sceneData) {
|
||||||
|
assertNotNull(sceneData, "Scene data cannot be null");
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t sceneInitialDispose(scenedata_t *sceneData) {
|
||||||
|
assertNotNull(sceneData, "Scene data cannot be null");
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "scene/scenebase.h"
|
||||||
|
|
||||||
|
// No per-scene state needed - the save globals and the two modal UI
|
||||||
|
// elements (see ui/frame/initial/) carry everything this scene cares
|
||||||
|
// about. A byte placeholder keeps the struct non-empty for portability.
|
||||||
|
typedef struct {
|
||||||
|
uint8_t reserved;
|
||||||
|
} sceneinitial_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises the initial (boot) scene - kicks off the save
|
||||||
|
* availability/existence check that decides which prompt, if any, to
|
||||||
|
* show before proceeding to the overworld.
|
||||||
|
*
|
||||||
|
* @param sceneData The scene data used for this scene.
|
||||||
|
* @return An error if the init failed, or errorOk() if it succeeded.
|
||||||
|
*/
|
||||||
|
errorret_t sceneInitialInit(scenedata_t *sceneData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the initial scene. Currently a no-op - all the work happens in
|
||||||
|
* save callbacks and the two modal UI elements' own button handling.
|
||||||
|
*
|
||||||
|
* @param sceneData The scene data used for this scene.
|
||||||
|
* @return An error if the update failed, or errorOk() if it succeeded.
|
||||||
|
*/
|
||||||
|
errorret_t sceneInitialUpdate(scenedata_t *sceneData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the initial scene. Currently a no-op - the modals draw
|
||||||
|
* themselves via the global UI element pipeline.
|
||||||
|
*
|
||||||
|
* @param sceneData The scene data used for this scene.
|
||||||
|
* @return An error if the render failed, or errorOk() if it succeeded.
|
||||||
|
*/
|
||||||
|
errorret_t sceneInitialRender(scenedata_t *sceneData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes the initial scene.
|
||||||
|
*
|
||||||
|
* @param sceneData The scene data used for this scene.
|
||||||
|
* @return An error if the dispose failed, or errorOk() if it succeeded.
|
||||||
|
*/
|
||||||
|
errorret_t sceneInitialDispose(scenedata_t *sceneData);
|
||||||
@@ -10,6 +10,13 @@
|
|||||||
scenecallbacks_t SCENE_TYPES[SCENE_TYPE_COUNT] = {
|
scenecallbacks_t SCENE_TYPES[SCENE_TYPE_COUNT] = {
|
||||||
[SCENE_TYPE_NULL] = { 0 },
|
[SCENE_TYPE_NULL] = { 0 },
|
||||||
|
|
||||||
|
[SCENE_TYPE_INITIAL] = {
|
||||||
|
.init = sceneInitialInit,
|
||||||
|
.update = sceneInitialUpdate,
|
||||||
|
.render = sceneInitialRender,
|
||||||
|
.dispose = sceneInitialDispose
|
||||||
|
},
|
||||||
|
|
||||||
[SCENE_TYPE_OVERWORLD] = {
|
[SCENE_TYPE_OVERWORLD] = {
|
||||||
.init = sceneOverworldInit,
|
.init = sceneOverworldInit,
|
||||||
.update = sceneOverworldUpdate,
|
.update = sceneOverworldUpdate,
|
||||||
|
|||||||
@@ -7,10 +7,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "scene/scenebase.h"
|
#include "scene/scenebase.h"
|
||||||
|
#include "scene/initial/sceneinitial.h"
|
||||||
#include "scene/overworld/sceneoverworld.h"
|
#include "scene/overworld/sceneoverworld.h"
|
||||||
#include "scene/battle/scenebattle.h"
|
#include "scene/battle/scenebattle.h"
|
||||||
|
|
||||||
typedef union scenedata_u {
|
typedef union scenedata_u {
|
||||||
|
sceneinitial_t initial;
|
||||||
sceneoverworld_t overworld;
|
sceneoverworld_t overworld;
|
||||||
scenebattle_t battle;
|
scenebattle_t battle;
|
||||||
} scenedata_t;
|
} scenedata_t;
|
||||||
@@ -27,6 +29,7 @@ typedef struct {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
SCENE_TYPE_NULL,
|
SCENE_TYPE_NULL,
|
||||||
|
|
||||||
|
SCENE_TYPE_INITIAL,
|
||||||
SCENE_TYPE_OVERWORLD,
|
SCENE_TYPE_OVERWORLD,
|
||||||
SCENE_TYPE_BATTLE,
|
SCENE_TYPE_BATTLE,
|
||||||
|
|
||||||
|
|||||||
@@ -13,3 +13,4 @@ add_subdirectory(game)
|
|||||||
add_subdirectory(settings)
|
add_subdirectory(settings)
|
||||||
add_subdirectory(battle)
|
add_subdirectory(battle)
|
||||||
add_subdirectory(backpack)
|
add_subdirectory(backpack)
|
||||||
|
add_subdirectory(initial)
|
||||||
|
|||||||
@@ -81,6 +81,13 @@ static void uiGameMenuSaveCheckComplete(errorret_t result, void *user) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void uiGameMenuSave(void) {
|
static void uiGameMenuSave(void) {
|
||||||
|
if(saveIsTemporary()) {
|
||||||
|
uiTextboxMainSetText(
|
||||||
|
"This session is temporary - no save device was found, so saving "
|
||||||
|
"is disabled."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if(!saveIsAvailable()) {
|
if(!saveIsAvailable()) {
|
||||||
uiTextboxMainSetText("Can't save - no save device found.");
|
uiTextboxMainSetText("Can't save - no save device found.");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -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
|
||||||
|
uiinitialnocard.c
|
||||||
|
uiinitialcreatesave.c
|
||||||
|
)
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "uiinitialcreatesave.h"
|
||||||
|
#include "ui/frame/uiframe.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "util/math.h"
|
||||||
|
#include "display/screen/screen.h"
|
||||||
|
#include "display/text/text.h"
|
||||||
|
#include "display/color.h"
|
||||||
|
#include "display/spritebatch/spritebatch.h"
|
||||||
|
#include "display/texture/texture.h"
|
||||||
|
#include "display/shader/shaderunlit.h"
|
||||||
|
|
||||||
|
#define UI_INITIAL_CREATE_SAVE_BACKDROP_COLOR color4b(0, 0, 0, 160)
|
||||||
|
#define UI_INITIAL_CREATE_SAVE_TEXT "No save data found. Create a new save?"
|
||||||
|
|
||||||
|
uiinitialcreatesave_t UI_INITIAL_CREATE_SAVE;
|
||||||
|
|
||||||
|
static void uiInitialCreateSaveSelected(
|
||||||
|
const uimenu_t *menu,
|
||||||
|
const uint8_t index,
|
||||||
|
const uimenuitem_t *item
|
||||||
|
) {
|
||||||
|
UI_INITIAL_CREATE_SAVE.create = index == UI_INITIAL_CREATE_SAVE_INDEX_YES;
|
||||||
|
uiInitialCreateSaveClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uiInitialCreateSaveClosed(const uimenu_t *menu) {
|
||||||
|
if(UI_INITIAL_CREATE_SAVE.callback != NULL) {
|
||||||
|
UI_INITIAL_CREATE_SAVE.callback(
|
||||||
|
UI_INITIAL_CREATE_SAVE.create, UI_INITIAL_CREATE_SAVE.user
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiInitialCreateSaveInit(void) {
|
||||||
|
memoryZero(&UI_INITIAL_CREATE_SAVE, sizeof(uiinitialcreatesave_t));
|
||||||
|
|
||||||
|
MENU_BEGIN(
|
||||||
|
&UI_INITIAL_CREATE_SAVE.menu, UI_INITIAL_CREATE_SAVE.items,
|
||||||
|
uiInitialCreateSaveSelected, uiInitialCreateSaveClosed, NULL
|
||||||
|
);
|
||||||
|
MENU_BUTTON("Yes");
|
||||||
|
MENU_BUTTON("No");
|
||||||
|
|
||||||
|
MENU_END(UI_INITIAL_CREATE_SAVE.items, menuIndex);
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiInitialCreateSaveDraw(void) {
|
||||||
|
if(!uiMenuIsActive(&UI_INITIAL_CREATE_SAVE.menu)) errorOk();
|
||||||
|
|
||||||
|
spritebatchsprite_t backdropSprite = {
|
||||||
|
.min = { 0.0f, 0.0f, 0.0f },
|
||||||
|
.max = { (float_t)SCREEN.width, (float_t)SCREEN.height, 0.0f },
|
||||||
|
.uvMin = { 0.0f, 0.0f },
|
||||||
|
.uvMax = { 1.0f, 1.0f }
|
||||||
|
};
|
||||||
|
shadermaterial_t backdropMaterial = {
|
||||||
|
.unlit = {
|
||||||
|
.color = UI_INITIAL_CREATE_SAVE_BACKDROP_COLOR,
|
||||||
|
.texture = &TEXTURE_WHITE
|
||||||
|
}
|
||||||
|
};
|
||||||
|
errorChain(
|
||||||
|
spriteBatchBuffer(&backdropSprite, 1, &SHADER_UNLIT, backdropMaterial)
|
||||||
|
);
|
||||||
|
errorChain(spriteBatchFlush());
|
||||||
|
|
||||||
|
int32_t textW, textH;
|
||||||
|
textMeasure(UI_INITIAL_CREATE_SAVE_TEXT, &FONT_DEFAULT, &textW, &textH);
|
||||||
|
|
||||||
|
float_t rowHeight = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
||||||
|
float_t width = mathMax(
|
||||||
|
(float_t)textW + (UI_FRAME_START_X * 2), UI_INITIAL_CREATE_SAVE_MIN_WIDTH
|
||||||
|
);
|
||||||
|
float_t height = (UI_FRAME_START_Y * 2) + rowHeight + UI_FRAME_PADDING_Y +
|
||||||
|
rowHeight;
|
||||||
|
float_t x = (float_t)SCREEN.scanX + ((float_t)SCREEN.scanWidth - width) * 0.5f;
|
||||||
|
float_t y = (float_t)SCREEN.scanY + ((float_t)SCREEN.scanHeight - height) * 0.5f;
|
||||||
|
|
||||||
|
errorChain(uiFrameDraw(x, y, width, height));
|
||||||
|
|
||||||
|
float_t contentX = x + UI_FRAME_START_X;
|
||||||
|
float_t contentY = y + UI_FRAME_START_Y;
|
||||||
|
float_t contentWidth = width - (UI_FRAME_START_X * 2);
|
||||||
|
|
||||||
|
errorChain(textDraw(
|
||||||
|
contentX, contentY, UI_INITIAL_CREATE_SAVE_TEXT, COLOR_WHITE, &FONT_DEFAULT
|
||||||
|
));
|
||||||
|
|
||||||
|
float_t buttonsY = contentY + rowHeight + UI_FRAME_PADDING_Y;
|
||||||
|
errorChain(uiMenuDraw(
|
||||||
|
&UI_INITIAL_CREATE_SAVE.menu, contentX, buttonsY, contentWidth, rowHeight
|
||||||
|
));
|
||||||
|
|
||||||
|
errorChain(spriteBatchFlush());
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t uiInitialCreateSaveIsOpen(void) {
|
||||||
|
return uiMenuIsActive(&UI_INITIAL_CREATE_SAVE.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiInitialCreateSaveOpen(
|
||||||
|
uiinitialcreatesavecallback_t callback, void *user
|
||||||
|
) {
|
||||||
|
UI_INITIAL_CREATE_SAVE.callback = callback;
|
||||||
|
UI_INITIAL_CREATE_SAVE.user = user;
|
||||||
|
UI_INITIAL_CREATE_SAVE.create = false;
|
||||||
|
uiMenuOpen(&UI_INITIAL_CREATE_SAVE.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiInitialCreateSaveClose(void) {
|
||||||
|
uiMenuClose(&UI_INITIAL_CREATE_SAVE.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiInitialCreateSaveDispose(void) {
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "error/error.h"
|
||||||
|
#include "ui/widget/uimenu.h"
|
||||||
|
|
||||||
|
#define UI_INITIAL_CREATE_SAVE_INDEX_YES 0
|
||||||
|
#define UI_INITIAL_CREATE_SAVE_INDEX_NO 1
|
||||||
|
#define UI_INITIAL_CREATE_SAVE_ITEM_COUNT 2
|
||||||
|
#define UI_INITIAL_CREATE_SAVE_MIN_WIDTH 160.0f
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback invoked once the create-save modal is dismissed.
|
||||||
|
*
|
||||||
|
* @param create True if Yes was selected, false if No was selected or
|
||||||
|
* the dialog was backed out of.
|
||||||
|
* @param user Arbitrary pointer passed to uiInitialCreateSaveOpen.
|
||||||
|
*/
|
||||||
|
typedef void (*uiinitialcreatesavecallback_t)(const bool_t create, void *user);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uimenu_t menu;
|
||||||
|
uimenuitem_t items[UI_INITIAL_CREATE_SAVE_ITEM_COUNT];
|
||||||
|
uiinitialcreatesavecallback_t callback;
|
||||||
|
void *user;
|
||||||
|
bool_t create;
|
||||||
|
} uiinitialcreatesave_t;
|
||||||
|
|
||||||
|
extern uiinitialcreatesave_t UI_INITIAL_CREATE_SAVE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the create-save modal.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiInitialCreateSaveInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the create-save modal: a semi-transparent black backdrop covering
|
||||||
|
* the whole screen, then its own centered frame with a fixed message and
|
||||||
|
* Yes/No buttons. No-op when not open.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiInitialCreateSaveDraw(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when the modal is currently open.
|
||||||
|
*
|
||||||
|
* @return True if open.
|
||||||
|
*/
|
||||||
|
bool_t uiInitialCreateSaveIsOpen(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the create-save modal. callback is invoked exactly once with the
|
||||||
|
* result, whether dismissed by selecting a button or backing out.
|
||||||
|
*
|
||||||
|
* @param callback Called with the result once the modal closes. May be
|
||||||
|
* NULL.
|
||||||
|
* @param user Arbitrary pointer passed through to callback.
|
||||||
|
*/
|
||||||
|
void uiInitialCreateSaveOpen(uiinitialcreatesavecallback_t callback, void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the modal. No-op when already closed.
|
||||||
|
*/
|
||||||
|
void uiInitialCreateSaveClose(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes of the modal.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiInitialCreateSaveDispose(void);
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "uiinitialnocard.h"
|
||||||
|
#include "ui/frame/uiframe.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "util/math.h"
|
||||||
|
#include "display/screen/screen.h"
|
||||||
|
#include "display/text/text.h"
|
||||||
|
#include "display/color.h"
|
||||||
|
#include "display/spritebatch/spritebatch.h"
|
||||||
|
#include "display/texture/texture.h"
|
||||||
|
#include "display/shader/shaderunlit.h"
|
||||||
|
|
||||||
|
#define UI_INITIAL_NO_CARD_BACKDROP_COLOR color4b(0, 0, 0, 160)
|
||||||
|
#define UI_INITIAL_NO_CARD_TEXT \
|
||||||
|
"No save device found. You can continue, but\nprogress will not be saved."
|
||||||
|
|
||||||
|
uiinitialnocard_t UI_INITIAL_NO_CARD;
|
||||||
|
|
||||||
|
static void uiInitialNoCardSelected(
|
||||||
|
const uimenu_t *menu,
|
||||||
|
const uint8_t index,
|
||||||
|
const uimenuitem_t *item
|
||||||
|
) {
|
||||||
|
UI_INITIAL_NO_CARD.retry = index == UI_INITIAL_NO_CARD_INDEX_RETRY;
|
||||||
|
uiInitialNoCardClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uiInitialNoCardClosed(const uimenu_t *menu) {
|
||||||
|
if(UI_INITIAL_NO_CARD.callback != NULL) {
|
||||||
|
UI_INITIAL_NO_CARD.callback(UI_INITIAL_NO_CARD.retry, UI_INITIAL_NO_CARD.user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiInitialNoCardInit(void) {
|
||||||
|
memoryZero(&UI_INITIAL_NO_CARD, sizeof(uiinitialnocard_t));
|
||||||
|
|
||||||
|
MENU_BEGIN(
|
||||||
|
&UI_INITIAL_NO_CARD.menu, UI_INITIAL_NO_CARD.items,
|
||||||
|
uiInitialNoCardSelected, uiInitialNoCardClosed, NULL
|
||||||
|
);
|
||||||
|
MENU_BUTTON("Retry");
|
||||||
|
MENU_BUTTON("Continue Anyway");
|
||||||
|
|
||||||
|
MENU_END(UI_INITIAL_NO_CARD.items, menuIndex);
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiInitialNoCardDraw(void) {
|
||||||
|
if(!uiMenuIsActive(&UI_INITIAL_NO_CARD.menu)) errorOk();
|
||||||
|
|
||||||
|
spritebatchsprite_t backdropSprite = {
|
||||||
|
.min = { 0.0f, 0.0f, 0.0f },
|
||||||
|
.max = { (float_t)SCREEN.width, (float_t)SCREEN.height, 0.0f },
|
||||||
|
.uvMin = { 0.0f, 0.0f },
|
||||||
|
.uvMax = { 1.0f, 1.0f }
|
||||||
|
};
|
||||||
|
shadermaterial_t backdropMaterial = {
|
||||||
|
.unlit = {
|
||||||
|
.color = UI_INITIAL_NO_CARD_BACKDROP_COLOR,
|
||||||
|
.texture = &TEXTURE_WHITE
|
||||||
|
}
|
||||||
|
};
|
||||||
|
errorChain(
|
||||||
|
spriteBatchBuffer(&backdropSprite, 1, &SHADER_UNLIT, backdropMaterial)
|
||||||
|
);
|
||||||
|
errorChain(spriteBatchFlush());
|
||||||
|
|
||||||
|
int32_t textW, textH;
|
||||||
|
textMeasure(UI_INITIAL_NO_CARD_TEXT, &FONT_DEFAULT, &textW, &textH);
|
||||||
|
|
||||||
|
float_t rowHeight = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
||||||
|
float_t width = mathMax(
|
||||||
|
(float_t)textW + (UI_FRAME_START_X * 2), UI_INITIAL_NO_CARD_MIN_WIDTH
|
||||||
|
);
|
||||||
|
float_t height = (UI_FRAME_START_Y * 2) + (float_t)textH +
|
||||||
|
UI_FRAME_PADDING_Y + rowHeight;
|
||||||
|
float_t x = (float_t)SCREEN.scanX + ((float_t)SCREEN.scanWidth - width) * 0.5f;
|
||||||
|
float_t y = (float_t)SCREEN.scanY + ((float_t)SCREEN.scanHeight - height) * 0.5f;
|
||||||
|
|
||||||
|
errorChain(uiFrameDraw(x, y, width, height));
|
||||||
|
|
||||||
|
float_t contentX = x + UI_FRAME_START_X;
|
||||||
|
float_t contentY = y + UI_FRAME_START_Y;
|
||||||
|
float_t contentWidth = width - (UI_FRAME_START_X * 2);
|
||||||
|
|
||||||
|
errorChain(textDraw(
|
||||||
|
contentX, contentY, UI_INITIAL_NO_CARD_TEXT, COLOR_WHITE, &FONT_DEFAULT
|
||||||
|
));
|
||||||
|
|
||||||
|
float_t buttonsY = contentY + (float_t)textH + UI_FRAME_PADDING_Y;
|
||||||
|
errorChain(uiMenuDraw(
|
||||||
|
&UI_INITIAL_NO_CARD.menu, contentX, buttonsY, contentWidth, rowHeight
|
||||||
|
));
|
||||||
|
|
||||||
|
errorChain(spriteBatchFlush());
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t uiInitialNoCardIsOpen(void) {
|
||||||
|
return uiMenuIsActive(&UI_INITIAL_NO_CARD.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiInitialNoCardOpen(uiinitialnocardcallback_t callback, void *user) {
|
||||||
|
UI_INITIAL_NO_CARD.callback = callback;
|
||||||
|
UI_INITIAL_NO_CARD.user = user;
|
||||||
|
UI_INITIAL_NO_CARD.retry = false;
|
||||||
|
uiMenuOpen(&UI_INITIAL_NO_CARD.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiInitialNoCardClose(void) {
|
||||||
|
uiMenuClose(&UI_INITIAL_NO_CARD.menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiInitialNoCardDispose(void) {
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "error/error.h"
|
||||||
|
#include "ui/widget/uimenu.h"
|
||||||
|
|
||||||
|
#define UI_INITIAL_NO_CARD_INDEX_RETRY 0
|
||||||
|
#define UI_INITIAL_NO_CARD_INDEX_CONTINUE 1
|
||||||
|
#define UI_INITIAL_NO_CARD_ITEM_COUNT 2
|
||||||
|
#define UI_INITIAL_NO_CARD_MIN_WIDTH 220.0f
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback invoked once the no-save-device modal is dismissed.
|
||||||
|
*
|
||||||
|
* @param retry True if Retry was selected, false if Continue Anyway was.
|
||||||
|
* @param user Arbitrary pointer passed to uiInitialNoCardOpen.
|
||||||
|
*/
|
||||||
|
typedef void (*uiinitialnocardcallback_t)(const bool_t retry, void *user);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uimenu_t menu;
|
||||||
|
uimenuitem_t items[UI_INITIAL_NO_CARD_ITEM_COUNT];
|
||||||
|
uiinitialnocardcallback_t callback;
|
||||||
|
void *user;
|
||||||
|
bool_t retry;
|
||||||
|
} uiinitialnocard_t;
|
||||||
|
|
||||||
|
extern uiinitialnocard_t UI_INITIAL_NO_CARD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the no-save-device modal.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiInitialNoCardInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the no-save-device modal: a semi-transparent black backdrop
|
||||||
|
* covering the whole screen, then its own centered frame with a fixed
|
||||||
|
* message and Retry/Continue Anyway buttons. No-op when not open.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiInitialNoCardDraw(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when the modal is currently open.
|
||||||
|
*
|
||||||
|
* @return True if open.
|
||||||
|
*/
|
||||||
|
bool_t uiInitialNoCardIsOpen(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the no-save-device modal. callback is invoked exactly once with
|
||||||
|
* the result, whether dismissed by selecting a button or backing out.
|
||||||
|
*
|
||||||
|
* @param callback Called with the result once the modal closes. May be
|
||||||
|
* NULL.
|
||||||
|
* @param user Arbitrary pointer passed through to callback.
|
||||||
|
*/
|
||||||
|
void uiInitialNoCardOpen(uiinitialnocardcallback_t callback, void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the modal. No-op when already closed.
|
||||||
|
*/
|
||||||
|
void uiInitialNoCardClose(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes of the modal.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiInitialNoCardDispose(void);
|
||||||
@@ -21,6 +21,8 @@
|
|||||||
#include "ui/frame/battle/uibattlemenu.h"
|
#include "ui/frame/battle/uibattlemenu.h"
|
||||||
#include "ui/frame/backpack/uibackpack.h"
|
#include "ui/frame/backpack/uibackpack.h"
|
||||||
#include "ui/frame/uiconfirm.h"
|
#include "ui/frame/uiconfirm.h"
|
||||||
|
#include "ui/frame/initial/uiinitialnocard.h"
|
||||||
|
#include "ui/frame/initial/uiinitialcreatesave.h"
|
||||||
#include "ui/rpg/textbox/uitextboxmain.h"
|
#include "ui/rpg/textbox/uitextboxmain.h"
|
||||||
#include "ui/rpg/textbox/uitextboxminilist.h"
|
#include "ui/rpg/textbox/uitextboxminilist.h"
|
||||||
#include "ui/rpg/uiemoji.h"
|
#include "ui/rpg/uiemoji.h"
|
||||||
@@ -79,6 +81,18 @@ uielement_t UI_ELEMENTS[] = {
|
|||||||
.dispose = uiConfirmDispose
|
.dispose = uiConfirmDispose
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.init = uiInitialNoCardInit,
|
||||||
|
.draw = uiInitialNoCardDraw,
|
||||||
|
.dispose = uiInitialNoCardDispose
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.init = uiInitialCreateSaveInit,
|
||||||
|
.draw = uiInitialCreateSaveDraw,
|
||||||
|
.dispose = uiInitialCreateSaveDispose
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
.init = uiTextboxMainInit,
|
.init = uiTextboxMainInit,
|
||||||
.update = uiTextboxMainUpdate,
|
.update = uiTextboxMainUpdate,
|
||||||
|
|||||||
Reference in New Issue
Block a user