many
This commit is contained in:
parent
0b9ff1363b
commit
0a6148c73f
67 changed files with 2653 additions and 1519 deletions
124
js/sim_core.js
124
js/sim_core.js
|
|
@ -64,6 +64,62 @@ function stableUnit(seed, salt = "") {
|
|||
return ((h >>> 0) % 100000) / 100000;
|
||||
}
|
||||
|
||||
const GENETIC_KEYS = ["lifeSpanMul", "attackMul", "speedMul", "sizeMul"];
|
||||
const GENETIC_LIMITS = Object.freeze({
|
||||
lifeSpanMul: [0.82, 1.24],
|
||||
attackMul: [0.80, 1.24],
|
||||
speedMul: [0.86, 1.18],
|
||||
sizeMul: [0.84, 1.18],
|
||||
});
|
||||
|
||||
function clampGeneticValue(key, value) {
|
||||
const range = GENETIC_LIMITS[key] || [0.75, 1.25];
|
||||
return clamp(Number(value) || 1, range[0], range[1]);
|
||||
}
|
||||
|
||||
function randomGenetics(seed = "") {
|
||||
return {
|
||||
lifeSpanMul: clampGeneticValue("lifeSpanMul", 0.82 + stableUnit(seed, "gene-life") * 0.42),
|
||||
attackMul: clampGeneticValue("attackMul", 0.80 + stableUnit(seed, "gene-attack") * 0.44),
|
||||
speedMul: clampGeneticValue("speedMul", 0.86 + stableUnit(seed, "gene-speed") * 0.32),
|
||||
sizeMul: clampGeneticValue("sizeMul", 0.84 + stableUnit(seed, "gene-size") * 0.34),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeGenetics(source = null, seed = "") {
|
||||
const fallback = randomGenetics(seed);
|
||||
const out = {};
|
||||
for (const key of GENETIC_KEYS) out[key] = clampGeneticValue(key, source?.[key] ?? fallback[key]);
|
||||
return out;
|
||||
}
|
||||
|
||||
function inheritedGenetics(a, b, seed = "") {
|
||||
const ga = normalizeGenetics(a?.genetics, a?.familyKey || a?.id || "a");
|
||||
const gb = normalizeGenetics(b?.genetics, b?.familyKey || b?.id || "b");
|
||||
const out = {};
|
||||
for (const key of GENETIC_KEYS) {
|
||||
const base = ((ga[key] || 1) + (gb[key] || 1)) / 2;
|
||||
const mutation = (stableUnit(seed, `gene-mut-${key}`) - 0.5) * 0.10;
|
||||
out[key] = clampGeneticValue(key, base + mutation);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function adultScaleFromGenetics(seed = "", genetics = null) {
|
||||
const g = normalizeGenetics(genetics, seed);
|
||||
return clamp((0.265 + stableUnit(seed, "adult-scale") * 0.060) * g.sizeMul, 0.205, 0.390);
|
||||
}
|
||||
|
||||
function childGrowthScaleFactor(growth = 1) {
|
||||
const x = clamp(Number(growth) || 0, 0, 1);
|
||||
const smooth = x * x * (3 - 2 * x);
|
||||
return 0.46 + smooth * 0.54;
|
||||
}
|
||||
|
||||
function scaleForGrowth(adultScale = 0.28, growth = 1) {
|
||||
return clamp((Number(adultScale) || 0.28) * childGrowthScaleFactor(growth), 0.10, 0.42);
|
||||
}
|
||||
|
||||
const PERSONALITIES = Object.freeze({});
|
||||
const PERSONALITY_IDS = [];
|
||||
|
||||
|
|
@ -73,7 +129,7 @@ function personalityForSeed(seed) {
|
|||
|
||||
const PERSONALITY_KEYS = ["aggression", "openness", "sociability", "neuroticism"];
|
||||
const PERSONALITY_DAILY_LIMIT = { perAxis: 0.08, total: 0.20 };
|
||||
const FRIEND_AFFINITY_THRESHOLD = 16;
|
||||
const FRIEND_AFFINITY_THRESHOLD = 14;
|
||||
const PERSONALITY_LABELS = {
|
||||
aggression: { label: "\u653b\u6483\u6027", low: "\u304a\u3060\u3084\u304b", high: "\u6012\u308a\u3063\u307d\u3044" },
|
||||
openness: { label: "\u958b\u653e\u6027", low: "\u4fdd\u5b88\u7684", high: "\u904a\u3073\u597d\u304d" },
|
||||
|
|
@ -91,6 +147,28 @@ function normalizePersonality(source = null) {
|
|||
return out;
|
||||
}
|
||||
|
||||
function zunchiSlavePersonalityValue() {
|
||||
const out = {};
|
||||
for (const key of PERSONALITY_KEYS) out[key] = -1;
|
||||
return out;
|
||||
}
|
||||
|
||||
function applyZunchiSlavePersonality(tarinai, opts = {}) {
|
||||
if (!tarinai) return false;
|
||||
const next = zunchiSlavePersonalityValue();
|
||||
let changed = false;
|
||||
tarinai.birthPersonality = normalizePersonality(tarinai.birthPersonality || next);
|
||||
tarinai.currentPersonality = normalizePersonality(tarinai.currentPersonality || next);
|
||||
for (const key of PERSONALITY_KEYS) {
|
||||
if ((tarinai.currentPersonality[key] || 0) !== -1) changed = true;
|
||||
tarinai.currentPersonality[key] = -1;
|
||||
if (opts.birth) tarinai.birthPersonality[key] = -1;
|
||||
}
|
||||
tarinai.personalityDaily = { day: tarinai.world?.day || 1, total: 0, byKey: {}, byCause: {} };
|
||||
if (changed && opts.record !== false) tarinai.recordChangeCause?.("\u305a\u3093\u3061\u3069\u308c\u3044\u5316", "\u6027\u683c", { value: -1 });
|
||||
return changed;
|
||||
}
|
||||
|
||||
function randomBirthPersonality(seed = "") {
|
||||
const out = {};
|
||||
for (const key of PERSONALITY_KEYS) out[key] = clampPersonalityValue(stableUnit(seed, `personality-${key}`) * 1.2 - 0.6);
|
||||
|
|
@ -137,6 +215,10 @@ function personalityReasonText(reason = "") {
|
|||
"after repeated contact with poop": "\u305a\u3093\u3061\u306b\u6163\u308c\u305f\u5f71\u97ff\u3067",
|
||||
"after repeated contact with corpses": "\u6b7b\u9ab8\u306b\u6163\u308c\u305f\u5f71\u97ff\u3067",
|
||||
"after being hurt": "\u75db\u307f\u3092\u899a\u3048\u305f\u5f71\u97ff\u3067",
|
||||
"after ammo knockback": "\u5f3e\u85ac\u3067\u5f3e\u304d\u98db\u3070\u3057\u305f\u5f71\u97ff\u3067",
|
||||
"after successful intimidation": "\u5a01\u5687\u306b\u6210\u529f\u3057\u305f\u5f71\u97ff\u3067",
|
||||
"after failed intimidation": "\u5a01\u5687\u306b\u5931\u6557\u3057\u305f\u5f71\u97ff\u3067",
|
||||
"after being struck by genkotsu": "\u3052\u3093\u3053\u3064\u3092\u53d7\u3051\u305f\u5f71\u97ff\u3067",
|
||||
};
|
||||
return map[key] || key;
|
||||
}
|
||||
|
|
@ -171,14 +253,31 @@ function personalityDailyState(tarinai) {
|
|||
const day = tarinai?.world?.day || 1;
|
||||
const state = tarinai.personalityDaily || {};
|
||||
if (state.day !== day) {
|
||||
tarinai.personalityDaily = { day, total: 0, byKey: {} };
|
||||
tarinai.personalityDaily = { day, total: 0, byKey: {}, byCause: {} };
|
||||
return tarinai.personalityDaily;
|
||||
}
|
||||
state.byKey = state.byKey || {};
|
||||
for (const key of Object.keys(state.byKey)) {
|
||||
const v = state.byKey[key];
|
||||
if (typeof v === "number") state.byKey[key] = { up: Math.max(0, v), down: Math.max(0, -v) };
|
||||
else state.byKey[key] = { up: Math.max(0, Number(v?.up) || 0), down: Math.max(0, Number(v?.down) || 0) };
|
||||
if (typeof v === "number") state.byKey[key] = { up: Math.max(0, v), down: Math.max(0, -v), causes: [] };
|
||||
else {
|
||||
const causes = Array.isArray(v?.causes) ? [...new Set(v.causes.map(c => String(c || "").trim()).filter(Boolean))].slice(0, 4) : [];
|
||||
state.byKey[key] = { up: Math.max(0, Number(v?.up) || 0), down: Math.max(0, Number(v?.down) || 0), causes };
|
||||
}
|
||||
}
|
||||
state.byCause = state.byCause && typeof state.byCause === "object" ? state.byCause : {};
|
||||
for (const cause of Object.keys(state.byCause)) {
|
||||
const entry = state.byCause[cause] || {};
|
||||
const byKey = entry.byKey && typeof entry.byKey === "object" ? entry.byKey : {};
|
||||
const nextByKey = {};
|
||||
for (const key of PERSONALITY_KEYS) {
|
||||
const v = byKey[key] || {};
|
||||
const up = Math.max(0, Number(v.up) || 0);
|
||||
const down = Math.max(0, Number(v.down) || 0);
|
||||
if (up > 0.0001 || down > 0.0001) nextByKey[key] = { up, down };
|
||||
}
|
||||
if (Object.keys(nextByKey).length) state.byCause[cause] = { byKey: nextByKey };
|
||||
else delete state.byCause[cause];
|
||||
}
|
||||
state.total = Math.max(0, Number(state.total) || 0);
|
||||
tarinai.personalityDaily = state;
|
||||
|
|
@ -229,8 +328,23 @@ function adjustPersonality(tarinai, key, delta, reason = "") {
|
|||
if (!actual) return 0;
|
||||
tarinai.currentPersonality[key] = after;
|
||||
state.byKey[key] = used;
|
||||
if (!Array.isArray(used.causes)) used.causes = [];
|
||||
used[actual > 0 ? "up" : "down"] = (used[actual > 0 ? "up" : "down"] || 0) + Math.abs(actual);
|
||||
state.total = (state.total || 0) + Math.abs(actual);
|
||||
const label = PERSONALITY_LABELS[key]?.label || key;
|
||||
const reasonText = personalityReasonText(reason) || "\u6027\u683c\u5909\u5316";
|
||||
if (reasonText && !used.causes.includes(reasonText)) used.causes.push(reasonText);
|
||||
if (used.causes.length > 4) used.causes.length = 4;
|
||||
if (reasonText) {
|
||||
state.byCause = state.byCause && typeof state.byCause === "object" ? state.byCause : {};
|
||||
const causeEntry = state.byCause[reasonText] || { byKey: {} };
|
||||
causeEntry.byKey = causeEntry.byKey && typeof causeEntry.byKey === "object" ? causeEntry.byKey : {};
|
||||
const causeKeyEntry = causeEntry.byKey[key] || { up: 0, down: 0 };
|
||||
causeKeyEntry[actual > 0 ? "up" : "down"] = (causeKeyEntry[actual > 0 ? "up" : "down"] || 0) + Math.abs(actual);
|
||||
causeEntry.byKey[key] = causeKeyEntry;
|
||||
state.byCause[reasonText] = causeEntry;
|
||||
}
|
||||
tarinai.recordChangeCause?.(reasonText, label, { value: actual });
|
||||
const afterBand = personalityBand(after);
|
||||
if (beforeBand !== afterBand) {
|
||||
const text = personalityThresholdLogText(tarinai, key, after, reason, beforeBand);
|
||||
|
|
@ -458,7 +572,7 @@ class SpatialGrid {
|
|||
const type = it.type || "";
|
||||
if (type === "stone" || type === "ball" || type === "nest_box" || type === "bed" || type === "fence_v" || type === "fence_h") this.add(this.obstacleCells, it);
|
||||
if (type === "sweet" || type === "love_mochi" || type === "fight_mochi" || type === "grass" || type === "water" || type === "water_bowl" || type === "ant_corpse" || (typeof isParamEffectItemType === "function" && isParamEffectItemType(type))) this.add(this.foodCells, it);
|
||||
if (type === "firecracker" || type === "genkotsu" || type === "sleep_drug" || type === "zunchi" || type === "splat" || type === "mystery_drug" || type === "laxative" || type === "niteropu" || type === "mercury" || type === "giant_drug" || type === "dwarf_drug") this.add(this.hazardCells, it);
|
||||
if (type === "firecracker" || type === "genkotsu" || type === "pushpin" || type === "sleep_drug" || type === "zunchi" || type === "splat" || type === "mystery_drug" || type === "laxative" || type === "niteropu" || type === "mercury" || type === "giant_drug" || type === "dwarf_drug") this.add(this.hazardCells, it);
|
||||
}
|
||||
|
||||
rebuild(items, tarinai, ants = []) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue