Commit Graph
1083 Commits
Author SHA1 Message Date
YourWishesandClaude Sonnet 5 f64364f37d Trim cutscenesystem.h doc comments to the essentials
Comments-only change - drops implementation rationale/cross-referencing
detail in favor of shorter one-liners, matching the simplified style
already applied to part of the file.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-18 11:21:12 -05:00
YourWishesandClaude Sonnet 5 d16e0bbfbf Stop stealing/reaping cutscene JSON docs - parse in place instead
cutsceneSystemLoad, cutsceneCutsceneResolve and
cutsceneInsertResolveAndSplice all used to steal ownership of the
asset entry's parsed yyjson_doc (nulling entry->data.json) and force
an immediate assetReapUnused() to avoid handing back a corrupted
cache entry on a later load of the same file - which doubled as a
latent crash if that reap was ever skipped (as it briefly was),
since a stale cache hit would then have a NULL doc.

Turns out none of that was needed: every cutsceneitem_t field that
could reference the doc's memory already copies its string data by
value (cutsceneJsonCopyString into fixed buffers), so the parsed
items don't depend on the doc surviving past cutsceneParseDoc. All
three call sites now parse straight out of entry->data.json while
still locked, then unlock normally - the asset entry stays a normal,
correctly-cached resource, and CUTSCENE_LOADED_DOC/cutsceneLoadedSetDoc
are gone entirely.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-18 09:47:05 -05:00
YourWishesandClaude Sonnet 5 70ae41bd29 Track the loaded cutscene filename and re-load it on restart
Adds CUTSCENE_SYSTEM.loadedFile, set by cutsceneSystemLoad and cleared
by cutsceneSystemPrepare. cutsceneRestart now re-reads and re-parses
that file via cutsceneSystemLoad instead of just rerunning whatever's
still resident in loadedScene/loadedItems, so restarting a file-loaded
cutscene always reflects its current on-disk contents - restart of a
C-authored cutscene is unchanged.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-18 09:28:15 -05:00
YourWishesandClaude Sonnet 5 abf6863044 Assert NULL pointer/marker args in cutscenesystem.c entry points
Matches the existing assertNotNull convention in cutscene.c
(cutsceneAppend/cutsceneParseDoc etc.) - these are internal-API
preconditions on trusted callers, not untrusted input.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-18 09:09:35 -05:00
YourWishesandClaude Sonnet 5 4279bd9675 Remove unused CUTSCENE_SYSTEM.userData scratch buffer
Nothing ever wrote to it - it only ever passed its own address through
to onComplete/CUTSCENE_CALLBACK/modal-option callbacks, so every call
site now passes NULL directly and the 8KB buffer is gone.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-17 22:32:09 -05:00
YourWishesandClaude Sonnet 5 5910110b95 Rework CUTSCENE_ITEM_TYPE_INSERT to splice into the running scene's item array
Replaces the separate insert buffer/scene and insertStack return-frame
mechanism with cutsceneAppend/cutsceneAppendNext, so an inserted
cutscene's items become part of the running scene's own array instead
of a jump-and-return. Moves the loaded-items buffer/scene from file-scope
globals into cutscenesystem_t, and extracts shared start-cutscene setup
into cutsceneSystemPrepare.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-17 22:26:36 -05:00
YourWishesandClaude Sonnet 5 44594f7011 Add cutsceneAppend/cutsceneAppendNext for mutating a running cutscene
cutsceneAppend adds items to the end of a cutscene_t's item array;
cutsceneAppendNext inserts them at a given index instead (e.g.
CUTSCENE_SYSTEM.currentItem + 1, to run immediately after the
currently-executing item), shifting everything from that index
onward to make room. Both are capacity-bounded against the caller-
supplied itemsMax, matching cutsceneParseDoc's convention of never
assuming ownership of the backing buffer's size.

