Commit Graph
20 Commits
Author SHA1 Message Date
jurjen.ladeniusandClaude Opus 5 aa8fa3ddf9 Art: emit the palette as .gpl/.ase/.aco as well as .act
The .act files only helped Photoshop. Affinity imports .ase and .aco;
Inkscape, GIMP, Krita and Aseprite want .gpl. tools/make_palettes.py now
generates all four plus swatch PNGs, for both the full 16 and the
11-colour portrait subset, all derived from the same PALETTE table shared
with kissaten.c so nothing can drift.

Colour names ride along in .gpl/.ase/.aco, so the swatches show up labelled
("skin", "lamp glow", "dusk violet") rather than as anonymous chips.

.ase output verified by reading it back and checking all 11 colours
round-trip exactly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 21:34:14 +02:00
jurjen.ladeniusandClaude Opus 5 db7de024de Art: SVG -> 48x48 portrait converter, and it works
Magnific's SVG export is flat-shaded vector (~550 solid-fill paths, 6
gradients), which converts to pixel art cleanly. tools/svg2portrait.py
rasterises at 16x the target via qlmanage, then takes the most common
colour per 16x16 block — a true pixelate with no blending — and maps to
the 11-colour portrait palette.

Measured on Aki: 19 colours after pixelate, 10 after palette, visually
near-identical. So the portrait palette is sufficient for this art.

Contrast with the bitmap route this replaces: a 48x48 PNG resized in
Photoshop had 1330 unique colours in 2304 pixels, because bicubic
resampling turns flat regions into gradients. Quantising that speckled
badly across the blazer and background.

Also adds tools/pixtool.py — dependency-free PNG decode/encode (no Pillow
on this machine) plus the palette table shared with kissaten.c.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 20:46:12 +02:00
jurjenwerk 1c0d2d9a88 Add AKI images 2026-07-28 11:41:10 +02:00
jurjen.ladeniusandClaude Opus 5 60c9e13fa8 Art brief: give each asset type its own palette block
The palette constraint is not uniform across assets, so a single generic
line was not enough:

- Portraits list only the 11 non-sky colours, since a face never shows sky;
  omitting the sky slots is safer than asking a generator to avoid them.
  Adds the flat #492424 backdrop requirement (HMMM blits have no
  transparency, so whatever surrounds the head gets copied literally).
- The background block is split by region: four sky colours inside the
  window opening, eleven interior colours everywhere else, with the frame
  and mullions counted as interior wood. Also reserves the top 16 and
  bottom 64 rows the game draws over.
- Sprites take no palette at all — they are hardware sprites coloured at
  runtime, so the art only defines shape.

Notes that the available clothing colours are few enough that characters
need distinct ones or they blur together in the dialogue box.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 10:23:34 +02:00
jurjen.ladeniusandClaude Opus 5 25a098e6af Add art brief and palette references for the PixelLab pipeline
Resolves decision 8: art is generated in PixelLab from written prompts.
docs/ART.md carries the palette spec, the sky/wood slot rule, per-asset
sizes and return format, and prompts for Aki (needed for M3), the rest of
the cast, and the shop background.

art/palette.png (16x1, one pixel per entry) and art/palette_ref.png
(512x64 swatch strip) are generated from g_Palette so the generator and
the build cannot drift apart.

Flags a constraint the palette had hidden: only one literal skin entry, so
portraits use 6 -> 14 -> 4 as a hue-compatible skin ramp.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:34:05 +02:00
jurjen.ladeniusandClaude Opus 5 97eebbaefc Plan: park the M10 save-slot choices as deferred decisions
Records slot count, naming, presentation, delete guard and the day-length
dial as decision-queue items 16-20 marked "defer": captured so the
reasoning survives, but explicitly not decided now. They only bite at M10
and are all easier to judge against a real slot screen than in the
abstract. Adds a "defer" convention to the queue preamble.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:26:54 +02:00
jurjen.ladeniusandClaude Opus 5 70b42f6da1 Plan: add M10 save slots (multi-file), renumber polish to M11
tool/disk_save already indexes every call by entry, so multiple save files
cost almost nothing on the storage side — KISSAT00.SAV, KISSAT01.SAV, and
DiskSave_Check/Delete/GetFreeEntries already exist per entry. The work is
the boot-time slot screen, which is why it sits late: it is easier to
design once there is real save content to display.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:21:01 +02:00
jurjen.ladeniusandClaude Opus 5 cbde457da5 M2 complete: disk saving works via MSXgl's tool/disk_save module
Replaces the hand-rolled PHYDIO/DSKIO sector writer, which never worked,
with engine/src/tool/disk_save.h — added in MSXgl v1.3.0 for exactly this
("save to disk from a ROM application"). Saves are now a real FAT file,
KISSAT00.SAV, on the disk in drive A.

