This commit is contained in:
33333-33333 2026-06-25 14:51:58 +09:00
commit 434f07cc4e
103 changed files with 8387 additions and 8152 deletions

View file

@ -19,6 +19,11 @@
this.items = [];
this.ants = [];
this.effects = [];
this.itemCounts = {};
this.effectCounts = {};
this.itemTypeBuckets = new Map();
this.itemIdMap = new Map();
this.itemBucketsDirty = false;
this.logs = [];
this.events = window.TarinaiEvents || null;
this.eventCounters = {};
@ -27,6 +32,8 @@
this.drawSortTimer = 0;
this.drawListDirty = true;
this.antUpdatePhase = 0;
this.grassGrowthTimer = 0;
this.nextGrassLimitCheckAt = 0;
this.selected = null;
this.deadCount = 0;
this.liveIdNext = 1;
@ -51,6 +58,7 @@
this.clampCamera();
this.lastPhase = this.phaseName();
this.weather = "sunny";
this.groundType = window.TarinaiGround?.exists?.(this.groundType || "soil") ? (this.groundType || "soil") : "soil";
this.nextWeatherChange = rand(CONFIG.weatherChangeMin, CONFIG.weatherChangeMax);
seedPopulation = seedPopulation ?? field.population ?? CONFIG.initialPopulation;
const grassCount = field.grass ?? 18;
@ -65,11 +73,14 @@
}
for (let i = 0; i < grassCount; i++) {
const grass = new Item("grass", rand(70, this.w - 70), rand(70, this.h - 70));
globalThis.TarinaiGrass?.setStage?.(grass, Math.floor(rand(0, 2.999)), { force: true });
if (this.addItem) this.addItem(grass, "reset-grass");
else this.items.push(grass);
}
this.addItem?.(new Item("stone", this.w * 0.68, this.h * 0.32), "reset-stone") || this.items.push(new Item("stone", this.w * 0.68, this.h * 0.32));
this.updateItemCounts();
this.enforceGrassLimit?.("reset-grass-limit");
this.updateItemCounts();
this.updateEffectCounts();
this.markTerrainDirty?.("reset");
this.rebuildSpatial(true);
@ -78,7 +89,7 @@
},
tarinaiFamilyKey(t) {
return t?.familyKey || t?.archiveKey || t?.id || "";
return t?.familyKey || t?.id || "";
},
assignTarinaiLiveId(t) {
@ -377,7 +388,6 @@
this.markFamilyTreeDirty("death");
}
}
this.notifyDeathToRelations?.(t, finalReason);
this.familyPrunePending = true;
},
@ -410,8 +420,66 @@
return t;
},
nestBoxOccupants(box, limit = 5) {
if (!box || box.dead || box.type !== "nest_box") return [];
const occupants = (this.tarinai || []).filter(t => t && !t.dead && t.insideNestBoxId === box.id);
return Number.isFinite(limit) ? occupants.slice(0, Math.max(0, limit)) : occupants;
},
pointerNestBoxInfo() {
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 !== "nest_box") 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; }
}
if (!best) return null;
const occupants = this.nestBoxOccupants(best, Infinity);
const capacity = this.nestBoxCapacity(best);
return { box: best, occupants, capacity };
},
nestBoxTooltipLines(box) {
if (!box || box.dead || box.type !== "nest_box") return [];
const occupants = this.nestBoxOccupants(box, Infinity);
const names = occupants.map(t => t?.name).filter(Boolean).slice(0, 5);
const more = occupants.length > names.length ? `、ほか${occupants.length - names.length}匹` : "";
return ["巣箱", names.length ? `入ってる子: ${names.join("、")}${more}` : "入ってる子: なし"];
},
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 ["かんたんベッド", `持ち主: ${owner?.name || "不明"}`];
},
pointerItemTooltipInfo() {
const nest = this.pointerNestBoxInfo?.();
if (nest?.box) return { target: nest.box, lines: this.nestBoxTooltipLines(nest.box), kind: "nest_box" };
const bedInfo = this.pointerOwnedBedInfo?.();
if (bedInfo?.bed) return { target: bedInfo.bed, lines: this.ownedBedTooltipLines(bedInfo.bed), kind: "grass_bed" };
return null;
},
startBirthRitual(a, b) {
if (!a || !b || a.dead || b.dead) return false;
if (!this.tarinai?.includes?.(a) || !this.tarinai?.includes?.(b)) return false;
if (!!a.isZunchiSlave !== !!b.isZunchiSlave) return false;
if (this.areParentChild(a, b)) return false;
if (a.sleepDisease || b.sleepDisease || a.fightDisease || b.fightDisease) return false;
@ -433,17 +501,35 @@
b.birthRitualRole = 1;
a.birthRitualLeader = true;
b.birthRitualLeader = false;
a.setActionState("birth_ritual", { target: b, reason: "\u7e41\u6b96\u306e\u524d\u3076\u308c\u3092\u3057\u3066\u3044\u308b" });
b.setActionState("birth_ritual", { target: a, reason: "\u7e41\u6b96\u306e\u524d\u3076\u308c\u3092\u3057\u3066\u3044\u308b" });
if (typeof setLiveActionText === "function") {
const ritualReason = "\u7e41\u6b96\u306e\u524d\u3076\u308c\u3092\u3057\u3066\u3044\u308b";
const applyRitualAction = (actor, partner) => {
if (!actor) return false;
if (typeof actor.setActionState === "function") {
actor.setActionState("birth_ritual", { target: partner || null, reason: ritualReason, need: "social", subNeed: "mate", actionId: "birth_ritual", phase: "acting" });
return true;
}
actor.state = "birth_ritual";
actor.target = partner || null;
actor.thought = ritualReason;
actor.sleeping = false;
if (typeof setBehaviorText === "function") {
setBehaviorText(actor, { need: "social", subNeed: "mate", actionId: "birth_ritual", actionLabel: ritualReason, reasonText: ritualReason, target: partner || null, phase: "acting", source: "state" });
} else if (typeof patchTarinaiBehavior === "function") {
patchTarinaiBehavior(actor, { actionId: "birth_ritual", phase: "acting", target: partner || null, source: "state", need: "social", subNeed: "mate", reason: ritualReason, label: ritualReason, text: ritualReason });
}
return true;
};
applyRitualAction(a, b);
applyRitualAction(b, a);
if (typeof setBehaviorText === "function") {
const aBehavior = typeof currentTarinaiBehavior === "function" ? currentTarinaiBehavior(a) : a.behavior;
const bBehavior = typeof currentTarinaiBehavior === "function" ? currentTarinaiBehavior(b) : b.behavior;
const aLoveCause = (aBehavior?.source === "love_mochi" || (a.loveMochiTimer || 0) > 0.04) ? "\u3078\u3053\u9905\u306e\u52b9\u679c" : `${b.name || "\u76f8\u624b"}\u304c\u6c17\u306b\u306a\u308b`;
const bLoveCause = (bBehavior?.source === "love_mochi" || (b.loveMochiTimer || 0) > 0.04) ? "\u3078\u3053\u9905\u306e\u52b9\u679c" : `${a.name || "\u76f8\u624b"}\u304c\u6c17\u306b\u306a\u308b`;
const aText = typeof buildReasonText === "function" ? buildReasonText("social", aBehavior?.tiedNeeds || ["social"], { id: "birth_ritual", need: "social", subNeed: "mate", label: `${b.name || "\u76f8\u624b"}\u3068\u7e41\u6b96\u306e\u524d\u3076\u308c\u3092\u3057\u3066\u3044\u308b` }, a, this, { causeText: aLoveCause }) : `${b.name || "\u76f8\u624b"}\u3068\u7e41\u6b96\u306e\u524d\u3076\u308c\u3092\u3057\u3066\u3044\u308b`;
const bText = typeof buildReasonText === "function" ? buildReasonText("social", bBehavior?.tiedNeeds || ["social"], { id: "birth_ritual", need: "social", subNeed: "mate", label: `${a.name || "\u76f8\u624b"}\u3068\u7e41\u6b96\u306e\u524d\u3076\u308c\u3092\u3057\u3066\u3044\u308b` }, b, this, { causeText: bLoveCause }) : `${a.name || "\u76f8\u624b"}\u3068\u7e41\u6b96\u306e\u524d\u3076\u308c\u3092\u3057\u3066\u3044\u308b`;
setLiveActionText(a, { need: "social", subNeed: "mate", actionId: "birth_ritual", actionLabel: `${b.name || "\u76f8\u624b"}\u3068\u7e41\u6b96\u306e\u524d\u3076\u308c\u3092\u3057\u3066\u3044\u308b`, reasonText: aText, causeText: aLoveCause, target: b });
setLiveActionText(b, { need: "social", subNeed: "mate", actionId: "birth_ritual", actionLabel: `${a.name || "\u76f8\u624b"}\u3068\u7e41\u6b96\u306e\u524d\u3076\u308c\u3092\u3057\u3066\u3044\u308b`, reasonText: bText, causeText: bLoveCause, target: a });
setBehaviorText(a, { need: "social", subNeed: "mate", actionId: "birth_ritual", actionLabel: `${b.name || "\u76f8\u624b"}\u3068\u7e41\u6b96\u306e\u524d\u3076\u308c\u3092\u3057\u3066\u3044\u308b`, reasonText: aText, causeText: aLoveCause, target: b });
setBehaviorText(b, { need: "social", subNeed: "mate", actionId: "birth_ritual", actionLabel: `${a.name || "\u76f8\u624b"}\u3068\u7e41\u6b96\u306e\u524d\u3076\u308c\u3092\u3057\u3066\u3044\u308b`, reasonText: bText, causeText: bLoveCause, target: a });
}
a.reproductionTimer = CONFIG.reproductionCooldown + rand(2, 10);
b.reproductionTimer = CONFIG.reproductionCooldown + rand(2, 10);
@ -584,7 +670,6 @@
if (this.family[aKey] && !this.family[aKey].children.includes(childKey)) this.family[aKey].children.push(childKey);
if (this.family[bKey] && !this.family[bKey].children.includes(childKey)) this.family[bKey].children.push(childKey);
this.familyCleanVersion = null;
audio.birth();
this.lastBirthAt = this.time;
if (total <= 1) this.log(`${child.name}\u304c${a.name}\u3068${b.name}\u306e\u5b50\u3068\u3057\u3066\u751f\u307e\u308c\u305f\u3002`, "birth", { participants: [child, a, b] });
return child;
@ -715,7 +800,7 @@
actor.intimidateTimer = Math.max(actor.intimidateTimer, rand(1.05, 1.65));
actor.intimidateTargetId = target.id;
actor.setActionState("intimidate", { target, reason: "\u76f8\u624b\u3092\u5a01\u5687\u3057\u3066\u3044\u308b" });
if (typeof setLiveActionText === "function") setLiveActionText(actor, { need: "social", subNeed: "conflict", actionId: "intimidate_enemy", actionLabel: "\u5a01\u5687\u3057\u3066\u3044\u308b", reasonText: `${target.name || "\u76f8\u624b"}\u3092\u5a01\u5687\u3057\u3066\u3044\u308b`, target, phase: "perform", source: "behavior" });
if (typeof setBehaviorText === "function") setBehaviorText(actor, { need: "social", subNeed: "conflict", actionId: "intimidate_enemy", actionLabel: "\u5a01\u5687\u3057\u3066\u3044\u308b", reasonText: `${target.name || "\u76f8\u624b"}\u3092\u5a01\u5687\u3057\u3066\u3044\u308b`, target, phase: "perform", source: "behavior" });
this.effects.push(new Effect("ring", actor.x, actor.y - actor.radius * 0.65, {
life: 0.42,
size: actor.radius * 0.55,
@ -782,9 +867,9 @@
b.counterAttackFromId = b === receiver ? initiator.id : null;
a.setActionState("fight", { target: b, reason: reasonA });
b.setActionState("fight", { target: a, reason: reasonB });
if (typeof setLiveActionText === "function") {
setLiveActionText(a, { need: "social", subNeed: "conflict", actionId: "fight_rival", actionLabel: a === receiver ? "\u53cd\u6483\u3057\u3066\u3044\u308b" : "\u55a7\u5629\u3057\u3066\u3044\u308b", reasonText: reasonA, causeText: causeFor(a, b), target: b, phase: "perform", source: "behavior" });
setLiveActionText(b, { need: "social", subNeed: "conflict", actionId: "fight_rival", actionLabel: b === receiver ? "\u53cd\u6483\u3057\u3066\u3044\u308b" : "\u55a7\u5629\u3057\u3066\u3044\u308b", reasonText: reasonB, causeText: causeFor(b, a), target: a, phase: "perform", source: "behavior" });
if (typeof setBehaviorText === "function") {
setBehaviorText(a, { need: "social", subNeed: "conflict", actionId: "fight_rival", actionLabel: a === receiver ? "\u53cd\u6483\u3057\u3066\u3044\u308b" : "\u55a7\u5629\u3057\u3066\u3044\u308b", reasonText: reasonA, causeText: causeFor(a, b), target: b, phase: "perform", source: "behavior" });
setBehaviorText(b, { need: "social", subNeed: "conflict", actionId: "fight_rival", actionLabel: b === receiver ? "\u53cd\u6483\u3057\u3066\u3044\u308b" : "\u55a7\u5629\u3057\u3066\u3044\u308b", reasonText: reasonB, causeText: causeFor(b, a), target: a, phase: "perform", source: "behavior" });
}
a.fightTimer = Math.max(a.fightTimer, rand(3.2, 4.8));
b.fightTimer = Math.max(b.fightTimer, rand(3.2, 4.8));
@ -820,7 +905,7 @@
}));
audio.fight();
if (this.time - Math.max(a.lastLog, b.lastLog) > 6) {
this.log(`${a.name}\u3068${b.name}\u304c\u5c0f\u3055\u306a\u55a7\u5629\u3092\u3057\u305f\u3002`, "fight", { participants: [a, b] });
this.log(`${a.name}\u3068${b.name}\u304c\u5c0f\u3055\u306a\u55a7\u5629\u3092\u3057\u305f\u3002`, "fight", { participants: [a, b], sound: false });
a.lastLog = b.lastLog = this.time;
}
return true;