cutscene_t.items drops its const qualifier to allow this in-place
mutation; cutsceneitem_t backing arrays in the test fixtures that get
assigned into a mutable cutscene_t.items are no longer declared const
either, since a const source would otherwise silently violate the
field's new type.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-15 13:16:02 -05:00
YourWishesandClaude Sonnet 5 dbb5301ade Extract battleactiontype_t/battleaction_t into battleaction.h
Matches the existing battleability.h/battlefighter.h split - keeps
battle.h focused on the overall battle_t state machine rather than
also owning the action data shape.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-14 12:28:40 -05:00
YourWishesandClaude Sonnet 5 74a4290be4 Add optional cutscene field to move schema
Lets a move reference a cutscene to play when used, resolved by name
the same way a CUTSCENE item's "name" field is. Still just a schema
placeholder - no loader consumes moves.jsonc yet.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-14 12:10:06 -05:00
YourWishesandClaude Sonnet 5 3ce892f846 Move items.jsonc under items/, add starter battle/moves.jsonc
Groups item data under assets/items/ to match the cutscenes/ folder
convention, updating itemInit()'s asset path accordingly. Also adds
assets/battle/moves.jsonc as a starting point for move data - there's
no JSON loader consuming it yet (battleability_t is still just a bare
enum), so its field shape is a placeholder rather than a contract.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-14 12:00:46 -05:00
YourWishesandClaude Sonnet 5 68cebce04a Remove per-item cutscene authoring macros
Every CUTSCENE_XXX(...) shorthand (CUTSCENE_WAIT, CUTSCENE_TEXT,
CUTSCENE_ENTITY_WALK_TO, etc.) existed to build a cutsceneitem_t literal
in C; real cutscenes are now authored exclusively as .jsonc files, and
the only other users were test fixtures. Deletes all ~55 of these
macros (and the shared CUTSCENE_ITEM(...) helper they built on) along
with their doc comments, leaving each item's struct/enum/Start/Update/
Load declarations untouched.