Four non-obvious requirements, all documented in CLAUDE.md:
- LibModules needs both "tool/disk_save" and "dos"
- ROMDelayBoot = true, or the Disk ROM's INIT never runs
- boot then takes 40-90s of emulated time (use `set throttle off`)
- DiskSave_Check() returns SAVEDATA_UNSIGNED for good files: with APPSIGN
  it wants the first 4 bytes to be g_AppSignature, but DiskSave_Save()
  writes the payload raw and never adds it

Also fixes a save-timing bug: the write now happens in AdvanceDay() after
the day increments, so the file describes the morning the player wakes to.
Saving during the evening made a reload replay that day and double-count
its cups.

Verified: play a full day, cold boot, resume at day 2 with served intact.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:17:49 +02:00
jurjen.ladeniusandClaude Opus 5 45749775fa Scaffold Kissaten Yugure; add M2 day spine, plan and script
Stage 1 (shop scene, customer, siphon brew minigame, serve loop) and the
M2 day spine: day counter, three-phase day, event-driven clock, journal
line, and a SaveState with magic/version/checksum.

Saving to disk is NOT working yet. A cartridge ROM can reach the Disk ROM
only from RAM (both PHYDIO and CALSLT switch page 1 out from under the
caller), and ROMDelayBoot is required so the Disk ROM's INIT runs at all.
With both in place the disk is detected, but sector I/O returns carry-clear
without transferring — proven with a sentinel. Probe now verifies the
buffer actually changed, so a non-working disk degrades to "no save"
rather than corrupting anything. Full findings in the project CLAUDE.md.

Also documented: keyboard reads need interrupt protection (the BIOS ISR
scans the matrix too), and BankedCall must stay off until banked data
exists.

Docs: PLAN.md (ten milestones, per-stage requirements, decision queue),
SCRIPT.md (all four character arcs, 60 beats, checked against the
26-character box), and amendment notes in DESIGN.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 21:58:22 +02:00
jurjen.ladeniusandClaude Fable 5 4c33bdb880 Document special keys and ayFX in CLAUDE.md
Game flow now lists all movement-loop keys (M/joy-A minimap, S cat
sense, C cheat landing one move short of the exit), and the MSXgl
configuration section covers the full audio stack including ayFX.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 23:36:09 +02:00
jurjen.ladeniusandClaude Fable 5 d64cb8b37c Add ayFX sound effects; fix torus strip fills ignoring the orthogonal base
Sound effects (ayFX on the PSG, mixed over the FM music):
- 8-effect bank from sfx/sfx_bank.afb embedded as sfx_bank.h in fixed
  main ROM (interrupt-readable without bank switching). Triggers: random
  cat-paw footstep (0-2) per move, fanfare (4) on reaching the exit, map
  unfold (5) on M, laugh (6) on C, ta-daa (7) when advancing the title/
  hint/win screens. Priorities keep footsteps below UI/finish sounds.
- The H_TIMI hook now also runs ayFX_Update + PSG_Apply each frame;
  PSG switched to indirect (buffered) access with AYFX_BUFFER_DEFAULT.
