Skip to content

Add Homecoming scroll, canonical home anchor, and safe return portal flow#25

Open
PJensen wants to merge 6 commits intomasterfrom
features/define-home-anchor-and-implement-teleportation
Open

Add Homecoming scroll, canonical home anchor, and safe return portal flow#25
PJensen wants to merge 6 commits intomasterfrom
features/define-home-anchor-and-implement-teleportation

Conversation

@PJensen
Copy link
Owner

@PJensen PJensen commented Feb 18, 2026

Motivation

  • Provide a canonical "home anchor" (depth 0) and a single-item consumable to return the player to home without re-traversing many levels.
  • Ensure teleportation is safe and deterministic with fallback rules when the target tile is blocked.
  • Surface a clear return path (temporary portal) so the player can return to where they left from, and present appropriate VFX for the event.

Description

  • Add rules-state fields homeAnchor and returnPortal to DungeonState and initialize/refresh them during dungeon bootstrap and when transitioning to depth 0. (files: src/rules/components/DungeonState.js, src/rules/environment/dungeon/index.js, src/rules/environment/dungeon/transition.js)
  • Implement teleport helpers in src/rules/utils/teleport.js including resolveTeleportDestination (Chebyshev-radius fallback, excluded tiles, blocked/tile walkability) and findHomeAnchor (compute canonical anchor from bed + nearest chest).
  • Add new spell homecoming and catalog item scroll_homecoming, and add it to scroll loot tables so it can appear in the world. (files: src/rules/data/spells.js, src/rules/data/itemCatalog.js, src/rules/data/lootTables.js)
  • Implement Homecoming behavior in the spell scripts: teleport player to depth 0, place them at a safe home tile, clear/create a temporary home_return_portal entity storing return metadata, and emit semantic events (portal:opened, teleport:home, teleport:failed). (file: src/rules/scripts/spells.js)
  • Implement return-trip resolution in interactionSystem when the player interacts with the return portal: transition back to the source depth and place the player safely with fallback to nearby tiles if the exact tile is occupied. (file: src/rules/systems/interactionSystem.js)
  • Add display wiring: palette entries for the scroll and portal glyph and a purple swirling particle VFX handler for portal:opened to visualize the portal in-game. (files: src/display/palette/base.js, src/main.js)
  • Add regression tests exercising successful return (including portal fallback), invalid-anchor failure, and blocked-home fallback. (file: tests/homecomingScroll.test.mjs)

Testing

  • Ran static checks with node --check on modified modules which succeeded (no syntax errors). (passed)
  • Attempted to run the test suite with deno test but deno is not available in this environment so the Deno tests could not be executed here. (not run)
  • Attempted to run bun test which errored due to environment differences around jsr:@std/assert import resolution (test runtime mismatch); the new test file is authored for the project's Deno-based test runner. (failed under bun)
  • Manually exercised display wiring by launching the app and capturing a screenshot of the portal VFX to validate visual hookup; particle VFX and palette entries were exercised in the browser snapshot. (manual visual check)

Notes: new regression tests were added in tests/homecomingScroll.test.mjs; they are written for the project's Deno test runner and should pass when executed in the project's normal Deno test environment.


Codex Task

Copilot AI review requested due to automatic review settings February 18, 2026 00:27
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a Homecoming scroll that allows players to instantly return to a canonical "home anchor" at depth 0 (overworld), with a temporary return portal that enables safe return to the departure location. The implementation includes comprehensive fallback logic for blocked tiles, proper event emissions, and visual effects for the portal.

Changes:

  • Added homecoming spell and scroll item with loot table integration
  • Implemented teleportation utilities with Chebyshev-distance fallback for safe placement
  • Added DungeonState fields (homeAnchor, returnPortal) with automatic refresh on depth 0 transitions
  • Created return portal interaction system with safe return placement
  • Added purple swirling particle VFX and palette entries for visual feedback

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/homecomingScroll.test.mjs Comprehensive regression tests covering successful return with fallback, invalid anchor failure, and blocked home tile scenarios
src/rules/utils/teleport.js Teleport utilities including resolveTeleportDestination with fallback logic and findHomeAnchor for canonical home position
src/rules/scripts/spells.js Homecoming spell implementation with safe teleportation, portal creation, and event emissions
src/rules/systems/interactionSystem.js Return portal interaction handler with depth transition and fallback placement
src/rules/environment/dungeon/transition.js Added skipPostTick option and homeAnchor refresh on depth 0 transitions
src/rules/environment/dungeon/index.js Initialize homeAnchor during dungeon bootstrap
src/rules/data/spells.js Homecoming spell definition
src/rules/data/lootTables.js Added scroll_homecoming to scroll loot table
src/rules/data/itemCatalog.js Scroll of Homecoming catalog entry
src/rules/components/DungeonState.js Added homeAnchor and returnPortal fields
src/main.js Portal VFX handler with purple swirling particles
src/display/palette/base.js Palette entries for scroll and portal glyphs

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 184 to 192
const fallback = resolveTeleportDestination(world, { x: fromPos.x, y: fromPos.y }, {
maxDistance: 3,
exclude: [{ x: fromPos.x, y: fromPos.y }],
}) || { x: fromPos.x, y: fromPos.y };

world.set(actor, Position, fallback);
if (ds) ds.returnPortal = null;
try { world.destroy(targetId); } catch {}
world.emit?.("portal:returned", { actor, targetId, to: { depth: fromDepth, pos: fallback } });
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback logic may place the player on a blocked tile. If resolveTeleportDestination returns null (when all tiles within maxDistance are blocked), the code falls back to fromPos, which could be occupied or blocked. This contradicts the test expectation on line 52 of homecomingScroll.test.mjs that expects the player to NOT be placed on a blocked departure tile. Consider either removing the || { x: fromPos.x, y: fromPos.y } fallback or handling the null case differently, such as emitting a failure event.

Suggested change
const fallback = resolveTeleportDestination(world, { x: fromPos.x, y: fromPos.y }, {
maxDistance: 3,
exclude: [{ x: fromPos.x, y: fromPos.y }],
}) || { x: fromPos.x, y: fromPos.y };
world.set(actor, Position, fallback);
if (ds) ds.returnPortal = null;
try { world.destroy(targetId); } catch {}
world.emit?.("portal:returned", { actor, targetId, to: { depth: fromDepth, pos: fallback } });
const destination = resolveTeleportDestination(world, { x: fromPos.x, y: fromPos.y }, {
maxDistance: 3,
exclude: [{ x: fromPos.x, y: fromPos.y }],
});
if (!destination) {
world.emit?.("portal:failed", { actor, targetId, reason: "blocked-destination" });
break;
}
world.set(actor, Position, destination);
if (ds) ds.returnPortal = null;
try { world.destroy(targetId); } catch {}
world.emit?.("portal:returned", { actor, targetId, to: { depth: fromDepth, pos: destination } });

Copilot uses AI. Check for mistakes.
homecoming: {
id: 'homecoming',
name: 'Homecoming',
manaCost: 0,
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The homecoming spell has manaCost set to 0, but the existing spell validation test in tests/dataIntegrity.test.mjs (line 13) expects all spells to have positive manaCost. This will cause the data integrity test to fail. Consider either setting manaCost to a positive value (e.g., 10 to represent the cost of a powerful teleportation spell) or updating the validation test to allow zero-cost spells if scrolls are meant to bypass mana costs entirely.

Suggested change
manaCost: 0,
manaCost: 10,

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants