Files
Battle-for-Tismo/client/js/map_view/modes.js
T

136 lines
5.3 KiB
JavaScript

// The map-mode switch: terrain, political, and the economic colour modes. It
// owns which fill the map is showing, keeps the effective political flag in
// step with the camera zoom (terrain mode still turns political when far out),
// and drives the economic fill and its legend. The political names themselves
// live in ./political.js; this file only decides what is on screen.
import { HEX_W } from "../../../shared/hex.js";
import { POLITICAL_ZOOM } from "./constants.js";
import { ECONOMIC_GRADIENT, ECONOMIC_MODES, economicColour, isEconomicMode } from "./economic.js";
import { NEUTRAL_LAND, POLITICAL_WATER } from "./political.js";
import { isWaterTile } from "./textures.js";
export const MAP_MODES = ["terrain", "political", "gdp", "population"];
export const modeMethods = {
// Selects a mode and repaints. Terrain mode keeps the far-out auto-switch, so
// "political" forces the political map at any zoom while "terrain" only shows
// it once the camera has pulled back.
setMapMode(mode) {
if (!MAP_MODES.includes(mode) || mode === this.mapMode) return;
this.mapMode = mode;
this._syncModeState();
this._refreshMapColours();
},
// Installs the per-tile figures an economic mode paints. Called by the game
// screen whenever a snapshot arrives while such a mode is active. A cheap
// signature skips the rebuild when nothing moved.
setEconomicData(kind, values, min, max) {
this.economicKind = kind;
this.economicValues = values instanceof Map ? values : new Map(values || []);
this.economicRange = { min: min || 0, max: max || 0 };
const signature = this._economicSignature();
if (signature === this._economicSignatureDone) return;
this._economicSignatureDone = signature;
if (this.economic) this._refreshMapColours();
},
_economicSignature() {
let sum = 0;
for (const value of this.economicValues.values()) sum += value;
return `${this.economicKind}:${this.economicValues.size}:${Math.round(sum)}`;
},
// Political shows when the user asked for it, or while terrain mode is
// zoomed far enough out. Economic modes are exclusive with it.
_effectivePolitical() {
return this.mapMode === "political" ||
(this.mapMode === "terrain" && this.camera.zoom <= POLITICAL_ZOOM);
},
_isEconomic() {
return isEconomicMode(this.mapMode);
},
// Recomputes the two effective flags and reflects them on the world element.
// The renderers read `political` / `economic` off the view.
_syncModeState() {
this.political = this._effectivePolitical();
this.economic = this._isEconomic();
this.$world.toggleClass("political", this.political);
this.$world.toggleClass("economic", this.economic);
},
// Called from the camera whenever the zoom changes: terrain mode may have
// crossed into or out of the far-out political map.
_updatePoliticalMode() {
const on = this._effectivePolitical();
if (on === this.political) return;
this._syncModeState();
this._refreshMapColours();
},
// Repaints the active fill and the labels/legend that go with it. The DOM
// renderer recolours its chunks; WebGL rebuilds the fill passes.
_refreshMapColours() {
if (this.glRenderer) {
this.glRenderer.buildPolitical(this);
this.glRenderer.buildEconomic(this);
} else if (this.topology) {
this._rebuildDomTerrain();
}
if (this.political) this._renderPoliticalLabels();
else if (this.$politicalLabels) this.$politicalLabels.empty();
this._updateLegend();
},
// Rebuilds every materialised chunk so a change of mode or territory colour
// is reflected in the DOM terrain. Only the chunks near the camera exist, so
// this stays cheap.
_rebuildDomTerrain() {
if (!this._chunks) return;
this._destroyAllChunks();
this._chunkDirty = true;
this._chunkView = { x: null, y: null, zoom: null };
this._syncChunks();
},
// Shows the gradient scale while an economic mode is active, with the range's
// low, middle and high figures for the current mode.
_updateLegend() {
const $legend = this.$legend;
if (!$legend || !$legend.length) return;
const meta = ECONOMIC_MODES[this.mapMode];
if (!this.economic || !meta) {
$legend.addClass("hidden");
return;
}
$legend.removeClass("hidden");
$legend.find("#map-legend-title").text(meta.label);
const { min, max } = this.economicRange;
$legend.find("#map-legend-min").text(meta.format(min));
$legend.find("#map-legend-mid").text(meta.format((min + max) / 2));
$legend.find("#map-legend-max").text(meta.format(max));
$legend.find("#map-legend-bar")
.css("background", `linear-gradient(to right, ${ECONOMIC_GRADIENT.join(", ")})`);
},
// Paints one DOM terrain tile with its economic colour. Water and tiles with
// no figure stay neutral; the atlas mask still clips the hexagon.
_applyEconomicColour(div, tile, k) {
let colour = POLITICAL_WATER;
if (!isWaterTile(tile)) {
const value = this.economicValues.get(k);
colour = value === undefined
? NEUTRAL_LAND
: economicColour(value, this.economicRange.min, this.economicRange.max);
}
div.style.backgroundImage = "none";
div.style.backgroundColor = colour;
const maskPosition = `${-tile.col * HEX_W}px 0`;
div.style.maskPosition = maskPosition;
div.style.webkitMaskPosition = maskPosition;
},
};