- Interrupt-safety fixes this surfaced: all joystick reads go through
  ReadJoy() (DI/EI around Joystick_Read, which drives the PSG and raced
  the ISR's PSG_Apply — caused noise, phantom input, minimap-on-move),
  and the PSG mixer is muted at startup (zeroed buffer = all channels
  enabled active-low, which PSG_Apply drove as noise during the logo).

Torus scrolling fix (bug from 7c24daa, exposed by mixed-direction play):
- ScrollDraw's bulk strip fills only honoured the scroll-axis base, so
  with a non-zero orthogonal base the newly revealed edge was drawn
  rotated by 2/4/6 tiles — phantom walls/passages while collision stayed
  correct. Vertical strips are now pre-rotated by g_BaseX in staging;
  horizontal column fills split at the 256-row wrap via FillStripColumns.

CLAUDE.md updated for both (SFX table + ISR rules, both-axis fill rule).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 23:33:07 +02:00
jurjen.ladeniusandClaude Opus 4.8 00b5833bb2 C cheat drops player one move from the exit, not on it
Instead of teleporting straight onto the exit cell (instant win), the C
cheat now places the player on an open neighbour of the exit, leaving one
valid move onto it. The exit is a normal spanning-tree cell, so at least
one of its up/left/right neighbours has an open passage; pick the first.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 21:48:14 +02:00
jurjen.ladeniusandClaude Opus 4.8 9faff1cfba Fix music tempo, note-on-switch, and title scroll shift
Three follow-up fixes to the per-screen music:

- Decode music in the H_TIMI VBlank interrupt (MusicVBlank) instead of
  once per Halt() in the game loops. The heavy per-step VDP work in
  ScrollDraw could overrun a VBlank, dropping the decode rate so the
  music slowed only while moving; the ISR keeps tempo steady regardless.
  The playing track is mapped into the unused bank-3 window (0xA000) and
  left there for the screen, so the ISR reads it with no bank switching
  and never races the main code's bank-2 tile/sprite swaps. PlayTrack()
  switches tracks under DI/EI. The game loops revert to plain Halt()s.

- PlayTrack() mutes the OPLL (key-off all 9 channels) before starting the
  next track, silencing the note that lingered across screen changes.

- TitleScreen()/ShowHints() zero the hardware scroll offset before drawing
  their absolute-coord images. Gameplay rests at g_BaseX/g_BaseY in
  {0,64,128,192}; without the reset the returning title/hints screen
  appeared horizontally shifted (only after finishing on a non-zero base).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 21:15:55 +02:00
jurjen.ladeniusandClaude Opus 4.8 1c0636e8ff Add per-screen FM music (lVGM / MSX-Music)
Each screen now plays its own looping MSX-Music (YM2413/OPLL) track:
title, hints, gameplay, and victory. Tracks are lVGM files embedded in
banked segments 26-29 (one per segment, each < 8KB so it never crosses a
boundary); segments 30-31 remain free.

- project_config.js: add psg/msx-music/vgm/lvgm_player modules; set
  EmulMSXMusic so ./build.sh run gives openMSX the FM-PAC (-ext fmpac).
- msxgl_config.h: enable LVGM_USE_PSG/MSXMUSIC (rest off), PSG_DIRECT.
- mazegame.c: MSXMusic_Initialize() once at boot; PlayTrack()/PlayFrame()
  bracket every player call with a bank-2 save/restore so music never
  disturbs the tile/sprite banking; a PlayFrame() follows every Halt() in
  the wait/animation loops (ScrollDraw, input loops now frame-synced,
  WaitKeySequence) to advance each track at 60 Hz. Logo stays silent.
- CLAUDE.md: document the music pipeline, banking protocol, and segments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 20:43:18 +02:00
jurjen.ladeniusandClaude Opus 4.8 7c24daaf14 Eliminate per-move scroll flash with wrap-around (torus) scrolling
Movement previously ended each step by blanking the display, resetting the
scroll offset to 0, bulk-HMMM-shifting the whole viewport back to a canonical
origin, and pasting the new strip. That blank was visible as a flash on every
move.

Treat the 256x256 VRAM page as a torus instead. New g_BaseX/g_BaseY hold the
resting hardware scroll offset (a multiple of 64); a move animates the scroll
register from base to base +/- 64 and fills the newly exposed edge into the
VRAM cells that scroll off the opposite side, then keeps the new offset as the
resting base. No offset reset, no bulk shift, no strip paste, no blank -- so
no flash. All fill targets are base-relative; at base 0 they reduce exactly to
the previous code, and the fill timing is base-invariant.

Route all gameplay VRAM drawing (tiles, sprite, compositors, cat-sense arrow)
through the base via a new WriteVV helper that splits writes at the 256-column
wrap. The minimap overlay draws at absolute coords, so it zeroes the offset
while shown and restores the base on exit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 16:02:39 +02:00
jurjen.ladeniusandClaude Opus 4.8 d2f9b35356 Inset the logo's lower-right stroke from the bars' right edge
The leg sat flush against the middle bar's right end, looking tacked on.
Move it left so both bars overhang it on the right and it crosses the
middle bar like a real glyph stroke. The leg/mid-bar crossing gets its
own palette entry (pal8), lit during both the mid bar's and the leg's
fade so neither shows a gap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 00:02:33 +02:00
jurjen.ladeniusandClaude Opus 4.8 9b554187af Restore Japanese-glyph logo shape with evened bars
The uniform-thickness pass had dropped the bars' left overhangs, turning
the logo into a pinwheel (swastika-like). Restore the original
firehawk.sc5 layout — a vertical spine crossed by two bars that overhang
it on both sides, plus a hanging lower-right stroke — but even out the
weights: both bars and the leg are a uniform 6px, with the spine slightly
thicker (8px) to keep the original glyph's character.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:56:22 +02:00
jurjen.ladeniusandClaude Opus 4.8 961aa89196 Make FH logo stroke thickness uniform
Give every stroke a 6px thickness (stem and right leg width 6, both
crossbars height 6) instead of the previous mix (stem 10, leg 4,
bars 4-5), and recenter the logo. The F+H now reads evenly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:44:35 +02:00
jurjen.ladeniusandClaude Opus 4.8 cdc7bc545d Fix MSX2+ horizontal scroll and rework FH logo animation
- Enable single-page horizontal scrolling (R#25 SP2=1) so the scroll
  offset wraps within the 256px page. Without it the V9958 default
  (two-page) scroll revealed the wrong VRAM page, making new tiles
  appear on the opposite edge during horizontal moves.
- Replace the rectangle-approximated FH logo with strokes matching the
  original firehawk.sc5 artwork. The F+H is drawn as partitioned fills
  (crossbar arms, stem, right leg) with dedicated palette entries for
  the two stem/crossbar crossings, so each horizontal then each
  vertical shimmers in and out gap-free before the whole logo holds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:40:09 +02:00
jurjen.ladeniusandClaude Opus 4.8 f3340e86c6 Initial commit: MSXgl 1.4.1 with mazegame project
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 22:50:20 +02:00