Solidity
This commit is contained in:
@@ -46,12 +46,13 @@ Root `Cargo.toml`: `members = ["kiln-core", "kiln-tui"]`.
|
||||
**`kiln-core/src/game.rs`** — all core game types:
|
||||
- `Glyph` (`Copy`, `Eq`, `Hash`) — per-cell visual: `tile: u32` (tilesheet index), `fg/bg: Color32`. `Glyph::player()` is a `const fn`; all other glyphs come from the map file. Derives `Hash` so boards can deduplicate glyphs into a palette.
|
||||
- `FontSpec` — optional per-board bitmap font override: `path: String`, `tile_w: u32`, `tile_h: u32`. When `None`, the app default font is used.
|
||||
- `Behavior` — plain data struct of runtime behavioral properties: `passable: bool`, `opaque: bool`. Returned by `Archetype::behavior()`; new properties added here require no match arms elsewhere.
|
||||
- `Behavior` — plain data struct of runtime behavioral properties: `solid: bool` (blocks/participates in movement — inverse of the old `passable`), `opaque: bool`, `pushable: bool` (all archetypes `false` for now; the push mechanic is future work). Returned by `Archetype::behavior()`; new properties added here require no match arms elsewhere.
|
||||
- `Solid<'a>` — the single solid occupant of a cell, returned by `Board::solid_at`: `Cell(Archetype)` (a solid grid archetype like a wall) or `Object(&ObjectDef)` (a solid object). At most one solid may occupy a cell — enforced at load time.
|
||||
- `Archetype` (`Copy`, `PartialEq`) — enum of named element types: `Empty`, `Wall`, `ErrorBlock`. Each variant provides `behavior()`, `name()` (used in map files), and `default_glyph()` (used by the editor when stamping a cell). `ErrorBlock` is a sentinel for unknown archetype names — renders as yellow `?` on red.
|
||||
- `ALL_ARCHETYPES: &[Archetype]` — ordered list of valid editor choices (excludes `ErrorBlock`).
|
||||
- `Board` — the complete game unit (ZZT-style "board"): `width`, `height`, `cells: Vec<(Glyph, Archetype)>` (row-major; each cell owns its visual and behavioral class directly), `player: Player`, `objects: Vec<ObjectDef>`, `portals: Vec<PortalDef>`, `font: Option<FontSpec>`. `cells` is `pub(crate)`. `is_passable(x, y)` checks objects before the grid cell: an impassable object blocks even over a passable floor tile.
|
||||
- `Board` — the complete game unit (ZZT-style "board"): `width`, `height`, `cells: Vec<(Glyph, Archetype)>` (row-major; each cell owns its visual and behavioral class directly), `player: Player`, `objects: Vec<ObjectDef>`, `portals: Vec<PortalDef>`, `font: Option<FontSpec>`. `cells` is `pub(crate)`. `solid_at(x, y) -> Option<Solid>` returns the cell's single solid occupant (object checked before grid archetype); `is_passable(x, y)` is the convenience inverse (`solid_at(...).is_none()`).
|
||||
- `Player` — `x: i32, y: i32`
|
||||
- `ObjectDef` — scripted object placed on the board: `x`, `y`, `glyph: Glyph`, `passable: bool`, `opaque: bool`, `script_name: Option<String>`. `passable` defaults `false` and `opaque` defaults `true` in map files. Its `init()`/`tick(dt)` hooks are run by the scripting runtime (see `script.rs`).
|
||||
- `ObjectDef` — scripted object placed on the board: `x`, `y`, `glyph: Glyph`, `solid: bool`, `opaque: bool`, `pushable: bool`, `script_name: Option<String>`. `solid` and `opaque` default `true`, `pushable` defaults `false` in map files. Its `init()`/`tick(dt)` hooks are run by the scripting runtime (see `script.rs`).
|
||||
- `PortalDef` — parsed from map files, stored on Board; not yet runtime-wired
|
||||
- `GameState` — holds `board: Rc<RefCell<Board>>`, the message `log: Vec<LogLine>`, and a `ScriptHost`. The board sits behind `Rc<RefCell<…>>` as a sibling of the `ScriptHost` so script host functions can hold a shared handle to it without aliasing the borrow that's running the engine. Front-ends/logic reach the board through `board() -> Ref<Board>` / `board_mut() -> RefMut<Board>` (there is no `pub board` field). `try_move` moves the player; `run_init()` runs object `init()` hooks once at startup; `tick(dt)` runs object `tick(dt)` hooks every frame. After each script batch, `apply_commands()` drains the script command queue: `Log`/`Error` → `log`, `SetTile` → the source object's glyph, `Move(dir)` → `move_object` (bounds + `is_passable`). This deferred apply (writes after the batch) is what keeps script execution borrow-safe.
|
||||
|
||||
@@ -177,17 +178,20 @@ tile_h = 16
|
||||
[grid]
|
||||
content = """
|
||||
############################################################
|
||||
# #
|
||||
# G #
|
||||
############################################################
|
||||
"""
|
||||
|
||||
[[objects]] # optional
|
||||
x = 10
|
||||
y = 5
|
||||
# Placement: either `x`/`y`, OR a `palette` char that appears in the grid.
|
||||
# Convention: object palette chars are uppercase letters (here `G`). The char
|
||||
# must appear exactly once in the grid, be unique across objects, and not be a
|
||||
# [palette] key; the cell under it loads as Empty.
|
||||
palette = "G"
|
||||
tile = "#"
|
||||
fg = "#aa3333"
|
||||
bg = "#000000"
|
||||
passable = true
|
||||
solid = false # blocks movement? defaults true. (pushable defaults false)
|
||||
script_name = "greeter" # references a key in [scripts]
|
||||
|
||||
[[portals]] # optional; parsed but not yet runtime-wired
|
||||
@@ -207,11 +211,12 @@ fn tick(dt) { move(North); } # optional, run every frame; dt = elapsed s
|
||||
"""
|
||||
```
|
||||
|
||||
Colors are `"#RRGGBB"` hex strings. `player_start` is a header field — the player is not a board cell. The `tile` field accepts either a single-character string (`tile = " "`) or an integer (`tile = 35`); both are valid and existing char-style map files continue to work. The grid multi-line string's leading newline is trimmed by TOML; trailing newline is handled correctly by `str::lines()`. Unknown archetype names produce an `ErrorBlock` cell and a logged warning. Script source lives in the `[scripts]` table (name → Rhai source); objects reference a script by `script_name`. Scripts may define optional `init()`/`tick(dt)` functions; they **read** the world through `Board.*` (e.g. `Board.player_x`) and **write** via host functions `move(dir)` (`dir` ∈ `North`/`South`/`East`/`West`), `set_tile(n)`, and `log(s)`.
|
||||
Colors are `"#RRGGBB"` hex strings. `player_start` is a header field — the player is not a board cell. The `tile` field accepts either a single-character string (`tile = " "`) or an integer (`tile = 35`); both are valid and existing char-style map files continue to work. The grid multi-line string's leading newline is trimmed by TOML; trailing newline is handled correctly by `str::lines()`. Unknown archetype names produce an `ErrorBlock` cell and a logged warning, as does any grid character that is neither a `[palette]` key nor an object's `palette` char. Objects may be placed by `x`/`y` or by a single grid `palette` char (uppercase by convention); placement and one-solid-per-cell violations are logged and the offending object is dropped (the rest of the board still loads). Script source lives in the `[scripts]` table (name → Rhai source); objects reference a script by `script_name`. Scripts may define optional `init()`/`tick(dt)` functions; they **read** the world through `Board.*` (e.g. `Board.player_x`) and **write** via host functions `move(dir)` (`dir` ∈ `North`/`South`/`East`/`West`), `set_tile(n)`, and `log(s)`.
|
||||
|
||||
### Key design decisions
|
||||
|
||||
- **`Behavior` and `Archetype` are separate types** — `Archetype` is the named class of a thing (`Wall`, `Empty`, `Object`); `Behavior` is its runtime properties (`passable`, `opaque`). Adding a new property means adding a field to `Behavior`, not a match arm at every call site.
|
||||
- **`Behavior` and `Archetype` are separate types** — `Archetype` is the named class of a thing (`Wall`, `Empty`, `Object`); `Behavior` is its runtime properties (`solid`, `opaque`, `pushable`). Adding a new property means adding a field to `Behavior`, not a match arm at every call site.
|
||||
- **One solid per cell** — at most one solid entity (a solid grid archetype *or* a solid object) may occupy a cell. The map loader enforces this: a solid object landing on an already-solid cell is logged and dropped. `Board::solid_at` relies on this invariant.
|
||||
- **`cells: Vec<(Glyph, Archetype)>`** — each cell owns its visual and behavioral class directly; there is no per-board element palette or integer indirection. `Archetype` is `Copy` so this is efficient.
|
||||
- **Archetypes are referenced by name in map files** — so `ALL_ARCHETYPES` can be reordered or extended without breaking saved games.
|
||||
- **`Board` is the complete unit** — grid, player, objects, and portals all live on `Board`, matching how ZZT treats a "board". No separate wrapper struct.
|
||||
|
||||
Reference in New Issue
Block a user