-
-
-
-
+
+
diff --git a/client/js/game_screen.js b/client/js/game_screen.js
index f0f0c1f..1424fde 100644
--- a/client/js/game_screen.js
+++ b/client/js/game_screen.js
@@ -32,6 +32,7 @@ export class GameScreen {
this._tileBattleGdpDeficits = new Map();
this._territoryOwner = new Map();
this._tileRegion = new Map();
+ this._gdpExpanded = false;
this._battles = new Set();
this._researched = new Set();
this._wars = new Set();
diff --git a/client/js/game_screen/panels.js b/client/js/game_screen/panels.js
index a05168b..116f4cf 100644
--- a/client/js/game_screen/panels.js
+++ b/client/js/game_screen/panels.js
@@ -574,16 +574,16 @@ export const panelMethods = {
_renderTileInfo(coords) {
const k = `${coords.x},${coords.y}`;
const owner = this._territoryOwner.has(k) ? this._territoryOwner.get(k) : -1;
- const civ = this.civilisations[owner];
const tile = this.map.getTile(coords);
const city = (this.snapshot.cities || []).find(
(c) => c.coords[0] === coords.x && c.coords[1] === coords.y
);
+ const regionCity = (this.snapshot.cities || []).find(
+ (c) => c.id === this._tileRegion.get(k)
+ );
+ $("#tile-desc").text(this._tileDescription(coords, owner, tile, regionCity));
$("#tile-coords").text(`Tile (${coords.x}, ${coords.y})`);
- $("#tile-owner").text(`Owner: ${civ ? civ.name : "Unclaimed"}`);
- $("#tile-terrain").text(`Terrain: ${tile ? tile.terrainType : "Unknown"}`);
- $("#tile-population").text(`Population: ${groupDigits(this._population.get(k) || 0)}`);
- this._renderTileEthnicity(k);
+ this._renderTilePeople(k);
this._renderTileGdp(owner >= 0 ? this._tileGdpBreakdown(coords, owner, city, tile) : null);
const roads = this.map.roads && this.map.roads.has(k) ? [ROADS.improvementName] : [];
const improvements = city
@@ -592,15 +592,61 @@ export const panelMethods = {
$("#tile-improvements").text(`Improvements: ${fmtImprovements(improvements)}`);
},
- // The tile's people, by ethnicity, as a small donut plus a legend with head
- // counts and last-day migration arrows.
- _renderTileEthnicity(k) {
+ // A short, readable label for the tile: its owner's demonym, the terrain and
+ // where it sits relative to the city whose region claims it.
+ _tileDescription(coords, owner, tile, regionCity) {
+ const civ = owner >= 0 ? this.civilisations[owner] : null;
+ const terrain = tile ? tile.terrainType.toLowerCase() : "unknown terrain";
+ const parts = [];
+ if (civ) parts.push(civ.demonym || civ.name);
+ parts.push(terrain);
+ if (regionCity) {
+ const sameTile =
+ regionCity.coords[0] === coords.x && regionCity.coords[1] === coords.y;
+ if (sameTile) {
+ parts.push(`around ${regionCity.name}`);
+ } else {
+ const direction = this._compassDirection(
+ { x: regionCity.coords[0], y: regionCity.coords[1] },
+ coords
+ );
+ if (direction) parts.push(`${direction} of ${regionCity.name}`);
+ }
+ }
+ const text = parts.join(" ");
+ return text.charAt(0).toUpperCase() + text.slice(1);
+ },
+
+ // The eight-point compass direction from `origin` to `coords`, measured in
+ // screen space so the label matches what the player sees on the map.
+ _compassDirection(origin, coords) {
+ const topology = this.map.topology;
+ if (!topology) return "";
+ const delta = topology.pixelDelta(origin, coords);
+ if (delta.x === 0 && delta.y === 0) return "";
+ const names = [
+ "east", "southeast", "south", "southwest",
+ "west", "northwest", "north", "northeast",
+ ];
+ const index = Math.round(Math.atan2(delta.y, delta.x) / (Math.PI / 4));
+ return names[((index % 8) + 8) % 8];
+ },
+
+ // The tile's people: the head count up top, then a small donut and legend of
+ // its ethnicities with last-day migration arrows.
+ _renderTilePeople(k) {
const $container = $("#tile-ethnicity");
if (!$container || !$container.length) return;
$container.empty();
+ const population = this._population.get(k) || 0;
+ $container.append($("
").text("People"));
+ $container.append(
+ $("
").text(
+ `Population: ${groupDigits(population)}`
+ )
+ );
const entries = this._tileEthnicity.get(k) || [];
if (entries.length === 0) return;
- const population = this._population.get(k) || 0;
const makeup = entries
.map(([ethnicity, share, trend]) => ({
ethnicity,
@@ -609,7 +655,6 @@ export const panelMethods = {
trend,
}))
.sort((a, b) => b.count - a.count);
- $container.append($("
").text("People"));
$container.append(ethnicityChart(makeup, this.civilisations, 104));
},
@@ -693,12 +738,12 @@ export const panelMethods = {
$container.text("GDP/capita: —");
return;
}
- $container.append(
- $("
").text(`GDP/capita: €${groupDigits(Math.round(breakdown.total))}`)
- );
- $container.append(
+ const $mods = $("
");
+ if (!this._gdpExpanded) $mods.addClass("hidden");
+ $mods.append(
$("
").text(`Base €${groupDigits(breakdown.base)}`)
);
+ let modifierCount = 0;
for (const factor of breakdown.factors) {
// A modifier that changes nothing (×1 or ±€0) is left out.
if (factor.amount !== undefined) {
@@ -710,9 +755,27 @@ export const panelMethods = {
factor.amount !== undefined
? `−€${groupDigits(Math.round(Math.abs(factor.amount)))}`
: `×${factor.multiplier.toFixed(2)}`;
- $container.append(
+ $mods.append(
$("
").text(`${factor.label} ${value}`)
);
+ modifierCount += 1;
+ }
+ const $head = $("
").text(
+ `GDP/capita: €${groupDigits(Math.round(breakdown.total))}`
+ );
+ if (modifierCount > 0) {
+ $head.addClass("tile-gdp-toggle");
+ $head.append(
+ $("
").text(this._gdpExpanded ? "▾" : "▸")
+ );
+ $head.on("click", () => {
+ this._gdpExpanded = !this._gdpExpanded;
+ $mods.toggleClass("hidden", !this._gdpExpanded);
+ $head.find(".tile-gdp-caret").text(this._gdpExpanded ? "▾" : "▸");
+ });
+ $container.append($head, $mods);
+ } else {
+ $container.append($head);
}
},