Add bitmap font, UI primitives and a HUD with message log
Ports the CP437 font renderer from wds to the framebuffer: proportional advance from measured ink bounds, luminance-keyed mask, word wrap. The right 80 px strip becomes a HUD with name, HP bar, population and a log fed from sim events; Attacked/Died now carry the type ids the log needs, since the victim is gone from the world by the time events are read. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+13
-22
@@ -2,29 +2,20 @@
|
||||
|
||||
## Current state
|
||||
|
||||
No text rendering exists yet. No font loading or glyph blitting code exists.
|
||||
`game/src/font.rs` — a CP437 bitmap font ported from the `wds` project.
|
||||
|
||||
## Goal
|
||||
|
||||
Draw ASCII text into the RGB565 framebuffer using a bitmap font.
|
||||
|
||||
## Design notes
|
||||
|
||||
- A bitmap font is the right fit: each glyph is a small masked tile, blitted
|
||||
directly into the framebuffer using the existing blit primitive in `pixelhelper.rs`.
|
||||
- Likely approach: a fixed 8×8 or similar glyph grid packed into a PNG,
|
||||
one glyph per ASCII codepoint starting at 0x20 (space).
|
||||
- The font image only needs its alpha mask: glyph pixels are opaque, background transparent;
|
||||
the caller supplies the color and `draw_text` paints the mask with it.
|
||||
- API sketch:
|
||||
```rust
|
||||
fn draw_text(frame: &mut [u16], font: &Font, x: i32, y: i32, color: u16, text: &str);
|
||||
```
|
||||
- `Font` holds the glyph sheet as an `Image` plus glyph width/height.
|
||||
- Atlas: 32 columns × 8 rows of fixed cells, codepoint = index (`A` at 65 → column 1,
|
||||
row 2). Loaded with `Image::from_png`; the mask is derived from luminance (white on
|
||||
black), so the caller paints glyphs in any RGB565 color.
|
||||
- Proportional advance: on load the ink span of every glyph (leftmost/rightmost lit
|
||||
column) is measured. `draw` crops to that span and advances by ink width + 1 px
|
||||
tracking; blank glyphs (space) advance half a cell.
|
||||
- `cp437(char)` maps ASCII 1:1 and the accented letters the atlas has (ä ö ü ß …);
|
||||
everything else becomes `?`.
|
||||
- `text_width` and `wrap` (word wrap with hard `\n`) for layout.
|
||||
- Font in use: `assets/fonts/cga-8x8.png` (Oldschool PC Font Pack, CGA 8×8).
|
||||
|
||||
## Open questions
|
||||
|
||||
- How do we handle proportional spacing of different characters?
|
||||
- How do we store font data in memory?
|
||||
- How do we handle font color rendering?
|
||||
- How do we handle special characters like Ä, Ö, Ü, ß, etc.?
|
||||
- A second, smaller or larger font (the EGA 8×14 from `wds`) once the UI needs hierarchy.
|
||||
- Text color per span / inline markup for the log — not before it is needed.
|
||||
|
||||
+12
-27
@@ -2,35 +2,20 @@
|
||||
|
||||
## Current state
|
||||
|
||||
No UI primitives exist. The framebuffer is currently filled by the game world only
|
||||
(`render_viewport` in `game.rs`: tile pass + entity sprite pass). `pixelhelper.rs` has
|
||||
low-level pixel/blit helpers but nothing higher-level.
|
||||
`game/src/ui.rs` — procedural primitives on the RGB565 framebuffer, all clipped:
|
||||
`fill_rect`, `draw_rect` (1 px outline), `draw_hbar` (value/max bar).
|
||||
|
||||
## Goal
|
||||
The HUD (`Game::render_hud` in `game.rs`) lives in the 80 px strip right of the 240×240
|
||||
viewport: player name, HP bar, population counters, a divider, then the message log
|
||||
filling the rest bottom-up (newest line always visible, lines wrapped to the panel).
|
||||
The log is a `VecDeque<String>` fed from `Sim` events in `Game::handle_events`.
|
||||
|
||||
Draw common roguelike UI elements (panels, borders, HUD bars, etc.) procedurally into the
|
||||
`u8` framebuffer, composited on top of the game world.
|
||||
|
||||
## Design notes
|
||||
|
||||
The virtual resolution is 320×240, so UI layout should be designed in those pixel units.
|
||||
|
||||
Likely primitives needed (built on top of `pixelhelper.rs`):
|
||||
|
||||
- `fill_rect(frame, x, y, w, h, color)` — solid filled rectangle
|
||||
- `draw_rect(frame, x, y, w, h, color)` — 1-pixel border rectangle
|
||||
- `draw_border_box(frame, x, y, w, h, tileset)` — box drawn with corner/edge tiles from
|
||||
a tileset (classic roguelike panel look)
|
||||
- `draw_hbar(frame, x, y, w, value, max, fg, bg)` — horizontal progress/HP bar
|
||||
|
||||
Panels are typically fixed regions of the screen (e.g. a status bar at the bottom 40px,
|
||||
a message log on the right). Hard-coding these regions first is fine; extract to a layout
|
||||
system only if needed.
|
||||
|
||||
The UI layer draws after the world layer so it always appears on top. Draw order within
|
||||
the UI should be back-to-front (backgrounds before text).
|
||||
Draw order: world, then HUD. The HUD overwrites the right-edge bleed of the tile pass,
|
||||
which is why there is still no clip-rect on the blit primitives.
|
||||
|
||||
## Open questions
|
||||
|
||||
- Tileset-based borders vs. line-drawing: which look are we going for?
|
||||
- Does the message log need scrolling? Probably not at first.
|
||||
- Tileset-based borders vs. line-drawing: line-drawing for now (`draw_rect`); a 9-slice
|
||||
ornament frame like `wds` has is an option once the look matters.
|
||||
- Does the message log need scrolling? Not yet — `LOG_LINES` keeps 40, the panel shows
|
||||
what fits.
|
||||
|
||||
+13
-6
@@ -158,13 +158,20 @@ Sprite rendering, camera follow and entity interpolation have all landed.
|
||||
|
||||
---
|
||||
|
||||
### Later — UI / HUD
|
||||
### UI / HUD *(first pass done)*
|
||||
|
||||
- Bitmap font renderer: 8×8 glyph sheet PNG, `draw_text(frame, font, x, y, color, text)` — see `notes/04`
|
||||
- UI primitives in `game/src/ui.rs`: `fill_rect`, `draw_rect`, `draw_hbar` — see `notes/05`
|
||||
- HUD layout (320×240): HP bar + player name bottom 16px, message log right panel
|
||||
- A real clip-rect on the blit primitives belongs here — once the viewport and HUD panels are
|
||||
two distinct regions, the abstraction earns its keep. Not before.
|
||||
- [x] Bitmap font: `game/src/font.rs`, CP437 atlas (32×8 cells, codepoint = index),
|
||||
proportional advance from measured ink bounds, luminance-keyed mask, `wrap`.
|
||||
Ported from `wds`; the atlas is `assets/fonts/cga-8x8.png` (Oldschool PC Font
|
||||
Pack CGA 8×8). See `notes/04-text_renderer.md`
|
||||
- [x] UI primitives in `game/src/ui.rs`: `fill_rect`, `draw_rect`, `draw_hbar`
|
||||
- [x] HUD in the 80 px strip right of the viewport (`Game::render_hud`): name, HP bar,
|
||||
population, and a message log fed from the sim events (only fights involving the
|
||||
player; newest at the bottom, wrapped to the panel)
|
||||
- A real clip-rect on the blit primitives — the HUD currently relies on being drawn
|
||||
*after* the viewport, which overwrites the right-edge bleed. Fine until a panel
|
||||
overlaps the world.
|
||||
- Player name is a constant; a character/stat screen comes with inventory.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user