From 2b45813ce0f15192052fe2ace5a2169d041d83fa Mon Sep 17 00:00:00 2001 From: evanpelle Date: Sun, 24 May 2026 17:18:11 +0100 Subject: [PATCH] Use spawn tile for name placement during spawn phase Names now follow the player's currently-selected spawn tile each tick instead of going through placeName, which read a cluster bounding box that only refreshed every cluster-calc cycle and was further throttled to every other tick. Adds placeSpawnName and switches the spawn-phase branch in GameRunner to use it. --- src/client/hud/NameBoxCalculator.ts | 31 +++++++++++++++++++++++++++++ src/core/GameRunner.ts | 24 ++++++++++------------ 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/client/hud/NameBoxCalculator.ts b/src/client/hud/NameBoxCalculator.ts index abb6bfb23..60f07c747 100644 --- a/src/client/hud/NameBoxCalculator.ts +++ b/src/client/hud/NameBoxCalculator.ts @@ -13,6 +13,37 @@ export interface Rectangle { height: number; } +// Spawn region diameter (see getSpawnTiles in SpawnExecution — euclidean +// radius 4). Used to size spawn-phase names directly off the spawn tile, +// instead of waiting on cluster recomputation. +const SPAWN_REGION_DIAMETER = 8; + +/** + * Place a player's name during the spawn phase using their currently-selected + * spawn tile. Tracks re-rolls immediately, since spawnTile updates the same + * tick the player picks a new location. + */ +export function placeSpawnName(game: Game, player: Player): NameViewData { + const spawnTile = player.spawnTile(); + if (spawnTile === undefined) { + return { x: 0, y: 0, size: 0 }; + } + const fontSize = calculateFontSize( + { + x: 0, + y: 0, + width: SPAWN_REGION_DIAMETER, + height: SPAWN_REGION_DIAMETER, + }, + player.displayName(), + ); + return { + x: Math.ceil(game.x(spawnTile)), + y: Math.ceil(game.y(spawnTile) - fontSize / 3), + size: fontSize, + }; +} + export function placeName(game: Game, player: Player): NameViewData { const boundingBox = player.largestClusterBoundingBox ?? diff --git a/src/core/GameRunner.ts b/src/core/GameRunner.ts index f98dcedb5..702abf110 100644 --- a/src/core/GameRunner.ts +++ b/src/core/GameRunner.ts @@ -1,4 +1,4 @@ -import { placeName } from "../client/hud/NameBoxCalculator"; +import { placeName, placeSpawnName } from "../client/hud/NameBoxCalculator"; import { Config } from "./configuration/Config"; import { Executor } from "./execution/ExecutionManager"; import { RecomputeRailClusterExecution } from "./execution/RecomputeRailClusterExecution"; @@ -160,16 +160,14 @@ export class GameRunner { return false; } - if (this.game.inSpawnPhase() && this.game.ticks() % 2 === 0) { - this.game - .players() - .filter( - (p) => - p.type() === PlayerType.Human || p.type() === PlayerType.Nation, - ) - .forEach( - (p) => (this.playerViewData[p.id()] = placeName(this.game, p)), - ); + if (this.game.inSpawnPhase()) { + for (const p of this.game.players()) { + if (p.type() !== PlayerType.Human && p.type() !== PlayerType.Nation) { + continue; + } + if (p.spawnTile() === undefined) continue; + this.playerViewData[p.id()] = placeSpawnName(this.game, p); + } } const spawnJustEnded = wasInSpawnPhase && !this.game.inSpawnPhase(); @@ -178,9 +176,9 @@ export class GameRunner { this.game.ticks() < 3 || this.game.ticks() % 30 === 0 ) { - this.game.players().forEach((p) => { + for (const p of this.game.players()) { this.playerViewData[p.id()] = placeName(this.game, p); - }); + } } const packedTileUpdates = this.game.drainPackedTileUpdates();