feat: module 05 assemble

Cards become JSON data in course/cards (one file per card, validated
against card.schema.json and the catalog); the M5 references load them
via registerCards and interpret effects, replacing content closures.
Adds assemble(base, donor, half), the assemble/buffHand effects with an
injected chooser, Fleshshape as the data-only Expansion Drop, and stages
course/cards in verify-references and start-module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-10-29 03:17:21 +01:00
1 parent 55fe5a71d7
commit e04dc1a11f
73 files changed
+6010 -9

No files matched your search

@@ -0,0 +1,30 @@
//! The Death Creation Step: the one place minions leave play.
//!
//! Damage and destroy effects only mark a minion (0 or less Health, or pending
//! destroy). It stays in play, and keeps triggering, until the outermost Phase
//! ends. Then this step removes every marked minion at once.
use super::event::Event;
use super::world::World;
/// Remove every mortally wounded or pending-destroy minion from play, simultaneously.
/// Returns their Death Events in order of play; empty if nothing died.
pub fn death_creation_step(world: &mut World) -> Vec<Event> {
// Every position is read before anything moves: removal is simultaneous, and each
// Death Event remembers where its minion stood when the step began.
let mut deaths = Vec::new();
for id in world.minions_in_play() {
if world.is_doomed(id) {
let position = world
.board_index(id)
.expect("a minion in play is on the board");
deaths.push(Event::Destroyed { id, position });
}
}
for death in &deaths {
if let Event::Destroyed { id, position } = *death {
world.kill(id, position);
}
}
deaths
}