Add the entities.png manifest, paste-in template and sprite loader

The sheet becomes a fixed grid (one row per entity, one column slot per
animation state) described by a sidecar entities.toml that only maps
names to rows; the loader reads present states and frame counts off the
pixels, so assembling the sheet by hand needs no bookkeeping.
tools/mktemplate.py renders the labelled template with low-alpha slot
guides. The CGA atlas is kept for larger UI text.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-20 20:25:26 +02:00
co-authored by Claude Opus 5
parent 335962800f
commit 8bf086b06a
13 changed files with 555 additions and 83 deletions
+19 -4
View File
@@ -10,9 +10,21 @@ branch `truecolor16`),
not in this repo.
Assets: `assets/packs/` holds the source packs untouched (Mini Medieval family by
VEXED, CC-BY); `assets/tilesets/` is what the game actually loads — sheets assembled
from the packs (`entities.png`: player, hen, fox); `assets/fonts/` the font sources and
the repacked atlas; `assets/maps/` the Tiled CSV maps.
VEXED, CC-BY — "documented" sheets: labels and state columns are pixel text in the
PNGs, readable by eye, not by script); `assets/tilesets/` is what the game actually
loads; `assets/fonts/` the font sources and repacked atlases (Micro Chat for the HUD,
CGA 8×8 kept for menus/titles); `assets/maps/` the Tiled CSV maps.
Entity sprites: `tilesets/entities.png` is assembled by hand from the packs into a
fixed grid described by the sidecar `entities.toml` — one row per entity, one column
slot per state (idle 2, sit 2, walk 4, action1 4, action2 2, hit 2, dead 2 frames
max). The manifest only maps `name → row` and the slot layout; which states and how
many frames a row has, the loader (`game/src/sprites.rs`) reads off the pixels
(leading non-empty cells; empty slot → falls back to idle). `tools/mktemplate.py`
renders the paste-in template `entities-template.png` (labels, header, slot guides at
alpha 48 — invisible to the loader) and carries existing sprites over, so adding rows
is: edit the manifest, rerun the tool, paste, save as `entities.png`. Game code refers
to sprites by name (`sprites.id("hen")`); the id doubles as the entity's `type_id`.
History: this fork started from a client/server multiplayer architecture (authoritative UDP
server, thin client with prediction). The scope was cut to single player in September 2026;
@@ -37,7 +49,10 @@ crate's `tick`/`set_action` interface is the seam a server could be wrapped arou
dimensions), `load_world` slices it into chunks, invisible solid border at the map rim.
See `notes/07-map-loader.md`
- [x] Tile rendering: chunk-based world rendering straight from `World` (`render_viewport`)
- [x] Sprite-based entity rendering: entity `type_id` → tile in `entities.png`, blitted with transparency
- [x] Sprite-based entity rendering: entity `type_id` → sprite row in `entities.png`
(via the `entities.toml` manifest, see Assets above), idle frame blitted with
transparency. Animation (walk while the lerp runs, hit/dead from events) is the
next step — the sheet layout and loader already carry the frames.
- [x] Hybrid tick loop: 24 Hz base tick, movement in `entity_tick` every 4th tick (6 Hz);
the game loop drives the sim with a fixed-timestep accumulator. See `notes/06-hybrid-tick-loop.md`
- [x] Intent model: one scheduled action per entity per movement window (`Sim::set_action`),