feat: summoned minions enter the grid in Module 10 (a fourth seam; Clay Golem, Clay Shard)
Summoned records its summoner (the minion whose trigger is resolving) inside the engine; the harness log is unchanged. When a Sequence ends, SpellGrid writes spawned text, places each summoned minion with a noun on its summoner's tile, removes the dead, reparses and syncs rule auras. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
820b378929
commit
4eaf24eed2
23 files changed
+361
-42
No files matched your search
@@ -17,6 +17,8 @@
|
||||
{ "name": "SCRIBE", "role": "noun" },
|
||||
{ "name": "KNIGHT", "role": "noun" },
|
||||
{ "name": "CRIER", "role": "noun" },
|
||||
{ "name": "GOLEM", "role": "noun" },
|
||||
{ "name": "SHARD", "role": "noun" },
|
||||
{ "name": "DEATHRATTLE", "role": "noun" },
|
||||
{ "name": "YOU", "role": "property" },
|
||||
{ "name": "WIN", "role": "property" },
|
||||
|
||||
@@ -5,7 +5,13 @@ import type { TriggerOn } from "./card";
|
||||
import type { EntityId, PlayerId } from "./world";
|
||||
|
||||
export type Event =
|
||||
| { type: "Summoned"; id: EntityId }
|
||||
/**
|
||||
* `id` entered the board. `by` is the minion whose triggered ability put it there (the
|
||||
* trigger resolving when it entered), or undefined for a spell, a play or a summon from
|
||||
* outside a Sequence. Module 10: SpellGrid puts it on `by`'s grid tile. The course's log
|
||||
* doesn't show `by`.
|
||||
*/
|
||||
| { type: "Summoned"; id: EntityId; by: EntityId | undefined }
|
||||
| { type: "Damaged"; target: EntityId; amount: number }
|
||||
| { type: "Triggered"; source: EntityId; on: TriggerOn }
|
||||
| { type: "Drew"; player: PlayerId; id: EntityId }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Part III: SpellGrid, the two engines in one level.
|
||||
//
|
||||
// Part I (the card engine) and Part II (the grid engine, `puzzle/`) don't import each
|
||||
// other. This file is the only place that knows both, and they meet at three seams:
|
||||
// other. This file is the only place that knows both, and they meet at four seams:
|
||||
//
|
||||
// 1. Rules become modifiers. After every reparse, the rules in force that join a
|
||||
// trigger word to a multiplier word (`DEATHRATTLE IS DOUBLE`) are compiled into
|
||||
@@ -15,6 +15,10 @@
|
||||
// ends, the text its effects asked for (`TextSpawned` events) is written, the
|
||||
// minions that died (`Destroyed` events) leave the grid, and the rules are parsed
|
||||
// again, before anything else happens.
|
||||
// 4. Summoned minions enter the grid. In the same place, before the dead leave: a
|
||||
// minion that a minion's ability summoned (`Summoned` events with a `by`), whose card
|
||||
// has a noun, becomes an object of that noun on its summoner's tile. One summoned by
|
||||
// a spell, or with no noun, is in play but not on the grid.
|
||||
//
|
||||
// One id space: a minion's id is its grid object's id. Every other card (a hero, a
|
||||
// card in hand) gets an id from the same counter that no object will have.
|
||||
@@ -223,7 +227,7 @@ export class SpellGrid {
|
||||
this.sequence({ type: "destroy", targets: minions.map((id) => this.entity(id)) });
|
||||
};
|
||||
|
||||
/** Run one Sequence in Part I, then seam 3: apply its grid changes and reparse. */
|
||||
/** Run one Sequence in Part I, then seams 3 and 4: apply its grid changes and reparse. */
|
||||
private sequence(action: Action): void {
|
||||
const from = this.world.log().length;
|
||||
runSequence(this.world, action);
|
||||
@@ -232,11 +236,22 @@ export class SpellGrid {
|
||||
// Text first, while every minion that wrote it is still on its tile.
|
||||
for (const event of events) {
|
||||
if (event.type !== "TextSpawned") continue;
|
||||
// A minion with no id (summoned by this Sequence) is not on the grid: nowhere to write.
|
||||
// A minion with no id (summoned by this Sequence) is not on the grid yet: nowhere to write.
|
||||
const id = this.ids.get(event.source);
|
||||
const source = id === undefined ? undefined : board.find(id);
|
||||
if (source !== undefined) board.place({ type: "text", word: event.word }, source.x, source.y);
|
||||
}
|
||||
// Seam 4: summoned minions, in summon order, on their summoner's tile. The dead are still
|
||||
// on the grid here, so a Deathrattle's summoner is found where it died; a minion summoned
|
||||
// earlier in the list is already placed, so it can be a summoner too.
|
||||
for (const event of events) {
|
||||
if (event.type !== "Summoned" || event.by === undefined) continue;
|
||||
const card = this.world.cardName(event.id);
|
||||
if (this.ids.has(event.id) || this.world.card(card).def.noun === undefined) continue;
|
||||
const by = this.ids.get(event.by);
|
||||
const summoner = by === undefined ? undefined : board.find(by);
|
||||
if (summoner !== undefined) this.link(event.id, board.place(this.nounOf(card), summoner.x, summoner.y));
|
||||
}
|
||||
for (const event of deaths(events)) {
|
||||
const id = this.ids.get(event.id);
|
||||
if (id !== undefined) board.remove(id);
|
||||
|
||||
@@ -126,7 +126,8 @@ export class World {
|
||||
entity.zone = zone;
|
||||
if (zone === "board") {
|
||||
entity.orderOfPlay = ++this.nextOrderOfPlay;
|
||||
this.record({ type: "Summoned", id });
|
||||
// The summoner is the minion whose trigger is resolving (the innermost one), if any.
|
||||
this.record({ type: "Summoned", id, by: this.resolving.at(-1)?.[0] });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ export type GameEvent =
|
||||
function toGameEvent(event: Event): GameEvent | undefined {
|
||||
if (event.type === "Triggered") return { type: "Triggered", source: event.source, trigger: triggerLabel(event.on) };
|
||||
if (event.type === "Destroyed") return { type: "Destroyed", id: event.id };
|
||||
if (event.type === "Summoned") return { type: "Summoned", id: event.id };
|
||||
// A played minion's `Summoned` already shows it entering the board.
|
||||
if (event.type === "Played") return undefined;
|
||||
// SpellGrid writes the text on the grid; the course's log doesn't show the request.
|
||||
|
||||
Reference in new issue
Block a user