refactor: implement dynamic track layout for overflowing trains
- Replace fixed `TRACK_PITCH` tiling with `computeTrackLayout()` to dynamically fit tiles within track bounds
- Add `tileHalfW()` helper to correctly calculate width for double vs. single tiles
- Add truncation marks ("+N«") when the tile count exceeds visible track space
- Update marker placement, flash effects, and next-tile positioning to use the new dynamic layout coordinates
- Adjust score badge positioning to align relative to portrait radius
This commit is contained in:
parent
ed7da9fcf0
commit
4644b8e3af
|
|
@ -277,30 +277,62 @@ export default class MexicanTrainGame extends Phaser.Scene {
|
|||
}
|
||||
}
|
||||
|
||||
// Returns the horizontal half-width a tile occupies when painted.
|
||||
tileHalfW(tile) {
|
||||
return tile.left === tile.right ? TRACK_HALF / 2 : TRACK_HALF;
|
||||
}
|
||||
|
||||
// Returns { start, positions[] } for the visible slice of a train.
|
||||
// Positions are the x-centres of each visible tile (index start..end).
|
||||
computeTrackLayout(key) {
|
||||
const tiles = this.gs.trains[key].tiles;
|
||||
if (tiles.length === 0) return { start: 0, positions: [] };
|
||||
const GAP = 6;
|
||||
const TRUNC = 16; // px reserved for the "+N«" truncation mark
|
||||
const avail = TRACKS_RIGHT - TRACKS_LEFT;
|
||||
|
||||
// Walk backwards to find the first tile that still fits.
|
||||
let accumulated = 0;
|
||||
let start = tiles.length;
|
||||
for (let t = tiles.length - 1; t >= 0; t--) {
|
||||
const addW = (start < tiles.length ? GAP : 0) + this.tileHalfW(tiles[t]) * 2;
|
||||
if (accumulated + addW > avail - (t > 0 ? TRUNC : 0)) break;
|
||||
accumulated += addW;
|
||||
start = t;
|
||||
}
|
||||
|
||||
// Build centre-x positions for each visible tile.
|
||||
const positions = [];
|
||||
let x = TRACKS_LEFT + (start > 0 ? TRUNC : 0) + this.tileHalfW(tiles[start]);
|
||||
for (let t = start; t < tiles.length; t++) {
|
||||
positions.push(x);
|
||||
if (t < tiles.length - 1) {
|
||||
x += this.tileHalfW(tiles[t]) + GAP + this.tileHalfW(tiles[t + 1]);
|
||||
}
|
||||
}
|
||||
return { start, positions };
|
||||
}
|
||||
|
||||
paintTracks() {
|
||||
const g = this.trackGfx;
|
||||
g.clear();
|
||||
const rows = this.gs.players.length + 1;
|
||||
const maxTiles = Math.floor((TRACKS_RIGHT - TRACKS_LEFT) / TRACK_PITCH);
|
||||
for (let i = 0; i < rows; i++) {
|
||||
const key = this.rowKeyFor(i);
|
||||
const y = this.rowY(i);
|
||||
const tiles = this.gs.trains[key].tiles;
|
||||
const start = Math.max(0, tiles.length - maxTiles);
|
||||
let drawX = TRACKS_LEFT + TRACK_HALF;
|
||||
if (start > 0) {
|
||||
this.maybeTruncMark(i, start);
|
||||
drawX += 8;
|
||||
} else {
|
||||
this.clearTruncMark(i);
|
||||
}
|
||||
for (let t = start; t < tiles.length; t++) {
|
||||
if (tiles.length === 0) { this.clearTruncMark(i); continue; }
|
||||
|
||||
const { start, positions } = this.computeTrackLayout(key);
|
||||
if (start > 0) this.maybeTruncMark(i, start); else this.clearTruncMark(i);
|
||||
|
||||
for (let ti = 0; ti < positions.length; ti++) {
|
||||
const t = start + ti;
|
||||
const tile = tiles[t];
|
||||
const isDouble = tile.left === tile.right;
|
||||
const isOpenDouble = this.gs.openDouble?.train === key && t === tiles.length - 1;
|
||||
const border = isOpenDouble ? COLORS.danger : (isDouble ? COLORS.gold : COLORS.accent);
|
||||
this.paintDomino(g, drawX, y, TRACK_HALF, tile.left, tile.right, isDouble, border);
|
||||
drawX += TRACK_PITCH;
|
||||
this.paintDomino(g, positions[ti], y, TRACK_HALF, tile.left, tile.right, isDouble, border);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -331,7 +363,10 @@ export default class MexicanTrainGame extends Phaser.Scene {
|
|||
if (!this.gs.trains[key].marker) continue;
|
||||
const y = this.rowY(i);
|
||||
const tiles = this.gs.trains[key].tiles;
|
||||
const x = TRACKS_LEFT + TRACK_HALF + Math.min(tiles.length, Math.floor((TRACKS_RIGHT - TRACKS_LEFT) / TRACK_PITCH)) * TRACK_PITCH + 6;
|
||||
const { positions } = this.computeTrackLayout(key);
|
||||
const lastPos = positions.length ? positions[positions.length - 1] : TRACKS_LEFT;
|
||||
const lastHW = tiles.length ? this.tileHalfW(tiles[tiles.length - 1]) : TRACK_HALF;
|
||||
const x = lastPos + lastHW + 6;
|
||||
// little open-train flag
|
||||
g.fillStyle(COLORS.danger, 1);
|
||||
g.fillCircle(x, y, 9);
|
||||
|
|
@ -419,8 +454,8 @@ export default class MexicanTrainGame extends Phaser.Scene {
|
|||
|
||||
for (let i = 0; i < this.gs.players.length; i++) {
|
||||
const score = scores[i];
|
||||
const x = PORTRAIT_X;
|
||||
const y = this.rowY(i) - PORTRAIT_R - 20;
|
||||
const x = PORTRAIT_X + Math.round(PORTRAIT_R * 0.85);
|
||||
const y = this.rowY(i) - Math.round(PORTRAIT_R * 0.85);
|
||||
const borderColor = score > 75 ? COLORS.danger
|
||||
: score > 50 ? 0xe07030
|
||||
: score > 25 ? COLORS.accent
|
||||
|
|
@ -676,14 +711,14 @@ export default class MexicanTrainGame extends Phaser.Scene {
|
|||
// ─── Effects ──────────────────────────────────────────────────────────
|
||||
flashTrainEnd(key) {
|
||||
const rows = this.gs.players.length + 1;
|
||||
let i = key === MEXICAN ? rows - 1 : Number(key);
|
||||
const i = key === MEXICAN ? rows - 1 : Number(key);
|
||||
const y = this.rowY(i);
|
||||
const tiles = this.gs.trains[key].tiles;
|
||||
const maxTiles = Math.floor((TRACKS_RIGHT - TRACKS_LEFT) / TRACK_PITCH);
|
||||
const shown = Math.min(tiles.length, maxTiles);
|
||||
const x = TRACKS_LEFT + TRACK_HALF + (shown - 1) * TRACK_PITCH;
|
||||
if (!tiles.length) return;
|
||||
const { positions } = this.computeTrackLayout(key);
|
||||
const x = positions[positions.length - 1];
|
||||
const lastTile = tiles[tiles.length - 1];
|
||||
const isLastDouble = lastTile && lastTile.left === lastTile.right;
|
||||
const isLastDouble = lastTile.left === lastTile.right;
|
||||
const fw = isLastDouble ? TRACK_HALF : TRACK_TILE_W;
|
||||
const fh = isLastDouble ? TRACK_TILE_W : TRACK_HALF;
|
||||
const fx = this.add.graphics().setDepth(DEPTH.tileFx);
|
||||
|
|
@ -697,9 +732,12 @@ export default class MexicanTrainGame extends Phaser.Scene {
|
|||
const i = key === MEXICAN ? rows - 1 : Number(key);
|
||||
const y = this.rowY(i);
|
||||
const tiles = this.gs.trains[key].tiles;
|
||||
const maxTiles = Math.floor((TRACKS_RIGHT - TRACKS_LEFT) / TRACK_PITCH);
|
||||
const newIdx = Math.min(tiles.length, maxTiles - 1);
|
||||
return { x: TRACKS_LEFT + TRACK_HALF + newIdx * TRACK_PITCH, y };
|
||||
if (tiles.length === 0) return { x: TRACKS_LEFT + TRACK_HALF, y };
|
||||
const { positions } = this.computeTrackLayout(key);
|
||||
const lastX = positions[positions.length - 1];
|
||||
const lastHW = this.tileHalfW(tiles[tiles.length - 1]);
|
||||
const nextX = Math.min(lastX + lastHW + 6 + TRACK_HALF, TRACKS_RIGHT - TRACK_HALF);
|
||||
return { x: nextX, y };
|
||||
}
|
||||
|
||||
animateTilePlay(fromX, fromY, toX, toY, tileA, tileB, onComplete) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue