This commit is contained in:
33333-33333 2026-07-20 14:36:59 +09:00
commit f6d7412744
94 changed files with 3836 additions and 1226 deletions

View file

@ -2,37 +2,47 @@
function inheritedChampionPersonality(seed = "", parents = [], opts = {}) {
const axes = ["aggression", "openness", "sociability"];
const inheritedUnit = (...parts) => typeof globalThis.stableUnit === "function" ? globalThis.stableUnit(...parts) : 0.5;
const axes = ["aggression", "openness", "sociability", "neuroticism"];
const profileFor = (birthSeed, profileOpts = {}) => globalThis.TarinaiSeedFactory?.birthProfile?.(birthSeed || "", profileOpts)?.birthPersonality || {};
const childBase = normalizePersonality(profileFor(seed, opts));
let inheritedAny = false;
const randomBase = normalizePersonality(profileFor(seed, opts));
const validParents = (parents || []).filter(Boolean).slice(0, 2);
if (!validParents.length) return null;
const child = { ...randomBase };
for (const key of axes) {
const values = validParents.map(parent => {
const parentBase = normalizePersonality(profileFor(parent.birthSeed || parent.familyKey || ""));
return Number(normalizePersonality(parent.birthPersonality || parentBase)[key]) || 0;
});
let inherited = values[0] || 0;
if (values.length > 1) {
const aWeight = 0.42 + inheritedUnit(seed, `personality-parent-weight-${key}`) * 0.16;
inherited = values[0] * aWeight + values[1] * (1 - aWeight);
}
const mutation = (inheritedUnit(seed, `personality-mut-${key}`) - 0.5) * 0.04;
child[key] = clampPersonalityValue(inherited * 0.94 + (Number(randomBase[key]) || 0) * 0.06 + mutation);
}
// Champion lineage boosts remain heritable on top of the stronger ordinary
// personality inheritance, so royal descendants still preserve that trait.
for (const key of ["aggression", "openness", "sociability"]) {
let totalLegacy = 0;
let parentCount = 0;
for (const parent of parents || []) {
if (!parent) continue;
for (const parent of validParents) {
if (!parent.isTarinaiChampion) continue;
const parentBase = normalizePersonality(profileFor(parent.birthSeed || parent.familyKey || ""));
const born = normalizePersonality(parent.birthPersonality || parentBase);
// A champion's three boosted axes become a heritable lineage delta. A
// descendant that inherited that delta can pass a damped form onward.
const inheritedDelta = Math.max(0, (Number(born[key]) || 0) - (Number(parentBase[key]) || 0));
let championDelta = 0;
if (parent.isTarinaiChampion) {
const baseValue = Number(parentBase[key]) || 0;
const championTarget = clampPersonalityValue(Math.max(baseValue + 0.48, 0.86));
championDelta = Math.max(0, championTarget - baseValue);
}
const legacy = Math.max(inheritedDelta, championDelta);
if (legacy <= 0) continue;
totalLegacy += legacy;
const baseValue = Number(parentBase[key]) || 0;
const championTarget = clampPersonalityValue(Math.max(baseValue + 0.48, 0.86));
const championDelta = Math.max(0, championTarget - baseValue);
if (championDelta <= 0) continue;
totalLegacy += championDelta;
parentCount += 1;
}
if (parentCount > 0 && totalLegacy > 0) {
childBase[key] = clampPersonalityValue((Number(childBase[key]) || 0) + (totalLegacy / parentCount) * 0.70);
inheritedAny = true;
child[key] = clampPersonalityValue((Number(child[key]) || 0) + (totalLegacy / parentCount) * 0.82);
}
}
return inheritedAny ? childBase : null;
return child;
}
(function (global) {
@ -154,7 +164,8 @@ function inheritedChampionPersonality(seed = "", parents = [], opts = {}) {
for (const item of nearby) {
if (!item || item.dead || item.type === "trace" || item.type === "splat") continue;
const isSleepFurniture = sleepFurniture?.has?.(item.type) || ["bed", "nest_box", "pipe", "grass_bed"].includes(item.type);
if (!isSleepFurniture) continue;
const isMemorial = item.type === "grave";
if (!isSleepFurniture && !isMemorial) continue;
const isNest = global.TarinaiToolRuntime?.isNestContainerItem?.(item);
const isOwnedBed = item.type === "grass_bed";
const fallbackDistance = distXY(p.x, p.y, Number(item.x || 0), Number(item.y || 0));
@ -182,6 +193,9 @@ function inheritedChampionPersonality(seed = "", parents = [], opts = {}) {
if (best.type === "grass_bed") {
return { target: best, lines: ownedBedTooltipLinesLocal(worldRef, best), kind: "grass_bed" };
}
if (best.type === "grave") {
return { target: best, lines: global.TarinaiMemorialBellSystem?.graveTooltipLines?.(best) || ["墓"], kind: "grave" };
}
const definition = global.TarinaiToolRuntime?.itemDefinition?.(best.type) || null;
const lines = [definition?.label || String(best.type || "\u30aa\u30d6\u30b8\u30a7\u30af\u30c8")];
if (definition?.tooltip) lines.push(definition.tooltip);
@ -196,8 +210,9 @@ function inheritedChampionPersonality(seed = "", parents = [], opts = {}) {
const preset = normalizeResetPresetId(presetId);
this.fieldType = field.id;
this.fieldZoom = 1;
this.setViewportSize(this.viewportW, this.viewportH);
this.setViewportSize(this.viewportW, this.viewportH, { resizeWorld: true });
this.time = 0;
this._fixedSimulationAccumulator = 0;
this.day = 1;
this.elapsedDays = 0;
this.paused = false;
@ -289,6 +304,16 @@ function inheritedChampionPersonality(seed = "", parents = [], opts = {}) {
return t && !t.dead ? t : null;
},
compactFamilyGeneticSummary(genetics = null) {
const g = typeof normalizeGenetics === "function" ? normalizeGenetics(genetics, "") : (genetics || {});
return [
Math.round(((Number(g.lifeSpanMul) || 1) - 1) * 100),
Math.round(((Number(g.sizeMul) || 1) - 1) * 100),
Math.round(((Number(g.attackMul) || 1) - 1) * 100),
Math.round(((Number(g.speedMul) || 1) - 1) * 100),
];
},
recordFamily(t) {
if (!this.family) this.family = {};
const familyKey = this.tarinaiFamilyKey(t);
@ -332,6 +357,7 @@ function inheritedChampionPersonality(seed = "", parents = [], opts = {}) {
isTarinaiChampion: nextChampion,
alive: !t.dead,
deathReason: t.deathReason || "",
deathTime: t.deathTime != null && Number.isFinite(Number(t.deathTime)) ? Number(t.deathTime) : null,
};
changed = true;
} else {
@ -356,6 +382,7 @@ function inheritedChampionPersonality(seed = "", parents = [], opts = {}) {
if ((Number(entry.totalFightLosses) || 0) !== nextFightLosses) changed = true;
if (!!entry.isTarinaiChampion !== nextChampion) changed = true;
if ((entry.deathReason || "") !== (t.deathReason || entry.deathReason || "")) changed = true;
if ((entry.deathTime ?? null) !== (t.deathTime != null && Number.isFinite(Number(t.deathTime)) ? Number(t.deathTime) : (entry.deathTime ?? null))) changed = true;
if (entry.alive !== nextAlive) changed = true;
if (!sameList(entry.parents, nextParents)) changed = true;
if (!sameList(entry.children, nextChildren)) changed = true;
@ -375,10 +402,22 @@ function inheritedChampionPersonality(seed = "", parents = [], opts = {}) {
entry.isTarinaiChampion = nextChampion;
entry.hasPaired = !!t.hasPaired;
entry.deathReason = t.deathReason || entry.deathReason || "";
entry.deathTime = t.deathTime != null && Number.isFinite(Number(t.deathTime)) ? Number(t.deathTime) : (entry.deathTime ?? null);
entry.parents = nextParents;
entry.parentNames = [...(t.parentNames || entry.parentNames || [])];
entry.children = nextChildren;
}
if (t.dead && this.family[familyKey]) {
const archived = this.family[familyKey];
archived.geneticSummary = this.compactFamilyGeneticSummary(archived.genetics);
delete archived.genetics;
delete archived.personality;
delete archived.lifeSpan;
delete archived.growth;
delete archived.adultScale;
delete archived.totalFightWins;
delete archived.totalFightLosses;
}
if (changed) {
this.familyVersion = (this.familyVersion || 0) + 1;
this.markFamilyTreeDirty("family-record");
@ -393,11 +432,12 @@ function inheritedChampionPersonality(seed = "", parents = [], opts = {}) {
if (!this.family) this.family = {};
if (this.familyCleanVersion === (this.familyVersion || 0)) return false;
const family = this.family;
const liveById = new Map((this.tarinai || []).map(t => [this.tarinaiFamilyKey(t), t]).filter(([id]) => id));
const liveById = new Map((this.tarinai || []).filter(t => t && !t.dead).map(t => [this.tarinaiFamilyKey(t), t]).filter(([id]) => id));
let changed = false;
const uniqueIds = (list, allow) => Array.from(new Set((list || []).filter(id => id && (!allow || allow.has(id)))));
const sameList = (a, b) => a.length === b.length && a.every((id, i) => id === b[i]);
for (const t of this.tarinai || []) {
if (!t || t.dead) continue;
const familyKey = this.tarinaiFamilyKey(t);
if (!familyKey || (!this.family[familyKey] && !(t.hasPaired || (t.parents || []).length || (t.children || []).length))) continue;
const before = this.familyVersion || 0;
@ -428,15 +468,21 @@ function inheritedChampionPersonality(seed = "", parents = [], opts = {}) {
children,
birthTime: live?.birthTime ?? n.birthTime ?? 0,
scale: Number.isFinite(live?.scale) ? live.scale : (Number.isFinite(n.scale) ? n.scale : (Number.isFinite(live?.adultScale) ? live.adultScale : (Number.isFinite(n.adultScale) ? n.adultScale : 0.28))),
adultScale: Number.isFinite(live?.adultScale) ? live.adultScale : (Number.isFinite(n.adultScale) ? n.adultScale : (Number.isFinite(live?.scale) ? live.scale : (Number.isFinite(n.scale) ? n.scale : 0.28))),
growth: Number.isFinite(live?.growth) ? live.growth : (Number.isFinite(n.growth) ? n.growth : 1),
genetics: live && typeof normalizeGenetics === "function" ? normalizeGenetics(live.genetics, live.familyKey || live.id || "") : (typeof normalizeGenetics === "function" ? normalizeGenetics(n.genetics, id) : { ...(n.genetics || {}) }),
lifeSpan: Number(live?.lifeSpan || n.lifeSpan || 0) || 0,
totalFightWins: Number(live?.totalFightWins ?? n.totalFightWins ?? 0) || 0,
totalFightLosses: Number(live?.totalFightLosses ?? n.totalFightLosses ?? 0) || 0,
isTarinaiChampion: !!(live?.isTarinaiChampion || n.isTarinaiChampion),
...(live ? {
adultScale: Number.isFinite(live.adultScale) ? live.adultScale : (Number.isFinite(live.scale) ? live.scale : 0.28),
growth: Number.isFinite(live.growth) ? live.growth : 1,
genetics: typeof normalizeGenetics === "function" ? normalizeGenetics(live.genetics, live.familyKey || live.id || "") : { ...(live.genetics || {}) },
lifeSpan: Number(live.lifeSpan || 0) || 0,
totalFightWins: Number(live.totalFightWins || 0) || 0,
totalFightLosses: Number(live.totalFightLosses || 0) || 0,
isTarinaiChampion: !!live.isTarinaiChampion,
} : {
geneticSummary: Array.isArray(n.geneticSummary) ? n.geneticSummary.slice(0, 4) : this.compactFamilyGeneticSummary(n.genetics),
isTarinaiChampion: !!n.isTarinaiChampion,
}),
alive: live ? !live.dead : n.alive !== false,
deathReason: live?.deathReason || n.deathReason || "",
deathTime: live?.deathTime != null && Number.isFinite(Number(live.deathTime)) ? Number(live.deathTime) : (n.deathTime != null && Number.isFinite(Number(n.deathTime)) ? Number(n.deathTime) : null),
};
for (const key of Object.keys(next)) {
const a = family[id]?.[key];
@ -549,15 +595,25 @@ function inheritedChampionPersonality(seed = "", parents = [], opts = {}) {
},
markDead(t, reason) {
this.clearLiveReferencesTo(t);
if (!this.family) this.family = {};
const finalReason = t?.normalizeDeathReason ? t.normalizeDeathReason(reason) : reason;
if (t && (t.deathTime == null || !Number.isFinite(Number(t.deathTime)))) t.deathTime = Number(this.time || 0) || 0;
global.TarinaiMemorialBellSystem?.captureDeath?.(this, t, finalReason);
this.clearLiveReferencesTo(t);
const familyKey = this.tarinaiFamilyKey(t);
if (familyKey && this.family[familyKey]) {
if (this.family[familyKey].alive !== false || this.family[familyKey].deathReason !== finalReason) {
if (this.family[familyKey].alive !== false || this.family[familyKey].deathReason !== finalReason || this.family[familyKey].deathTime == null) {
this.family[familyKey].alive = false;
this.family[familyKey].deathReason = finalReason;
this.family[familyKey].deathTime = t?.deathTime != null && Number.isFinite(Number(t.deathTime)) ? Number(t.deathTime) : (Number(this.time || 0) || 0);
this.family[familyKey].geneticSummary = this.compactFamilyGeneticSummary(this.family[familyKey].genetics);
delete this.family[familyKey].genetics;
delete this.family[familyKey].personality;
delete this.family[familyKey].lifeSpan;
delete this.family[familyKey].growth;
delete this.family[familyKey].adultScale;
delete this.family[familyKey].totalFightWins;
delete this.family[familyKey].totalFightLosses;
this.familyVersion = (this.familyVersion || 0) + 1;
this.markFamilyTreeDirty("death");
}