Commit Graph

957 Commits

Author SHA1 Message Date
YourWishes fb48285143 Rebuild battle flow as an explicit state machine with cutscene hooks
Replace the one-fighter-at-a-time turn model with an OPENING/PRE_ROUND/
PLAYER_SELECTION/AI_SELECTION/MOVES_EXECUTING/POST_ROUND/ENDED state
machine and a per-fighter action queue, so actions are decided before any
of them execute (needed for speed-ordered resolution) and so a cutscene
can pause the battle, wait for a specific state, and force a fighter's
action -- enabling automated, fully-scripted, and partially-scripted
battles. Adds CUTSCENE_PAUSE_BATTLE plus CUTSCENE_BATTLE_WAIT_STATE and
CUTSCENE_BATTLE_FORCE_ACTION cutscene items, and generic onStateChanged/
onActionDecided callbacks on battle_t. Re-enables the long-dormant
test/rpg suite and adds test/rpg/battle covering the state machine and
the new cutscene hooks end-to-end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 08:49:42 -05:00
YourWishes 3de50b8370 Lock in Chapter 1's literal opening scene
The village square at the mountain's foot, party roles established
(tank/paladin leader, ranged ranger, support mage, melee rogue), the
funds bicker, and the cowgirl teleporter's too-good-to-question,
one-way-only offer up the mountain - the innocent-sounding line that
seeds the whole chapter's central complication. Adds character stubs
for the three other party members and the teleporter, and confirms
the bubbly ranger as the first to abandon the mission.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 08:11:54 -05:00
YourWishes e61cbe25b7 Add three of the mentor's other former squadron members
Introduces Strider (a merc who protects those who can't afford real
protection, and has found genuine peace in it), Downes (retired to
farming and family, forced back into one last fight), and Egor (Royal
Guard, honor curdled into blind devotion, dies still certain of
himself). Together with the party leader, they give the same
mentor/same night four distinct answers to what honor means. Updates
themes and open questions accordingly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 06:54:49 -05:00
YourWishes 8395830be6 Introduce the Detective, kept deliberately thin by design
Reveals him as the presence following the party since Chapter 1 - a
king's agent who frees the protagonist from execution in early
Chapter 2, drops a small, cagey lead to the newly-dismissed party
leader, and then vanishes to chase other threads. Documents these
early Chapter 2 beats, notes culinary magic is now branded "the
forbidden magic" per the king's cover story, and updates open
questions/themes accordingly. His entry is intentionally sparse -
the character is meant to stay a mystery for as long as possible.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 23:47:50 -05:00
YourWishes 6e4ec2b9d8 Add the party leader's backstory and his mentor
Ties the party leader's rigid, orders-above-all sense of honor back
to a mentor he idolized as a young soldier - one who broke orders on
the night of the Great Explosion to keep his squadron out of the
massacre, then died in it. The party leader doesn't know why at the
time; his eventual realization that his mentor's disobedience was
itself the honorable act becomes his arc's throughline. Also notes
the rest of that squadron, now scattered, as a future plot thread.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 23:37:18 -05:00
YourWishes f501bb8e28 Detail the true events of the Great Explosion
Replaces the vague placeholder with the full account: the king's
personal, in-person purge of the culinary mages' hometown, the failed
hostage surrender, the town's collective final spell (built on a
shared folk song) that becomes the Great Explosion, and the
protagonist's survival via her mother's protective spell. Also adds
the king's official cover story (framing the mages as insurgents),
new characters (the Great Mage and the protagonist's mother), and
updates themes/open questions accordingly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 23:29:39 -05:00
YourWishes d07cd3397d Start documenting the story: premise, structure, and Chapter 1
Adds story.md covering the overarching premise (a prophecy caused by
the attempt to prevent it), the eight-movement story structure, the
Great Explosion backstory, core characters, and a detailed breakdown
of Chapter 1's opening - the bounty party's retrieval of the
protagonist, the mountain teleportation-logistics trap that splits
the party, and the confrontation with the king.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 23:16:46 -05:00
YourWishes e008fb108a Embed default font as a hard-coded bitmap instead of loading it as an asset
Moves FONT_DEFAULT and its init/dispose into their own font.h/font.c,
and rebuilds it from a static glyph bit array + a runtime-generated
texture/tileset rather than loading ui/minogram.png/.dtf through the
asset system. The engine now always has a usable font to render with,
even if asset loading fails. The now-unused minogram assets are removed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 22:37:22 -05:00
YourWishes 9aaffff7a8 Add autosave system with forced no-save-device prompt on failure
autoSaveQueue() is a single callable entry point for requesting an
autosave; autoSaveUpdate() pumps the queue each frame and, if the write
fails (e.g. no memory card), forces the existing no-card modal back
open and pauses world simulation until the player retries or accepts a
temporary session. A small overlay shows "SAVING" while a write is in
flight.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 22:05:43 -05:00
YourWishes 7357b4a5df Localize remaining hardcoded UI strings and persist language preference
Replaces the last hardcoded English strings (initial-scene modals, game
menu save status messages) with locale-loaded text, adds a shared
LOCALE_LIST so the settings dropdown and locale manager stay in sync,
and persists the chosen language into savemeta_t across all platforms.
Also fixes two message buffers that were too small for their longest
translations.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 21:39:24 -05:00
YourWishes 1ddc298a74 Only update overworld entities while the overworld scene is active
Entities (and the player's pause-to-open-game-menu handling with them)
previously kept updating every frame regardless of the active scene,
matching an existing TODO in rpgUpdate(). Gating the entity loop itself
means the game menu (and any other entity-driven input) naturally can't
trigger mid-battle or before the initial scene hands off to the
overworld, without needing a scene check at each individual call site.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 20:54:58 -05:00
YourWishes e2a9442aa6 Add initial boot scene to check/prompt for save data before overworld
A new SCENE_TYPE_INITIAL now runs before the overworld: it checks
save-device availability and existing save data, then shows one of two
new dedicated modals - "no save device found" (Retry / Continue Anyway)
or "no save data found, create one?" (Yes / No) - before handing off to
the overworld. Both modals are self-contained UI elements mirroring
uiconfirm.h's shape, registered like any other global UI element.

Choosing "Continue Anyway" marks the session temporary (SAVE.temporary,
folded into saveIsAvailable()) so saving stays disabled for the rest of
the session instead of silently retrying, and the game menu's Save
action now reports that distinctly instead of the generic "no device"
message.

Also removes rpg.c's leftover TEST block (unconditional player-name
stamp + save write on every boot) now that this real flow owns save
creation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 20:53:01 -05:00
YourWishes aa0180571e Unify save system into one save.h/.c; diverge storage format per platform
Renames savefile_t to saveslot_t and folds last session's standalone
settings.h/.c module back in as savemeta_t, so there's one save system
(SAVE.slots[] + SAVE.meta) instead of two parallel ones - while letting
each platform pick its own physical format for the two concepts:

- Linux now writes human-editable JSON (slot0.json, settings.json, ...)
  via yyjson's mutable writer API, so players can hand-fix a bad setting.
- PSP folds meta into the same sceUtilitySavedata binary payload as its
  one save slot (SAVE_SLOT_COUNT_MAX=1 there - a future save picker will
  let players manage multiple named saves via the OS's own browser).
- GameCube consolidates the 3 per-slot memory card files and the separate
  settings file into one combined card file.

Also fixes two bugs surfaced while building this: the CRC finalize step
seeked to a hardcoded offset (only safe for one section per file, breaks
once meta+slots share a buffer), and save.c's async/sync dispatch left an
unconditional fallback call that doesn't exist on PSP-only platforms.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 18:35:06 -05:00
YourWishes 7f7be39230 Split device settings out of the per-slot save file
Deadzone (and future prefs like locale) now live in their own
settingsfile_t/settings.c, loaded eagerly at boot and saved immediately
on Apply, instead of inside savefile_t - a setting shouldn't reset or
diverge just because the player is on a different save slot, and this
also fixes settings changes not actually reaching disk until the next
full game Save.

PSP settings use a new plain sceIo path rather than sceUtilitySavedata,
since that dialog would flash its native icon on every settings tweak.
GameCube reuses the save system's existing memory card mount rather than
mounting it twice (settingsInit() now runs after saveInit()).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 13:54:15 -05:00
YourWishes 4d95415232 Move global item collected-state, deadzone, and story flags into save file
Adds three new pieces of save-file state, all following the same shape:
the save file is the single source of truth, not a separate live runtime
copy that gets synced in/out.

- globalitemstore.h/.c: per-global-entity-ID "collected" flags
  (savefile_t.globalItemCollected), so a global item entity's init
  callback can check whether it was already picked up in a prior session
  without needing to keep the entity itself alive to remember that.

- Gamepad deadzone: removed input_t.deadzone entirely. The setting UI and
  every platform's actual deadzone-applying code (inputGetDeadzoneDolphin/
  SDL2, previously hardcoded per-platform literals that the settings menu
  didn't actually affect) now read savefile_t.deadzone directly via
  saveGet(SAVE_ACTIVE_SLOT). Default lives in savefile.h
  (SAVE_DEADZONE_DEFAULT), stamped onto every slot in saveInit().

- Story flags: STORY_FLAG_VALUES (a live, codegen-initialized array) is
  replaced by savefile_t.storyFlags, read/written via the existing
  storyFlagGet()/storyFlagSet() call sites (now macros/functions over the
  active save file instead of a separate array). tools/story.py now
  generates STORY_FLAG_DEFAULTS (const) instead; storyFlagInitDefaults()
  stamps those onto a save the first time it's used (file->exists false),
  called from rpgInit().

Added SAVE_ACTIVE_SLOT (0) to savefile.h as the one shared "which slot is
actually being played" constant, replacing three different local/implicit
0s (uigamemenu.c, rpg.c, and now the settings/input call sites).

Verified round-trip on Linux (all three together in one save/load cycle);
both Linux and PSP build clean.
2026-08-04 11:10:44 -05:00
YourWishes 2cbd80a004 Check for existing save data before saving, confirm before creating new
uiGameMenuSave() now attempts a real load first (via the existing generic
saveLoad()/saveExists() primitives) instead of writing blind. If a save
already exists, it saves straight over it as before. If not, it prompts
via the existing uiConfirm dialog ("No save data found. Create a new
save?") before writing - this is exactly the flow GameCube needs (no
native OS save browser to lean on, unlike PSP), but implemented generically
so it also applies correctly on every other platform without any
platform-specific UI code: saveIsAvailable()/saveExists() already reflect
each platform's real state (e.g. Dolphin's memory-card presence and
existing-file checks), so the same logic just does the right thing
everywhere.

Verified the two branches directly on Linux (temporarily wiring the same
saveLoad -> check -> uiConfirmOpen sequence into rpgInit): with no save
file, saveExists() is false and the confirm dialog opens; with one already
written, it's true and the confirm dialog is correctly skipped. Not
verified via actual menu navigation (no input-injection tooling available
here) or on Dolphin (no devkitPPC toolchain in this environment).
2026-08-04 10:33:57 -05:00
YourWishes 9abf8101da PSP: save through the real sceUtilitySavedata API, not raw file I/O
Rewrote savepsp.c/savestreampsp.c to use sceUtilitySavedataInitStart/
Update/GetStatus/ShutdownStart instead of sceIoOpen/Read/Write, so PSP
saves get a proper OS-generated PARAM.SFO (title/savedataTitle/detail) and
show up correctly in the native save browser.

This dialog spans multiple frames and, per this project's prior experience
with the network config dialog, must be pumped non-blocking one step per
real engine frame rather than blocked on synchronously - a raw-sceGu
blocking loop already froze the app on real hardware for that dialog,
since pspGL owns the GU context. So save.h's saveWrite()/saveLoad() are
now callback-based (savecallback_t onComplete) instead of returning a
result directly, mirroring networkRequestConnection()'s shape, with a new
saveUpdate() (wired into engineUpdate()) pumping the active op each frame.
Linux/Dolphin behavior is unchanged - their fallback path in save.c still
completes synchronously, just via an immediate callback call instead of a
direct return.

Two real bugs found via PPSSPP testing (not just code review): SAVE/LOAD
modes show a confirm screen even for brand-new data, which blocks forever
headlessly - switched to AUTOSAVE/AUTOLOAD, which write/read silently and
generate the identical PARAM.SFO. And PPSSPP's dialog status goes straight
from QUIT to NONE without a separately observable FINISHED in between,
which the first version misread as "disappeared without a result" even on
a successful save - fixed by tracking whether QUIT was already seen.

Confirmed end-to-end in PPSSPP: write, dialog completes, PARAM.SFO +
encrypted save.bin appear on the virtual memory stick, and a subsequent
load decrypts/deserializes back to the exact original data. Not tested on
real PSP hardware.
2026-08-04 09:54:45 -05:00
YourWishes 24badd06a5 Add player name field to save file, save it out as a round-trip test
Adds savefile_t.playerName (SAVE_PLAYER_NAME_MAX) serialized via the
existing saveFileReadString/WriteString helpers, and stamps + saves it in
rpgInit() as a test that the save system now persists actual game data,
not just the header/version. Verified manually: written bytes end in
"Dusk\0" immediately after the version field, and loading it back returns
the same string.
2026-08-04 08:59:29 -05:00
YourWishes 7a03ef8eaf Re-enable save system, fix header/version stamping, handle missing media
- Fixed the actual reason saving never worked on any platform: saveWrite()
  never stamped file->header/file->version before serializing, so every
  written save file had a zeroed magic header and failed its own
  validation on the next load. Confirmed via a manual write/load round
  trip that this alone fully explains "saving doesn't work."
- Re-enabled saveInit()/saveDispose() in engine.c (previously commented out
  under "Temporarily disable save code").
- Added SAVE.available + saveIsAvailable(), refreshed by every real
  save/load/delete attempt. saveInit() no longer treats an unreachable
  save medium as fatal to booting - it logs and continues, since a missing
  memory card/stick shouldn't prevent playing.
- Hardened PSP's saveInitPSP() to actually detect a missing memory stick
  (sceIoGetstat on ms0:/) instead of assuming success, and fixed
  single-level sceIoMkdir to build the full PSP/SAVEDATA directory chain.
- Added busy-retry (CARD_ERROR_BUSY) and a not-mounted guard to Dolphin's
  live savestreamdolphin.c path, extending the same handling already
  backported into savedolphin.c.
- Added a "Save" entry to the game menu wired to saveWrite(0), showing a
  clear message on success, on failure, and when saveIsAvailable() is false.
2026-08-04 08:30:24 -05:00
YourWishes f3ea507313 Fixed save crash
Backported from branch ac2 (commit 85b61097) - CARD_Mount was being called
without CARD_Init first, leaving per-channel control blocks and the DSP
unlock sequence unset. On real Dolphin/hardware this surfaced as a hard
MMIO crash instead of a clean CARD_ERROR_* failure.

Co-Authored-By: Dominic Masters <dominic@domsplace.com>
2026-08-04 08:12:57 -05:00
YourWishes 4b0388a0e1 Chunk streaming concurrency, entity slot fix, and map-data-driven spawns
- Allow 2 chunks to be mid-load concurrently instead of 1 (MAP_CHUNK_LOAD_CONCURRENCY).
- Fix entitySetChunk silently losing track of an entity when its target chunk's
  entity slots are full - it now stays detached (and retries later) instead of
  claiming a chunk that never actually registered it.
- DCF format bumped to v5: chunks can now declare entity spawns (global/NPC via
  the existing entityglobal registry, or one-shot item pickups) and map area
  triggers, resolved via a new callback-ID registry (mapareagloballist.h)
  mirroring the entity one. rpg.c's hardcoded TEST entity/item/area spawns are
  gone - chunk_0_0_0.json now carries that data instead. The player is still
  bootstrapped in code since it isn't map-authored content.
2026-08-04 07:37:48 -05:00
YourWishes a84137b5ff del md 2026-08-04 06:58:03 -05:00
YourWishes ca02ee0352 Add camera shake 2026-07-11 11:02:09 -05:00
YourWishes fbaa54145e Fixed dolphin rendering. 2026-07-10 20:15:26 -05:00
YourWishes 28754ffbf2 Removed old scripted types 2026-07-10 13:24:21 -05:00
YourWishes 470c0eba7a Whatever, some minor map chunking improvements 2026-07-10 12:57:31 -05:00
YourWishes 7098dcec43 Cleaned some log 2026-07-09 23:25:43 -05:00
YourWishes 07137f57af Assets are slightly optimized 2026-07-09 23:25:26 -05:00
YourWishes 8b7491a3d3 Emoji support to characters 2026-07-09 13:18:48 -05:00
YourWishes 8cfa8ddfeb Weaather baseline 2026-07-08 12:57:13 -05:00
YourWishes ef284a15a1 pre-cache shader matrices 2026-07-08 12:36:40 -05:00
YourWishes 3723921573 Render culling on sceneoverworld.h 2026-07-08 12:02:41 -05:00
YourWishes 195399635e Updating mini textbox 2026-07-08 11:45:10 -05:00
YourWishes 46e2a924d3 Mini textboxes 2026-07-08 10:53:53 -05:00
YourWishes b693ea4102 Starting item and battle stuff 2026-07-08 10:05:21 -05:00
YourWishes a73f55beb0 Battle stuff
Build Dusk / build-linux (push) Successful in 7m5s
Build Dusk / build-psp (push) Successful in 1m33s
Build Dusk / build-knulli (push) Successful in 4m0s
Build Dusk / build-gamecube (push) Successful in 3m31s
Build Dusk / build-gamecube-iso (push) Successful in 3m39s
Build Dusk / build-wii (push) Successful in 3m5s
Build Dusk / build-wii-iso (push) Successful in 3m25s
alpha-0.0.7
2026-07-08 07:55:15 -05:00
YourWishes 0bd2491ab7 Dropdown test. 2026-07-07 15:18:33 -05:00
YourWishes 860c797c9c Last round of cleanup for settings for now. 2026-07-07 14:56:08 -05:00
YourWishes 38c5080f9f Collapsing more ui settings code 2026-07-07 14:36:19 -05:00
YourWishes fae191d8fe Settings improvements 2026-07-07 14:00:37 -05:00
YourWishes d83a953e2d First pass of language 2026-07-07 11:14:02 -05:00
YourWishes 2dcf0d0f0d UI Confirm 2026-07-07 10:12:22 -05:00
YourWishes 6dee37d8c1 Slider widget and some cleanup 2026-07-07 09:58:10 -05:00
YourWishes 3bad03afb3 Item entity 2026-07-07 07:52:22 -05:00
YourWishes 6a6d8448f7 Area cutscene controls 2026-07-07 07:28:12 -05:00
YourWishes 189babd2cf Trigger types on areas 2026-07-07 07:07:32 -05:00
YourWishes 988a0f2294 Area basics 2026-07-06 23:19:05 -05:00
YourWishes 85bf455731 entity pos sets chunk now 2026-07-04 18:43:09 -05:00
YourWishes 589e4224f3 Example global ent 2026-07-04 13:03:24 -05:00
YourWishes bd7fa154b4 Add user data to callbacks 2026-07-04 09:13:37 -05:00