feat(catan): add debug mode for free roads/ships and improve fog revelation

- Add DEBUG_FREE_ROADS_AND_SHIPS flag to bypass resource costs for testing
- Extend fog hex revelation to include corner-adjacent hexes, not just edge-adjacent
- Restructure button enable logic to support debug mode in UI
- Initialize hexTileFrames map in CatanGame setup
This commit is contained in:
Brian Fertig 2026-06-12 09:46:30 -06:00
parent c9192cda75
commit 1e607e5802
2 changed files with 16 additions and 8 deletions

View File

@ -132,6 +132,7 @@ export default class CatanGame extends Phaser.Scene {
this.hexImgs = [];
this.hexLabels.forEach((t) => t.destroy());
this.hexLabels = [];
this.hexTileFrames = {};
// Inset a convex hex polygon toward its center.
// s=0 collapses to point; s=1 is original. Uses inradius (79.7px) as scale reference.
@ -1551,12 +1552,13 @@ export default class CatanGame extends Phaser.Scene {
const action = me && s.phase === 'action';
const set = (k, on) => this.buttons[k]?.setEnabled(!!on);
const hasSettleSpot = action && L.legalSettlementNodes(s, 0, false).length > 0;
const DEBUG_FREE_ROADS_AND_SHIPS = false;
const hasRoadSpot = action && L.legalRoadEdges(s, 0, false).length > 0;
set('roll', me && s.phase === 'rollPhase');
set('road', (action && hasRoadSpot && L.canAfford(p, COSTS.road)) || (action && s.freeRoads > 0 && hasRoadSpot));
set('road', action && hasRoadSpot && (DEBUG_FREE_ROADS_AND_SHIPS && me || L.canAfford(p, COSTS.road) || s.freeRoads > 0));
const hasShipSpot = action && L.legalShipEdges(s, 0).length > 0;
const sCost = L.shipCost(s);
set('ship', hasShipSpot && ((action && sCost && L.canAfford(p, sCost)) || (action && s.freeShips > 0)));
set('ship', hasShipSpot && action && (DEBUG_FREE_ROADS_AND_SHIPS && me || !sCost || L.canAfford(p, sCost) || s.freeShips > 0));
set('settlement', hasSettleSpot && L.canAfford(p, COSTS.settlement));
set('city', action && p.settlements.length > 0 && L.canAfford(p, COSTS.city));
set('buyDev', action && s.devDeck.length > 0 && L.canAfford(p, COSTS.devCard));

View File

@ -414,13 +414,15 @@ export function moveRobber(state, hexId, targetSeat = null) {
}
// ── building ───────────────────────────────────────────────────────────────────
const DEBUG_FREE_ROADS_AND_SHIPS = false;
export function buildRoad(state, seat, edgeId) {
const s = cloneState(state);
if (s.phase !== 'action' || s.currentPlayer !== seat) return s;
if (!legalRoadEdges(s, seat, false).includes(edgeId)) return s;
const free = s.freeRoads > 0;
if (!free && !canAfford(s.players[seat], COSTS.road)) return s;
if (free) s.freeRoads--; else pay(s, s.players[seat], COSTS.road);
if (!free && !canAfford(s.players[seat], COSTS.road) && !(DEBUG_FREE_ROADS_AND_SHIPS && seat === 0)) return s;
if (free) s.freeRoads--; else if (!(DEBUG_FREE_ROADS_AND_SHIPS && seat === 0)) pay(s, s.players[seat], COSTS.road);
s.players[seat].roads.push(edgeId);
revealFogHexes(s, edgeId);
recomputeLongestRoad(s);
@ -428,10 +430,14 @@ export function buildRoad(state, seat, edgeId) {
return s;
}
// Reveal any fog hexes that share an edge with edgeId.
// Reveal any fog hexes that share an edge or a corner (node) with edgeId.
function revealFogHexes(s, edgeId) {
const geo = geoFor(s);
for (const hexId of geo.edges[edgeId].hexes) {
const [a, b] = geo.edges[edgeId].nodes;
// Check all hexes touching either endpoint node — this catches fog hexes
// that share only a corner with the road/ship edge.
const hexIds = new Set([...geo.nodes[a].hexes, ...geo.nodes[b].hexes]);
for (const hexId of hexIds) {
const hex = s.hexes[hexId];
if (!hex || hex.kind !== 'fog') continue;
if (hex.fogData) {
@ -496,8 +502,8 @@ export function buildShip(state, seat, edgeId) {
if (!legalShipEdges(s, seat).includes(edgeId)) return s;
const cost = shipCost(s);
const free = s.freeShips > 0;
if (!free && !canAfford(s.players[seat], cost)) return s;
if (free) s.freeShips--; else pay(s, s.players[seat], cost);
if (!free && !canAfford(s.players[seat], cost) && !(DEBUG_FREE_ROADS_AND_SHIPS && seat === 0)) return s;
if (free) s.freeShips--; else if (!(DEBUG_FREE_ROADS_AND_SHIPS && seat === 0)) pay(s, s.players[seat], cost);
s.players[seat].ships.push(edgeId);
revealFogHexes(s, edgeId);
recomputeLongestRoad(s);