59 lines
3.2 KiB
JavaScript
59 lines
3.2 KiB
JavaScript
import { MAP_H, MAP_W, indexOf, inside } from "./grid.js";
|
|
import { hash2 } from "./random.js";
|
|
|
|
const CONTEXT_SUFFIXES = {
|
|
coastal: ["\u6d5c", "\u6e4a", "\u6e2f", "\u6d66", "\u6d25", "\u5d0e", "\u6e7e"],
|
|
river: ["\u5ddd", "\u702c", "\u6a4b", "\u6e21", "\u6cbc", "\u6ca2"],
|
|
plain: ["\u539f", "\u91ce", "\u7530", "\u91cc", "\u6751", "\u5e73"],
|
|
mountain: ["\u8c37", "\u5ce0", "\u5c3e\u6839", "\u5c71", "\u6ca2", "\u9e93"],
|
|
historic: ["\u57ce", "\u5e9c", "\u9928", "\u5bae", "\u753a"],
|
|
suburban: ["\u4e18", "\u53f0", "\u91ce", "\u539f", "\u65b0\u753a", "\u30f6\u4e18"],
|
|
};
|
|
|
|
function isNearSea(x, y, sea, radius = 3) {
|
|
if (!sea) return false;
|
|
for (let dy = -radius; dy <= radius; dy++) {
|
|
for (let dx = -radius; dx <= radius; dx++) {
|
|
const nx = x + dx;
|
|
const ny = y + dy;
|
|
if (inside(nx, ny) && sea[indexOf(nx, ny)]) return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export function chooseNameSuffixPool(entity, fields) {
|
|
const kind = String(entity.kind || "").toLowerCase();
|
|
const i = indexOf(Math.max(0, Math.min(MAP_W - 1, entity.x)), Math.max(0, Math.min(MAP_H - 1, entity.y)));
|
|
const coastal = (fields?.coastalLowland?.[i] || 0) > 0.35 || isNearSea(entity.x, entity.y, fields?.sea);
|
|
const river = (fields?.river?.[i] || 0) > 0.35 || (fields?.valleyField?.[i] || 0) > 0.42;
|
|
const plain = (fields?.plain?.[i] || 0) > 0.48 || (fields?.agriculture?.[i] || 0) > 0.46 || (fields?.basinField?.[i] || 0) > 0.35;
|
|
const mountain = (fields?.elevation?.[i] || 0) > 0.52 || (fields?.slope?.[i] || 0) > 0.35 || (fields?.ridgeField?.[i] || 0) > 0.42;
|
|
|
|
if (kind.includes("port") || kind.includes("harbor") || entity.portClass) return CONTEXT_SUFFIXES.coastal;
|
|
if (kind.includes("crossing") || kind.includes("bridge")) return CONTEXT_SUFFIXES.river;
|
|
if (kind.includes("pass") || kind.includes("mountain")) return CONTEXT_SUFFIXES.mountain;
|
|
if (kind.includes("castle") || kind.includes("market")) return CONTEXT_SUFFIXES.historic;
|
|
if (kind.includes("new town") || kind.includes("satellite")) return CONTEXT_SUFFIXES.suburban;
|
|
if (coastal && !mountain) return CONTEXT_SUFFIXES.coastal;
|
|
if (river) return CONTEXT_SUFFIXES.river;
|
|
if (mountain && !plain) return CONTEXT_SUFFIXES.mountain;
|
|
if (plain) return CONTEXT_SUFFIXES.plain;
|
|
return CONTEXT_SUFFIXES.plain;
|
|
}
|
|
|
|
export function contextualDefaultName(seed, id, entity, nameParts, fields, attempt = 0) {
|
|
const prefixKey = id.split("-")[0];
|
|
const n = Number(id.split("-")[1] || 0) + attempt * 997;
|
|
const prefixes = nameParts.prefixes || [""];
|
|
const infixes = nameParts.infixes || [""];
|
|
const suffixPool = chooseNameSuffixPool(entity, fields);
|
|
const fallbackSuffixes = nameParts.suffixes || [""];
|
|
const prefix = prefixes[(seed + n * 7) % prefixes.length];
|
|
const useInfix = hash2(n + prefixKey.length * 17, seed + n * 31, seed + 2777) >= 0.78;
|
|
const infix = useInfix ? infixes[(seed * 3 + n * 11) % infixes.length] : "";
|
|
const contextualSuffix = suffixPool[(seed * 5 + n * 13 + prefixKey.length) % suffixPool.length];
|
|
const fallbackSuffix = fallbackSuffixes[(seed * 7 + n * 17 + prefixKey.length) % fallbackSuffixes.length];
|
|
const suffix = attempt % 4 === 3 ? fallbackSuffix : contextualSuffix;
|
|
return `${prefix}${infix}${suffix}`;
|
|
}
|