Files
deepseek f040bb6be0 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)
2026-08-23 06:59:40 +00:00

75 lines
2.6 KiB
JavaScript

// ============================================================
// 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); }