diff --git a/packages/engine/src/game.ts b/packages/engine/src/game.ts index ea211e6..12bde4a 100644 --- a/packages/engine/src/game.ts +++ b/packages/engine/src/game.ts @@ -199,6 +199,8 @@ export interface CastParams { cardId?: string; points?: number; clockwise?: boolean; + /** MEGA-MONSTER: which stat doubles. */ + boost?: "life" | "movement"; } export interface GameConfig { @@ -1465,7 +1467,7 @@ const CARD_EFFECTS: Record if (!creature) return "no such monster"; if (creature.kind === "shadow" || creature.kind === "alter-ego") return "that is no monster"; if (!gameLos(state, caster.position, creature.position)) return "no line of sight"; - const boost = cmd.params?.cardId === "movement" ? "movement" : "life"; + const boost = cmd.params?.boost === "movement" ? "movement" : "life"; if (boost === "movement") creature.movesPerTurn *= 2; else creature.maxDamage *= 2; events.push({ type: "monsterBoosted", creatureId: creature.id, boost }); diff --git a/packages/engine/test/creatures.test.ts b/packages/engine/test/creatures.test.ts index e00d36a..01d6f79 100644 --- a/packages/engine/test/creatures.test.ts +++ b/packages/engine/test/creatures.test.ts @@ -205,6 +205,16 @@ describe("monsters", () => { }); expect(state.creatures[0]!.maxDamage).toBe(8); + // "Doubles the existing life-points OR movement rate" — the other choice. + const movesBefore = state.creatures[0]!.movesPerTurn; + const mm2 = giveCard(state, me, "mega-monster", "MM2", 3); + state = must(state, me, { + type: "cast", instanceId: mm2.instanceId, target: { kind: "creature", creatureId: id }, + params: { boost: "movement" }, + }); + expect(state.creatures[0]!.movesPerTurn).toBe(movesBefore * 2); + expect(state.creatures[0]!.maxDamage).toBe(8); + const dc = giveCard(state, me, "dispel-creation", "DC", 2); state = must(state, me, { type: "cast", instanceId: dc.instanceId, target: { kind: "cell", cell: state.creatures[0]!.position }, diff --git a/packages/web/src/App.svelte b/packages/web/src/App.svelte index 3bdf21b..e54e829 100644 --- a/packages/web/src/App.svelte +++ b/packages/web/src/App.svelte @@ -88,6 +88,8 @@ let tradeFrom = $state<{ x: number; y: number } | null>(null); /** rotate-sector direction. */ let rotateCW = $state(true); + /** mega-monster: which stat the chosen monster doubles. */ + let megaBoost = $state<"life" | "movement">("life"); /** Cards marked for discard. */ let discardSelection = $state>(new Set()); /** Voluntary discard mode: hand clicks mark cards instead of playing them. */ @@ -226,6 +228,7 @@ tradeFrom = null; selectedCreature = null; selectedCard = null; + megaBoost = "life"; attachedNumber = null; attachedMods = []; wbDamage = 0; @@ -614,6 +617,7 @@ dispatch({ type: "cast", instanceId: selectedCard.instanceId, target: { kind: "creature", creatureId }, + ...(selectedCard.cardId === "mega-monster" ? { params: { boost: megaBoost } } : {}), ...(attachedNumber ? { numberInstanceIds: [attachedNumber.instanceId] } : {}), }); clearSelection(); @@ -1378,6 +1382,14 @@ {:else if selectedCard && TWO_CELL_CARDS.has(selectedCard.cardId)} {tradeFrom ? "— now the second square" : "— click the first square"} {/if} + {#if selectedCard?.cardId === "mega-monster"} + — double its + + + — then tap the monster + {/if} {#if selectedCard?.cardId === "rotate-sector"} — click the sector @@ -1857,6 +1869,10 @@ cursor: pointer; box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.35); } + .stamp.lit { + background: #43331f; + color: #e9e1cb; + } .stamp:hover:not(:disabled) { background: #f4eede; } .stamp:active:not(:disabled) { transform: translate(1px, 1px); box-shadow: 1px 1px 0 rgba(0,0,0,0.35); } .stamp:disabled { opacity: 0.45; cursor: default; }