test_battle.c, test_cutscenecontrol.c, test_cutscenemaparea.c,
test_cutscenesystem.c and test_entityinteract.c now build their
cutsceneitem_t fixtures as plain struct literals instead.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-14 11:20:58 -05:00
YourWishesandClaude Sonnet 5 1646e964b5 Remove the C-authoring shorthand for declaring cutscenes
CUTSCENE(...)/CUTSCENE_REFERENCE/CUTSCENE_CUTSCENE/CUTSCENE_INSERT
existed to build a cutscene_t and its item array directly in C; real
cutscenes are now authored exclusively as .jsonc files parsed via
cutsceneParseDoc, so these are dead weight in production. Deletes the
demo scene that still used them (testcutscene.h, wired to NPC #3) along
with its now-dead include in npc.c. test_cutscenesystem.c - the only
other user, building cutscenes in-memory for tests - now writes the
equivalent struct literals directly, matching the convention already
used by test_battle.c/test_entityinteract.c.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-14 09:52:42 -05:00
YourWishesandClaude Sonnet 5 2ee97ad4ce Sunset the dedicated cutscene asset loader and the item pool
Cutscenes now load through the generic ASSET_LOADER_TYPE_JSON loader
and parse straight into fixed static buffers via new cutsceneParseDoc/
cutsceneParseItem/cutsceneCountItems helpers (cutscene.c), removing the
bespoke ASSET_LOADER_TYPE_CUTSCENE pipeline entirely. Only one cutscene
(plus one INSERT target) is ever resident, so each resolve locks,
steals the parsed doc, unlocks, and force-reaps immediately - fixing a
stale-cache-reuse bug where revisiting a cutscene name after a
different one could return data from the shared buffer's previous
occupant.

ENTITY_WALK_TO and MAP_AREA_WAIT now store their waypoints/area ids in
fixed-size arrays inline on their own item struct instead of a shared
bump-allocated pool, so pool/poolOffset are gone from every item type's
Load callback. cutscene_t.dataSize (and the CUTSCENE(...) macro's SIZE
parameter) is removed along with it - it sized entries out of that same
now-deleted pool and had no other reader.

NPC #4's battletest cutscene and the main menu's cutscene now resolve
their target by name on interact instead of eagerly at init, so a
resolved-once pointer is never held past the point its backing buffer
can be reused for something else.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-13 15:18:57 -05:00
YourWishesandClaude Sonnet 5 109ddc9e1e Switch item definitions to a JSON-driven runtime registry
Replaces the build-time item.json/tools.item.py code generator with a
single item_t ITEM registry populated at runtime by parsing
assets/items.jsonc, so items/types can grow without a rebuild. Adds
itemGetDefById/itemGetTypeById/itemGetIdByName/itemTypeGetOrCreate,
updates inventory/backpack to size off the new *_DEF_COUNT_MAX caps
and look up types dynamically, and reorders engine init so rpgInit
(which loads items) runs before uiInit (which needs the item type
count to size the backpack's tabs).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-13 11:36:50 -05:00
YourWishesandClaude Sonnet 5 804f8aa1fa Re-add CUTSCENE_INSERT item with its own dedicated load buffer
Splicing a JSON-authored INSERT target by name previously resolved
through the same shared CUTSCENE_LOADED_ITEMS/POOL buffer as whatever
outer cutscene was currently running, so loading it would clobber the
outer cutscene's own items mid-playback - that's why it was reverted.
Gives INSERT its own smaller CUTSCENE_INSERT_ITEMS/POOL buffer and lets
the cutscene asset loader be told which buffer set to parse into, so an
INSERT's dynamic load no longer stomps the running cutscene.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-13 11:12:43 -05:00
YourWishes 49b97ba508 Switch cutscene system to a set of pre deffed buffers 2026-09-13 09:58:57 -05:00
YourWishes 6c11d8165d Revert "Add CUTSCENE_INSERT item to splice another cutscene's items in place"
This reverts commit 6af0e9cf28.
2026-09-13 09:29:23 -05:00
YourWishesandClaude Sonnet 5 93554b39ba Add DUSK_TRACK_MEMORY allocation tracker and memoryCheckUnfreed report
memoryAllocate() becomes a macro capturing __FILE__/__LINE__ into a
fixed-size tracking table when DUSK_TRACK_MEMORY is enabled (Linux only
for now), so a leak surviving to shutdown can be traced back to its call
site. Replaces engineDispose's hard assertTrue with memoryCheckUnfreed(),
which reports unfreed allocations (with call sites when tracking is on,
just a count otherwise) instead of aborting.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-13 09:19:24 -05:00
YourWishesandClaude Sonnet 5 6af0e9cf28 Add CUTSCENE_INSERT item to splice another cutscene's items in place
Unlike CUTSCENE (a one-way jump that replaces the running cutscene),
INSERT splices a referenced cutscene's items into the running one and
resumes right after itself once they run out. Backed by a small return
stack on CUTSCENE_SYSTEM so inserts can nest.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-13 08:56:26 -05:00
YourWishesandClaude Sonnet 5 083deb8357 Move cutscene item type enum and authoring macros to their own headers
cutsceneitemtype_t moves from cutsceneitem.h into cutsceneitembase.h,
alongside a new CUTSCENE_ITEM(TYPE, UNION_NAME, ...) shorthand that
fills in a cutsceneitem_t literal's .type and union member together.

Every CUTSCENE_* authoring macro (CUTSCENE_WAIT, CUTSCENE_TEXT,
CUTSCENE_AUDIO_PLAY, etc.) moves out of the single, ever-growing
cutscene.h into the header of the item type it actually authors,
built on top of CUTSCENE_ITEM. cutscene.h now only holds cutscene_t
itself plus the CUTSCENE/CUTSCENE_REFERENCE/CUTSCENE_CUTSCENE macros,
which aren't tied to any one item type. Every relocated macro also
gained a proper JSDoc-style comment block with @param tags.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-12 22:30:23 -05:00
YourWishesandClaude Sonnet 5 ffb375faeb Add battle abilities and wire them into START_BATTLE cutscene parsing
Adds a battleability_t enum (starting with FIRE) and a fixed
abilities[8] array on battlefighter_t. Extends the START_BATTLE
cutscene item to parse an optional per-enemy "abilities" JSON array
(via a new cutsceneJsonLookupBattleAbility name lookup) and copies
abilities onto every spawned fighter, from the party roster for
allies and from the parsed config for enemies.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-12 22:30:12 -05:00
YourWishesandClaude Sonnet 5 b8cbd8ff6a Make party and save-slot init/new-game operate on SAVE.slot directly
party.c's functions no longer take a party_t* - they always operate on
SAVE.slot.party, since gameplay only ever has one active party. That
forced the same change on saveSlotInit/saveSlotNewGame (they called
partyInit/partyAddMember internally), so those are now parameterless
too, always resetting/seeding SAVE.slot. Callers building a save at a
specific index (new-game creation, slot deletion) now set
SAVE.slotCurrent first instead of building an independent local struct -
safe since the select-save screen is only ever reached before any
gameplay starts.

saveSlotWriteJSON/saveSlotReadJSON deliberately keep their explicit
saveslot_t* parameter - they're genuine serialization primitives used
by the save-device layer and tests against arbitrary structs, unrelated
to the single-current-party assumption. saveSlotReadJSON's internal
reset is now a plain memset instead of delegating to the now-global-only
saveSlotInit().

Updates test_save/test_savedevice/test_savedevicelinux/test_saveslot for
the new signatures. Note: test_savedevice/test_savedevicelinux/test_save
have pre-existing failures in this sandbox unrelated to this change -
savetestfixture.c swaps $HOME, but saveDeviceLinuxGetDirectory actually
derives the save path from ASSET.baseDirectory (the executable's own
location), so the fixture's sandboxing never actually applies. test_
saveslot.c (which doesn't depend on that fixture) passes 15/15.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-12 21:30:08 -05:00
YourWishesandClaude Sonnet 5 c2bd761383 Remove testbattle - superseded by the JSON-driven battle cutscene
testBattleStart() was never actually called anywhere (its only trace
was a stray unused include in uimainmenu.c) - battletest.jsonc's
START_BATTLE/REGULAR_BATTLE flow replaces it.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-12 21:14:50 -05:00
YourWishesandClaude Sonnet 5 878e3438c8 Complete REGULAR_BATTLE immediately, fix stale saveslot tests
REGULAR_BATTLE previously never completed on its own, hanging any
cutscene that reached it - START_BATTLE already waits out the whole
encounter and returns to the overworld before this item ever runs, so
it's now a completes-immediately hook point for future regular-battle-
specific follow-up.

Also updates test_saveslot.c for the mapName/partyMembers schema
additions: the round-trip test now writes a valid map name for its
in-use slot, "missingFieldsUseDefaults" is replaced with a test for the
blank-slot short-circuit it was actually hitting, and a new regression
test locks in that an in-use slot with no map name is treated as corrupt.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-12 19:59:16 -05:00
YourWishesandClaude Sonnet 5 3da65370e0 Add requireStrMin save-JSON shorthand
Replaces the hand-rolled mapName missing/null/empty check in
saveSlotVer1ReadJSON with a reusable macro.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-12 19:55:17 -05:00
YourWishesandClaude Sonnet 5 83e646cba9 Load the save's map on select, seed a real new-game slot, fix readString off-by-one
New games now go through saveSlotNewGame (starting party + default map
name) instead of the empty saveSlotInit, and selecting a save loads its
stored map instead of the hardcoded boot-time overworld. Save loading now
requires an in-use slot to have a non-empty map name, treating a blank one
as corruption instead of silently faking a default.

Also fixes a real off-by-one in the readString save-JSON macro: it passed
its maxLength (documented as excluding the null terminator) straight
through as stringCopy's destSize, which needs +1 for the terminator - so a
string exactly at the length limit (e.g. "overworld", 9 chars) always
asserted "src is too long".

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-10 23:14:44 -05:00
YourWishesandClaude Sonnet 5 0566166af4 Persist party across saves and flag/warn on corrupt save slots
Party moves off a global singleton onto each save slot, letting
START_BATTLE pick specific active-order members to bring into a fight,
and round-trips through save/load via saveslotver1. A corrupt slot load
now flags the cache and shows a red "Corrupted Save" warning in the
select-save UI instead of silently resetting.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-10 14:19:03 -05:00
YourWishesandClaude Sonnet 5 ed0b8c3911 Exit delete mode on save select after delete or back button
Previously deleting a save left the panel in delete mode, and pressing
back while in delete mode closed the whole save-select screen instead
of just returning to the normal load view.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-09 21:38:28 -05:00
YourWishesandClaude Sonnet 5 03d753a83f Move cutscene JSON parsing entirely into the async loader phase
File read, yyjson parse, and all item dispatch now happen in
assetCutsceneLoaderAsync - none of it touches the main thread now that
CUTSCENE items defer their nested asset resolution to start time. The
sync phase is now just a state flip, kept only so assetUpdate()'s
onLoaded dispatch (which only fires for entries finishing via the sync
path) still runs.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-09 21:34:47 -05:00
YourWishes cf51ed1988 Fixed cutscene item cutscene trying to recursively load 2026-09-09 21:21:50 -05:00
YourWishesandClaude Sonnet 5 70f46e89d3 Merge branch 'raw-json-maps' into main
Load map chunks from raw JSON at runtime instead of a baked DCF/DMF
step, add a reusable perf timing module, fix a real PSP logDebug/logError
Memory Stick I/O cost bug, and switch chunk tile storage to a dense
minified grid.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-09 15:34:08 -05:00
YourWishesandClaude Sonnet 5 efd760fb2e Store chunk tile data as a dense minified grid instead of per-tile objects
Cuts the six overworld chunk JSON files from 146KB to 10KB by replacing
{"pos":[x,y,z],"type":t,"tile":u} entries with a fixed 256-slot array
(index x + y*16, value null or [type, z]) and dropping pretty-printing.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-09 15:28:22 -05:00
YourWishesandClaude Sonnet 5 f303d24f4a Add a perf timing/stack module and start using it to diagnose PSP hitching
New src/dusk/perf module: a simple push/pop tick-stack (perfPush/perfPop/
perfReport) for measuring how long a labeled span of code takes, wired
into engine.c's init/dispose. perfGetTick() is a placeholder - real time
on DUSK_LINUX (clock_gettime) and DUSK_PSP (sceKernelGetSystemTimeWide),
0 elsewhere until a proper cross-platform answer is settled on.

Used it this session to chase a reported PSP hitch when crossing a chunk
boundary. Confirmed mapPositionSet itself (unload/load sweeps, chunk
order rebuild, entity chunkIndex recompute) is consistently under ~3ms -
not the cause. Along the way, found and fixed a real, unrelated bug:
logDebug()/logError() on PSP did a fopen+write+fclose to the Memory
Stick on every single call, which was massively inflating any perf
measurement that logged from inside another measurement (the actual
source of the ~29-128ms numbers first seen). That's now gated behind a
new DUSK_PSP_LOG_FILE CMake option (default off).

Also, as a real test based on this session's findings:
- MAP_CHUNK_LOAD_CONCURRENCY dropped from 2 to 1 (8 and 4 both crashed/
  were untested further on real PSP hardware; 1 is confirmed safe).
- MAP_CHUNK_LOAD_DELAY_TEST: an intentionally exaggerated (now 50ms)
  artificial delay between starting successive chunk loads, gated in
  mapChunkLoadNext/mapUpdate, to observe the effect on framerate.

All perf instrumentation call sites added during this investigation
(assetChunkLoaderSync, assetMeshLoaderSync, mapPositionSet, the map area
callback invocation, engine.c's frame boundary) were removed again once
they'd served their purpose - only the reusable perf module itself and
the two real fixes above remain.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-09 15:16:48 -05:00
YourWishesandClaude Sonnet 5 9688e88a7c Load raw chunk JSON at runtime instead of baked DCF/DMF
Experimental: skip the offline tools/asset/chunk bake step entirely.
The chunk asset loader now parses the same JSON schema tools/asset/chunk
already consumed (tiles/meshes/entities/areas) directly at load time,
generating the terrain mesh in C instead of reading pre-baked .dcf/.dmf
bytes. The raw assetsraw/maps/*/chunks/*.json files are now the shipped
source directly under assets/, replacing the old baked chunk/mesh/model
output.

Also drops MAP_CHUNK_LOAD_CONCURRENCY to 1 for now, per PSP hardware
testing (8 crashed, 4 untested further - 1 is the known-safe value).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-09 14:05:19 -05:00
YourWishesandClaude Sonnet 5 c8a6325615 Collapse dusk.dsk from two zip archives into one, all uncompressed
Bulk assets (mp3, png) are already compressed at the file level, so a
DEFLATE pass on top bought negligible space for real CPU cost. Packs
everything into a single ZIP_STORED archive instead, so every entry
(not just locale/*) gets the reliable repeated seeking/re-opening that
libzip only supports for uncompressed entries. Header shrinks from 32
to 16 bytes; version bumped to 2 so a stale pre-change dusk.dsk is
rejected and rebuilt rather than misread.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-09 10:17:34 -05:00
YourWishes 798db14bb5 Recover from a corrupt or version-mismatched save file instead of crashing
saveLoadSettings()/saveLoadSlot() previously let a read failure (e.g. a
version mismatch from the new schema check) propagate all the way up
through saveUpdate()/engineUpdate() to main(), which just prints the
error and exits - taking down the whole game over one bad file. Both
now catch the failure, log it, and fall back to init defaults instead,
matching the existing "no file yet" convention.
2026-09-08 12:49:20 -05:00
YourWishes 178d78ef72 Persist the current map name in the save slot
mapSetMap() now keeps SAVE.slot.cachedData.mapName in sync with the
active map, so whichever map is loaded gets written out next time the
slot is actually saved.
2026-09-08 12:49:13 -05:00
YourWishes 8139a0ea54 Add versioned save slot/settings schemas with strict version checks
Splits saveslot_t/savesettings_t into a stable, version-independent
in-memory struct (save/slot/, save/settings/) and a separate frozen
per-schema-version struct (saveslotver1_t/savesettingsver1_t) used only
for JSON serialization, converted between manually. Every write now
stamps a "version" field (SAVE_SLOT_CURRENT/SAVE_SETTINGS_CURRENT), and
every read requires it to match exactly, rejecting anything else -
laying the groundwork for a real migration path instead of silently
misreading old/incompatible save data.
2026-09-08 12:49:06 -05:00
YourWishes 327c2bec70 Show map name in the debug player position overlay
Useful now that multiple maps can be loaded, to tell which map the
printed coordinates belong to.
2026-09-07 22:34:02 -05:00
YourWishes fc0195d9f7 Bump ASSET_FILE_NAME_MAX to 128
Per-map chunk model paths (maps/<map>/models/chunks/chunk_X_Y_Z_0.json)
were hitting the old 48-char limit, crashing assetEntryInit's length
assert during initial chunk/cutscene loading.
2026-09-07 22:33:57 -05:00
YourWishes db42bca44b Reintroduce multiple maps: per-map chunk directories + map_t name/mapSetMap
Move chunk assets from a single flat assetsraw/chunks (and assets/chunks)
into per-map assetsraw/maps/<map>/chunks (assets/maps/<map>/chunks), so
terrain mesh/model output no longer collides across maps sharing chunk
coordinates. map_t gains a name[MAP_NAME_MAX] field plus mapSetMap()/
mapIsLoaded() (checks name[0] == '\0') in place of the old bool loaded
flag; mapInit() now just resets state and mapSetMap() does the chunk
grid load. Updated the chunk asset tool and the map editor's dev
server/client to match.
2026-09-07 21:28:07 -05:00
YourWishesandClaude Sonnet 5 b3623c84a5 Fix entity chunk-tracking leaks, add saveCanSave, use chunkindex_t
Entities weren't being detached from their chunk's entities[] slot in
two despawn paths (CUTSCENE_ENTITY_REMOVE, item pickup collection) - the
slot leaked forever until the whole chunk unloaded. Both now call
entitySetChunk(entity, CHUNK_INDEX_INVALID) before nulling the type.

entity_t.chunkIndex and entitySetChunk/mapGetChunk now use chunkindex_t
instead of uint8_t, matching mapGetChunkIndexAt's own -1-is-invalid
convention - added CHUNK_INDEX_INVALID next to the typedef in
worldpos.h rather than reusing the old 0xFF/uint8_t sentinel, which
would silently mean +255 on a signed 16-bit field instead of -1.

mapPositionSet now also recomputes every live entity's cached
chunkIndex after mapRebuildChunkOrder() reshuffles which chunk_t sits
at each chunkOrder slot - done as a direct field recompute rather than
through entitySetChunk, which would've looked up each entity's now-stale
old index, failed to find/clear its real registration, and inserted a
duplicate entry into the correct chunk on every map position change.

Also adds saveCanSave() (false while a cutscene is running) and shows
the player's live chunkIndex in the ui/debug/uiplayerpos overlay.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-07 10:12:37 -05:00
YourWishesandClaude Sonnet 5 45c1f9004b Load the chosen save slot on selection, fix transition finished re-firing
Picking a save slot now sets SAVE.slotCurrent and calls saveLoadSlot()
before entering the overworld, same slotCurrent/saveSlotInit/saveLoadSlot
sequence saveLoadAllSlots uses per-slot; a load failure shows the fatal
error overlay instead of proceeding with stale/garbage slot data.

Also fixes uiTransitionUpdate firing its "finished" callback every frame
after a transition completes instead of once - latent since the generic
transition system had no real caller yet.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-06 12:13:51 -05:00
YourWishesandClaude Sonnet 5 845a395ff1 Fix New Game falling through into the Quit confirm dialog
The SAVE_DEVICE_FOUND/SAVE_LOAD_ALL_SLOTS block had been moved to the
end of the file (after QUIT/QUIT_GAME) without moving the LOADED
marker with it. MARKER items always auto-advance to whatever follows
them in the array, so reaching LOADED after a successful save-load fell
straight through into the QUIT block's confirm dialog instead of
completing the cutscene. LOADED now sits after SAVE_LOAD_ALL_SLOTS as
the true last item, so it falls off the end and fires
uiMainMenuOpenSelectSave again as intended.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-06 11:52:48 -05:00
YourWishesandClaude Sonnet 5 f63653c307 Wire main menu Options/Quit through the cutscene, fix locale crashes
Options and Quit now route through main_menu.jsonc markers the same
way New Game does, with a new QUIT_GAME cutscene item type ending the
process directly once quit is confirmed via MODAL_OPTIONS_MARKERS
(whose "message" field is now optional, defaulting to "" like "title").

Fixes two crashes surfaced along the way:
- uiModalLocalize asserted on an empty locale key instead of treating
  it as "nothing to show" - hit as soon as a modal omitted a title or
  message.
- assetLocaleGetString's new empty-message-ID guard broke the PO
  format's own header-read convention (messageId "") used internally
  by assetLocaleLoaderAsync, crashing on every boot. Extracted the
  shared lookup into assetLocaleGetStringLookup and added a dedicated
  assetLocaleGetHeader for that internal read, leaving
  assetLocaleGetString's validation untouched for every real caller.

Also replaced every raw assetLocaleGetString/WithVA call across the UI
with the locale/localemanager.h helpers (adding localeManagerGetString
for the plain, non-formatting case), so app code no longer bypasses
them.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-06 11:47:34 -05:00
YourWishesandClaude Sonnet 5 5b523c68a7 Add cutsceneSystemLoad, a debug cutscene overlay, and NULL-safe fatal error
cutsceneSystemLoad(file) centralizes the lock/assetRequireLoaded/start
sequence previously duplicated in sceneInitialInit, and falls back to
the fatal error overlay instead of asserting when the asset fails to
load. uiFatalErrorOpen now accepts a NULL message, showing a generic
"contact support" message for callers with no specific error text.
Also adds a ui/debug/uicutscene overlay showing the running cutscene's
item count and current item type, and wires main_menu's post-save-picker
flow through cutsceneSystemLoad.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-06 11:18:48 -05:00
YourWishesandClaude Sonnet 5 03efbe0681 Move cutscene item JSON parsing into per-item Load callbacks
Each cutscene item type now owns its own JSON parsing (a Load callback
alongside its existing runtime callbacks), instead of one monolithic
switch in assetcutsceneloader.c. Shared JSON field/enum decoding helpers
move into a new rpg/cutscene/item/json/ module so per-type parsers stay
small and colocated with the behavior they configure.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-06 09:32:10 -05:00
YourWishesandClaude Sonnet 5 cebd3d81e7 Wrap long lines to fit within 80 columns across the codebase
Pure whitespace/line-break reformatting (braces, newlines, and line
continuations matching this codebase's existing wrap conventions) - no
logic, string content, or identifiers changed anywhere. Confirmed via
diff against the pre-change tree and by rebuilding + re-running the
affected test suites, which produce identical pass/fail results.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-05 13:13:10 -05:00
YourWishesandClaude Sonnet 5 de53bebeb8 Remove the main menu scene now that it's just a persistent cutscene
The main menu no longer needs its own scene lifecycle - it's started
directly by the initial cutscene's CUTSCENE item chaining into
main_menu.jsonc. Fold scenemainmenu.c's logic into uimainmenu.c (its
only real caller) and drop SCENE_TYPE_MAIN_MENU entirely. Also arm the
cutscene's onComplete callback unconditionally in uiMainMenuStartGame
so the save-picker still opens on a fresh boot, not just on restart.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-05 12:18:02 -05:00
YourWishesandClaude Sonnet 5 6ccaedd48f Load cutscenes directly from JSONC at runtime, fix asset bundling staleness
Cutscenes now parse their authored .jsonc straight into the runtime
cutsceneitem_t/pool representation via yyjson, instead of going through a
separate Python-compiled DCTS binary format - removes the whole
build/compile step and the byte-format contract between the Python
encoder and the C decoder, at the cost of a (still tiny, one-time)
parse per cutscene load.

Also fixes dusk.dsk going stale after a build: the old custom_command
depended on a CMake-configure-time file glob, which only re-detects
added/removed assets on the next configure and could miss edits
entirely. tools.asset.pack now always runs and decides for itself
(via a small manifest) whether anything actually needs repacking, so
asset changes are never missed regardless of add/edit/remove.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-05 12:05:23 -05:00