This commit is contained in:
33333-33333 2026-07-05 18:01:36 +09:00
commit 8f9f5b5100
108 changed files with 2139 additions and 1500 deletions

View file

@ -35,6 +35,80 @@
const groundForResetPreset = resetPresets.groundForResetPreset || (() => "soil");
const resetPresetLabel = resetPresets.label || (() => "\u30C7\u30D5\u30A9\u30EB\u30C8");
function pointerLargeTarinaiTooltipCandidatesLocal(worldRef, now = worldRef?.time || 0) {
const cache = worldRef._pointerTooltipLargeTarinaiCache;
if (cache && worldRef._pointerTooltipLargeTarinaiSerial === worldRef.liveIdSerial && now < (worldRef._pointerTooltipLargeTarinaiUntil || 0)) return cache;
const out = [];
for (const t of worldRef.tarinai || []) {
if (!t || t.dead) continue;
if ((Number(t.radius) || 20) > 64) out.push(t);
}
worldRef._pointerTooltipLargeTarinaiCache = out;
worldRef._pointerTooltipLargeTarinaiSerial = worldRef.liveIdSerial;
worldRef._pointerTooltipLargeTarinaiUntil = now + 0.35;
return out;
}
function pointerTarinaiTooltipCandidatesLocal(worldRef, p) {
const all = Array.isArray(worldRef.tarinai) ? worldRef.tarinai : [];
if (!p?.inside || !all.length) return all;
if (all.length < 72 || typeof worldRef.nearbyTarinai !== "function") return all;
const local = worldRef.nearbyTarinai(p.x, p.y, 128, false) || [];
const large = pointerLargeTarinaiTooltipCandidatesLocal(worldRef, worldRef.time || 0) || [];
if (!large.length) return local;
const stamp = (worldRef._pointerTooltipCandidateStamp = (worldRef._pointerTooltipCandidateStamp || 0) + 1);
for (const t of local) if (t) t._pointerTooltipCandidateStamp = stamp;
for (const t of large) {
if (!t || t.dead || t._pointerTooltipCandidateStamp === stamp) continue;
t._pointerTooltipCandidateStamp = stamp;
local.push(t);
}
return local;
}
function pointerTarinaiTooltipInfoLocal(worldRef) {
const p = worldRef.pointer;
if (!p?.inside || !Array.isArray(worldRef.tarinai)) return null;
let best = null, bestD = Infinity;
for (const t of pointerTarinaiTooltipCandidatesLocal(worldRef, p) || worldRef.tarinai) {
if (!t || t.dead) continue;
if (worldRef.isTarinaiHiddenInNestBox?.(t)) continue;
const d = distXY(p.x, p.y, t.x, t.y);
const limit = Math.max(46, (t.radius || 20) * 1.35);
if (d <= limit && d < bestD) { best = t; bestD = d; }
}
if (!best) return null;
const nameLine = String(best.name || "たりない");
const behavior = (typeof behaviorText === "function" ? behaviorText(best) : "")
|| window.TEXT_CATALOG?.stateLabel?.(best.state, best)
|| best.thought
|| "様子を見ている";
const maxEnergy = typeof tarinaiMaxEnergy === "function" ? tarinaiMaxEnergy(best) : (Number(best.maxEnergy) || 100);
const statusLine = `体力: ${Math.round(best.energy || 0)}/${Math.round(maxEnergy)} 空腹: ${Math.round(best.hunger || 0)} ストレス: ${Math.round(best.stress || 0)}`;
const tempLine = worldRef.temperatureTooltipLineFor(best, "体感気温") || "体感気温: --℃";
return { target: best, lines: [nameLine, String(behavior), statusLine, tempLine], kind: "tarinai" };
}
function pointerOwnedBedInfoLocal(worldRef) {
const p = worldRef.pointer;
if (!p?.inside) return null;
let best = null, bestD = Infinity;
for (const it of worldRef.nearbyItems(p.x, p.y, 140)) {
if (!it || it.dead || it.type !== "grass_bed") continue;
const d = distXY(p.x, p.y, it.x, it.y);
if (d < bestD && d <= Math.max(60, (it.r || 24) * 2.0)) { best = it; bestD = d; }
}
if (!best) return null;
const owner = worldRef.liveTarinaiById?.(best.ownerId) || null;
return { bed: best, owner };
}
function ownedBedTooltipLinesLocal(worldRef, bed) {
if (!bed || bed.dead || bed.type !== "grass_bed") return [];
const owner = worldRef.liveTarinaiById?.(bed.ownerId) || null;
return ["かんたんベッド", `持ち主: ${owner?.name || "不明"}`, worldRef.temperatureTooltipLineFor?.(bed, "周辺気温")].filter(Boolean);
}
Object.defineProperties(World.prototype, Object.getOwnPropertyDescriptors({
reset(seedPopulation = null, fieldType = this.fieldType || "garden", presetId = "default") {
const field = FIELD_TYPES?.[fieldType] || FIELD_TYPES.garden;
@ -433,12 +507,25 @@
}
}
const t = new Tarinai(this, opts);
const isSeededSpawn = !Number.isFinite(Number(opts.age))
&& opts.birthTime == null
&& !opts.dead
&& !(Array.isArray(opts.parents) && opts.parents.length)
&& !(Array.isArray(opts.seedParents) && opts.seedParents.length)
&& (Number(opts.generation ?? 1) <= 1);
if (isSeededSpawn) {
const span = Math.max(1, Number(t.lifeSpan || 1) || 1);
const ratio = typeof deterministicRange === "function"
? deterministicRange(this, "initial-life-spent-ratio", 0, 0.30, t.familyKey || t.birthSeed || t.id || this.birthSerial || Math.random())
: Math.random() * 0.30;
t.age = Math.max(Number(t.age || 0) || 0, span * ratio);
t.birthTime = (this.time || 0) - t.age;
}
this.assignTarinaiLiveId(t);
this.tarinai.push(t);
this.drawListDirty = true;
if (t.hasPaired || (t.parents || []).length || (t.children || []).length) this.recordFamily(t);
this.maxGeneration = Math.max(this.maxGeneration, t.generation);
this.emit?.("tarinai:born", { tarinai: t, source: opts.parents?.length ? "family" : "spawn" });
return t;
},
@ -451,7 +538,7 @@
if (!p?.inside) return null;
let best = null, bestD = Infinity;
for (const it of this.nearbyItems(p.x, p.y, 140)) {
if (!it || it.dead || !(typeof globalThis.isNestContainerType === "function" ? globalThis.isNestContainerType(it.type) : (it.type === "nest_box" || it.type === "pipe"))) continue;
if (!it || it.dead || !globalThis.TarinaiToolRuntime?.isNestContainerItem?.(it)) continue;
const d = distXY(p.x, p.y, it.x, it.y);
if (d < bestD && d <= Math.max(80, it.r * 2.1)) { best = it; bestD = d; }
}
@ -462,7 +549,7 @@
},
nestBoxTooltipLines(box) {
if (!box || box.dead || !(typeof globalThis.isNestContainerType === "function" ? globalThis.isNestContainerType(box.type) : (box.type === "nest_box" || box.type === "pipe"))) return [];
if (!box || box.dead || !globalThis.TarinaiToolRuntime?.isNestContainerItem?.(box)) return [];
const isPipe = box.type === "pipe";
let occupants = [];
if (isPipe) {
@ -500,65 +587,25 @@
temperatureTooltipLineFor(target, label = "体感気温") {
if (!target || !this.temperatureAt) return "";
const temp = target.type === "pipe" && this.pipeInteriorTemperatureFor
? this.pipeInteriorTemperatureFor(target)
: this.temperatureAt(target.x, target.y);
const isActor = Array.isArray(this.tarinai) && this.tarinai.includes(target);
const temp = isActor
? (this.feltTemperatureFor?.(target) ?? this.temperatureAt(target.x, target.y, target))
: (target.type === "pipe" && this.pipeInteriorTemperatureFor
? this.pipeInteriorTemperatureFor(target)
: this.temperatureAt(target.x, target.y));
if (!Number.isFinite(Number(temp))) return "";
const status = this.temperatureStatusFor?.(temp) || null;
const status = this.temperatureStatusFor?.(temp, isActor ? target : null) || null;
const suffix = status?.label ? `(${status.label})` : "";
return `${label}: ${Number(temp).toFixed(1)}℃${suffix}`;
},
pointerTarinaiTooltipInfo() {
const p = this.pointer;
if (!p?.inside || !Array.isArray(this.tarinai)) return null;
let best = null, bestD = Infinity;
for (const t of this.tarinai) {
if (!t || t.dead) continue;
if (this.isTarinaiHiddenInNestBox?.(t)) continue;
const d = distXY(p.x, p.y, t.x, t.y);
const limit = Math.max(46, (t.radius || 20) * 1.35);
if (d <= limit && d < bestD) { best = t; bestD = d; }
}
if (!best) return null;
const nameLine = String(best.name || "たりない");
const behavior = (typeof behaviorText === "function" ? behaviorText(best) : "")
|| window.TEXT_CATALOG?.stateLabel?.(best.state, best)
|| best.thought
|| "様子を見ている";
const maxEnergy = typeof tarinaiMaxEnergy === "function" ? tarinaiMaxEnergy(best) : (Number(best.maxEnergy) || 100);
const statusLine = `体力: ${Math.round(best.energy || 0)}/${Math.round(maxEnergy)} 空腹: ${Math.round(best.hunger || 0)} ストレス: ${Math.round(best.stress || 0)}`;
const tempLine = this.temperatureTooltipLineFor(best, "体感気温") || "体感気温: --℃";
return { target: best, lines: [nameLine, String(behavior), statusLine, tempLine], kind: "tarinai" };
},
pointerOwnedBedInfo() {
const p = this.pointer;
if (!p?.inside) return null;
let best = null, bestD = Infinity;
for (const it of this.nearbyItems(p.x, p.y, 140)) {
if (!it || it.dead || it.type !== "grass_bed") continue;
const d = distXY(p.x, p.y, it.x, it.y);
if (d < bestD && d <= Math.max(60, (it.r || 24) * 2.0)) { best = it; bestD = d; }
}
if (!best) return null;
const owner = this.liveTarinaiById?.(best.ownerId) || null;
return { bed: best, owner };
},
ownedBedTooltipLines(bed) {
if (!bed || bed.dead || bed.type !== "grass_bed") return [];
const owner = this.liveTarinaiById?.(bed.ownerId) || null;
return ["\u304b\u3093\u305f\u3093\u30d9\u30c3\u30c9", `\u6301\u3061\u4e3b: ${owner?.name || "\u4e0d\u660e"}`, this.temperatureTooltipLineFor?.(bed, "\u5468\u8FBA\u6C17\u6E29")].filter(Boolean);
},
pointerItemTooltipInfo() {
const tarinaiInfo = this.pointerTarinaiTooltipInfo?.();
const tarinaiInfo = pointerTarinaiTooltipInfoLocal(this);
if (tarinaiInfo?.target) return tarinaiInfo;
const nest = this.pointerNestBoxInfo?.();
if (nest?.box) return { target: nest.box, lines: this.nestBoxTooltipLines(nest.box), kind: nest.box?.type === "pipe" ? "pipe" : "nest_box" };
const bedInfo = this.pointerOwnedBedInfo?.();
if (bedInfo?.bed) return { target: bedInfo.bed, lines: this.ownedBedTooltipLines(bedInfo.bed), kind: "grass_bed" };
const bedInfo = pointerOwnedBedInfoLocal(this);
if (bedInfo?.bed) return { target: bedInfo.bed, lines: ownedBedTooltipLinesLocal(this, bedInfo.bed), kind: "grass_bed" };
return null;
},