task 2 report - xopherdeep/do-it-for-the-xp GitHub Wiki
Status: DONE_WITH_CONCERNS (concerns are design notes, nothing blocks the merge).
Branch: worktree-agent-a60baf7b9f8792e7e (worktree .claude/worktrees/agent-a60baf7b9f8792e7e), base 62e9e5fb (origin/master).
Commit: 153a921e feat(temple-creator): fight rooms pick what their beast drops
-
components/RoomDropsConfig.vue(new,<script setup lang="ts">, 260 lines): a "Drops" section styled likeRoomLootConfig.vue.- Props
{ drops: RoomDrops | null }, emitsupdate:dropswithRoomDrops | null. No state of its own. - Dungeon item row: None first, then
DUNGEON_ITEMS(Small Key, Boss Key, Map, Compass). A tile lights throughdungeonItemId, so a storeddungeon: 'key'(how the temples spell it) lights Small Key, as'small-key'does. - Items row:
CONSUMABLE_ITEMSwith the Bomb; any number can be ticked (a chest keeps at least one, a drop may keep none). - Gold field: a native number input (0 to 500, whole GP; fractions and negatives are cut, 500 matches the loot chest's slider); on leaving the field it shows the gold actually held, so a cut value never lingers on screen.
- The emitted shape is a chest's:
chestnames the main thing (dungeon, elseconsumable, elsegold); a field with nothing is left out; nothing at all isnull. Fields the picker does not touch survive (a relic indungeon,keyrows initems).
- Props
-
components/index.ts: exportsRoomDropsConfig. -
XpRoomEditorPage.vue(+20 lines, 458 total): mounts<RoomDropsConfig v-if="hasDrops" v-model:drops="drops" />between the monster/quest config chain and the loot config, so it shows for classic and quest fight rooms.hasDropsreadsFIGHT_ROOM_TYPES(single source of truth, not the hook's duplicate list);dropsis a writable computed overroomData.content.dropsthat deletes the key onnull, so nodrops: nullis ever saved. -
hooks/useTempleRoomEditor.ts(+2 lines):CONSUMABLE_ITEMSgains{ id: BOMB_ITEM, icon: 'fa-bomb', label: 'Bomb' }(id'Bomb', the name the solver,isBombSourceand the bag know). -
components/__tests__/RoomDropsConfig.spec.ts(new, 14 tests).
RED (before the component existed):
chemx test src/app/Admin/XpCompendium/views/XpTemples/components/XpTempleCreator/components/__tests__/RoomDropsConfig.spec.ts
✖ Test Failures (1 failed out of 1)
✖ Test suite failed to run
Cannot find module '../RoomDropsConfig.vue' from 'src/app/Admin/.../components/__tests__/RoomDropsConfig.spec.ts'
GREEN (committed tree, same command):
✔ All tests passed (14 passed in 41897ms)
The four brief tests use the brief's values verbatim: Small Key -> { chest: 'dungeon', dungeon: 'small-key' }; None with no gold or items -> null;
gold 30 on a key drop -> { chest: 'dungeon', dungeon: 'small-key', gold: 30 }; ticking Bomb -> items: ['Bomb'] (as { chest: 'consumable', items: ['Bomb'] }).
Extra tests: untick Bomb (null / keeps the other row), Bomb beside a key, None keeps gold and items, clearing gold, gold cut (-5, 12.7, 9000),
the field resyncs on change, tiles marked (aria-pressed) for what the drop holds, 'key' lights Small Key, empty lights None, tile order.
Page check: I could not run the page in a browser (no dev server, no build, per the constraints). As a stand-in I ran a throwaway spec
(tests/unit/ScratchRoomEditorPageCompile.spec.ts, deleted, not committed) that imports XpRoomEditorPage.vue through the repo's vue transform:
it compiled, registered RoomDropsConfig and the compiled render contained RoomDropsConfig and onUpdate:drops. 1 passed. No typecheck or lint
was run (the lead's wave gate does that).
-
components/RoomDropsConfig.vue(new, 260) -
components/__tests__/RoomDropsConfig.spec.ts(new, 123) -
components/index.ts(+1) -
XpRoomEditorPage.vue(+20, now 458, under 500) -
hooks/useTempleRoomEditor.ts(+2, now 1002; it was already over budget at 1000, so the LINE_BUDGET_FILE count does not change)
- Conditions in
ifs are named (isDungeonItemDrop,hasDungeonItem,holdsNothing,isTicked,isNumber); the one object-literal parameter has a named interface (HeldDrop). The two page linesif (!content) return; if (next) ...; else delete ...are simple tests, no compound booleans. - Mounted only for
FIGHT_ROOM_TYPES; a drop written under another type is never shown or handed out (engine rule), andcontentAfterTypeChangealready dropsdropswhen a room changes type (see concern 1). - No Ionic overlay, no alerts, no new text in the in-world copy catalogue (this is Game Master console text, like
RoomLootConfig's).
-
contentAfterTypeChange(hooks/grid/roomContent.ts) does not carrydropsbetween fight types, so retyping a monster to a mini-boss clears its drop (the GM re-picks). That is the safe side of its "a Boss Key cannot ride along into a miniboss" rule; I left it alone (not in the brief). Adding"drops"to theCOMBAT_ROOM_TYPESkeys inCARRIED_WITHIN_FAMILYwould carry it, if the owner wants that. - Adding the Bomb to the shared
CONSUMABLE_ITEMSalso makes it a tick in the loot chest's Consumable picker. I think that is wanted (the Task 7 "Bomb chest" is a loot room holdingitems: ['Bomb'], and a GM opening it should see the Bomb ticked), but it is a visible change beyond the Drops section. -
chestLoot'sKNOWNtable has nobombentry, so a Bomb row shows as a generic "Bomb / Goes in your bag." gift in the reveal. That is Task 3's reveal path, not this task. - The picker is unverified in a real browser (see Page check); the wiring is covered by the compile spec only, and the lead's typecheck/lint gate has not seen it.
(Concerns 1 and 3 are resolved in Fix round 1 below.)
Review finding (Important): a retype or a quest swap silently cleared the drop. applyQuestPick calls retypeRoom(room, tier), so swapping a quest
room to a quest of another Fibonacci tier wiped drops, while every other fight-owned field survived within the combat family. Plus the lead's ruling: give the Bomb its own loot face.
What changed:
-
hooks/grid/roomContent.ts:"drops"added to the combat keys ofCARRIED_WITHIN_FAMILY; the doc comment ofcontentAfterTypeChangenow says the drop stays with the fight between combat rooms. It still never rides into a room that does not fight (a loot room, an empty room). -
src/lib/services/temples/ChestLoot.ts:KNOWN.bomb = { name: 'Bomb', icon: 'bomb', blurb: 'Blows open a cracked wall. Goes in your bag.', tier: 'supply' }(a supply like the potions, so the chest's glow reads it as one), solootFace('Bomb')shows the Font Awesome bomb instead of the gift. -
tests/unit/TempleCreatorRoomContent.spec.ts: new test "keeps what a beast drops between combat rooms..." (monster -> miniboss, miniboss -> monster, monster -> boss, boss -> monster keep the drop; monster -> loot does not). -
tests/unit/ChestLoot.spec.ts: new test "knows the Bomb: a supply with the bomb icon, not the plain gift".
RED (new tests written first):
chemx test tests/unit/TempleCreatorRoomContent.spec.ts
✖ Test Failures (1 failed out of 60)
✖ contentAfterTypeChange › keeps what a beast drops between combat rooms, so a retype or a quest swap to another tier does not wipe it
Expected: {"chest": "dungeon", "dungeon": "master-key", "gold": 30, "items": ["Bomb"]}
Received: undefined
chemx test tests/unit/ChestLoot.spec.ts
✖ Test Failures (1 failed out of 29)
✖ lootFace › knows the Bomb: a supply with the bomb icon, not the plain gift
GREEN (after the fix):
chemx test tests/unit/TempleCreatorRoomContent.spec.ts -> ✔ All tests passed (60 passed in 18279ms)
chemx test tests/unit/ChestLoot.spec.ts -> ✔ All tests passed (29 passed in 21549ms)
chemx test src/app/Admin/XpCompendium/views/XpTemples/components/XpTempleCreator/components/__tests__/RoomDropsConfig.spec.ts
-> ✔ All tests passed (14 passed in 22987ms)
Only those three specs were run (no typecheck/lint). Committed only the four paths above.