Three Kingdoms: Warlord's Fate — complete playable game
- Stylized 3D ink-painting map of China (12 provinces, 32 cities, 17 factions) - Custom warlord creation (8 origins, banner, starting city) or historical factions - City management: 7 buildings, 5 dev tiers, recruitment from levies - Character system: stats, traits, loyalty, relationships, wounds, capture, death, succession - Turn-based tactical battles with formations, stances, hero skills, cinematic 3D replay - Sieges: assault, starvation, bribery, infiltration - Diplomacy with trust memory, alliances, NAPs, trade, marriage, espionage, betrayal - Scripted diverging history (Dong Zhuo, Guandu, Red Cliffs...) + world crises + court events - AI factions with distinct personalities; prisoners (execute/release/recruit/ransom) - Procedural guqin/taiko WebAudio score; save/load; victory + dynasty chronicle screens - View-relative camera controls; headless test suites (smoke, stress, map validator)
This commit is contained in:
+74
@@ -0,0 +1,74 @@
|
||||
// ============================================================
|
||||
// WORLD — derived geometry from the hex map (adjacency, centroids)
|
||||
// ============================================================
|
||||
import { MAP_ROWS, HEX_R, hexToWorld, PROVINCES } from "./data.js";
|
||||
|
||||
export const HEX_W = Math.sqrt(3) * HEX_R;
|
||||
|
||||
function nbrs(c, r) {
|
||||
const odd = r % 2;
|
||||
return [[c - 1, r], [c + 1, r], [c + odd - 1, r - 1], [c + odd, r - 1], [c + odd - 1, r + 1], [c + odd, r + 1]];
|
||||
}
|
||||
|
||||
const rows = MAP_ROWS;
|
||||
const H = rows.length;
|
||||
const W = Math.max(...rows.map(r => r.length));
|
||||
const at = (c, r) => (r < 0 || r >= H || c < 0 || c >= (rows[r]?.length ?? 0)) ? "." : rows[r][c];
|
||||
|
||||
export const hexes = [];
|
||||
for (let r = 0; r < H; r++) {
|
||||
for (let c = 0; c < rows[r].length; c++) {
|
||||
const ch = rows[r][c];
|
||||
if (ch === ".") continue;
|
||||
const [x, z] = hexToWorld(c, r);
|
||||
hexes.push({ col: c, row: r, ch, prov: ch.toLowerCase(), mountain: ch !== ch.toLowerCase(), x, z });
|
||||
}
|
||||
}
|
||||
|
||||
// province adjacency
|
||||
export const PROV_ADJ = {};
|
||||
for (const p of Object.keys(PROVINCES)) PROV_ADJ[p] = new Set();
|
||||
const key = (c, r) => c + "," + r;
|
||||
const provAt = (c, r) => { const ch = at(c, r); return ch === "." ? null : ch.toLowerCase(); };
|
||||
for (const h of hexes) {
|
||||
for (const [nc, nr] of nbrs(h.col, h.row)) {
|
||||
const q = provAt(nc, nr);
|
||||
if (q && q !== h.prov) { PROV_ADJ[h.prov].add(q); PROV_ADJ[q].add(h.prov); }
|
||||
}
|
||||
}
|
||||
export const provNeighbors = p => [...PROV_ADJ[p]];
|
||||
|
||||
// centroids (average of hexes, biased toward cities)
|
||||
export const PROV_CENTROID = {};
|
||||
for (const p of Object.keys(PROVINCES)) {
|
||||
let sx = 0, sz = 0, n = 0;
|
||||
for (const h of hexes) if (h.prov === p) { sx += h.x; sz += h.z; n++; }
|
||||
PROV_CENTROID[p] = n ? [sx / n, sz / n] : [0, 0];
|
||||
}
|
||||
// pull centroid toward cities so markers sit near population
|
||||
import { CITY_DEFS } from "./data.js";
|
||||
for (const p of Object.keys(PROVINCES)) {
|
||||
const cs = CITY_DEFS.filter(c => c[3] === p);
|
||||
if (!cs.length) continue;
|
||||
const [cx, cz] = PROV_CENTROID[p];
|
||||
let bx = 0, bz = 0;
|
||||
for (const [, , , , col, row] of cs) { const [x, z] = hexToWorld(col, row); bx += x; bz += z; }
|
||||
bx /= cs.length; bz /= cs.length;
|
||||
PROV_CENTROID[p] = [cx * 0.45 + bx * 0.55, cz * 0.45 + bz * 0.55];
|
||||
}
|
||||
|
||||
// distance between provinces (for AI threat estimates)
|
||||
export function provDist(a, b, depth = 0, seen = new Set()) {
|
||||
if (a === b) return 0;
|
||||
if (PROV_ADJ[a]?.has(b)) return 1;
|
||||
seen.add(a);
|
||||
let best = Infinity;
|
||||
for (const n of PROV_ADJ[a] || []) {
|
||||
if (seen.has(n)) continue;
|
||||
best = Math.min(best, 1 + provDist(n, b, depth + 1, seen));
|
||||
if (depth > 6) break;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
export function adjacentProvinces(a, b) { return !!PROV_ADJ[a]?.has(b); }
|
||||
Reference in New Issue
Block a user