This commit is contained in:
33333-33333 2026-07-18 13:06:02 +09:00
commit f657b6a4a4
94 changed files with 3970 additions and 1241 deletions

View file

@ -4,13 +4,54 @@
const Tarinai = global.Tarinai;
if (!Tarinai) throw new Error("Tarinai is not available for mixin: tarinai_identity_social.js");
function relationIdSetFor(tarinai) {
const ids = new Set(Object.keys(tarinai?.relationships || {}));
for (const other of tarinai?.world?.tarinai || []) {
if (!other || other === tarinai || !other.id) continue;
if (other.relationships?.[tarinai.id]) ids.add(other.id);
const relationPeerCache = new WeakMap();
function relationPeerEntry(tarinai) {
const relationships = tarinai?.relationships || {};
let entry = relationPeerCache.get(tarinai);
if (!entry || entry.relationships !== relationships) {
const keys = Object.keys(relationships);
entry = { relationships, ids: new Set(keys), ownCount: keys.length, incomingBuilt: false };
relationPeerCache.set(tarinai, entry);
}
return ids;
return entry;
}
function dropRelationPeerId(tarinai, id) {
if (!tarinai || !id) return false;
const relationships = tarinai.relationships && typeof tarinai.relationships === "object" ? tarinai.relationships : null;
const entry = relationPeerCache.get(tarinai);
let removed = false;
if (relationships && Object.prototype.hasOwnProperty.call(relationships, id)) {
delete relationships[id];
removed = true;
}
if (entry) {
entry.ids.delete(id);
if (removed) entry.ownCount = Math.max(0, (entry.ownCount || 0) - 1);
}
if (removed) tarinai.relationCache = null;
return removed;
}
function lazyDropDeadRelationPeer(tarinai, id) {
const pending = tarinai?.world?._deadTarinaiIdsPendingCleanup;
if (!(pending instanceof Set) || pending.size === 0 || !pending.has(id)) return false;
if (tarinai.world?.liveTarinaiById?.(id)) return false;
dropRelationPeerId(tarinai, id);
return true;
}
function relationIdSetFor(tarinai) {
const entry = relationPeerEntry(tarinai);
if (!entry.incomingBuilt) {
for (const other of tarinai?.world?.tarinai || []) {
if (!other || other.dead || other === tarinai || !other.id) continue;
if (other.relationships?.[tarinai.id]) entry.ids.add(other.id);
}
entry.incomingBuilt = true;
}
return entry.ids;
}
Object.defineProperties(Tarinai.prototype, Object.getOwnPropertyDescriptors({
@ -169,6 +210,7 @@
this.surpriseTimer = Math.max(this.surpriseTimer || 0, 0.65);
this.focusPulseTimer = Math.max(this.focusPulseTimer || 0, 1.0);
if (boosted.length) this.recordChangeCause?.("\u305F\u308A\u306A\u3044\u738B\u8005", boosted.join("\u30FB"), { value: +1 });
this.world?.syncTarinaiCountEntry?.(this);
this.world?.recordFamily?.(this);
return true;
},
@ -188,11 +230,30 @@
relationTo(id) {
if (!id) return relationDefaults();
const pendingDead = this.world?._deadTarinaiIdsPendingCleanup;
if (pendingDead instanceof Set && pendingDead.size > 0 && pendingDead.has(id) && !this.world?.liveTarinaiById?.(id)) {
dropRelationPeerId(this, id);
return relationDefaults();
}
if (!this.relationships) this.relationships = {};
if (!this.relationships[id]) this.relationships[id] = relationDefaults();
if (!this.relationships[id]) {
this.relationships[id] = relationDefaults();
const entry = relationPeerEntry(this);
entry.ids.add(id);
entry.ownCount = (entry.ownCount || 0) + 1;
const other = this.world?.liveTarinaiById?.(id) || null;
if (other) relationPeerEntry(other).ids.add(this.id);
}
return this.relationships[id];
},
pruneDeadRelationshipRefs(deadIds) {
if (!(deadIds instanceof Set) || deadIds.size === 0) return 0;
let removed = 0;
for (const id of deadIds) if (dropRelationPeerId(this, id)) removed += 1;
return removed;
},
adjustRelation(other, affinityDelta = 0, fearDelta = 0, event = "") {
if (!other || !other.id || other === this) return;
const rel = this.relationTo(other.id);
@ -217,9 +278,12 @@
let bestId = null;
let bestScore = kind === "fear" ? 5 : FRIEND_AFFINITY_THRESHOLD;
for (const id of relationIdSetFor(this)) {
const rel = this.relationships?.[id] || relationDefaults();
if (liveOnly && (!this.world?.liveTarinaiById?.(id) || id === this.id)) continue;
const other = this.world?.liveTarinaiById?.(id) || null;
if (liveOnly && (!other || id === this.id)) {
if (!other) lazyDropDeadRelationPeer(this, id);
continue;
}
const rel = this.relationships?.[id] || relationDefaults();
const reverse = kind === "fear" ? null : (other?.relationships?.[this.id] || null);
const score = kind === "fear" ? (rel.fear || 0) : Math.max(rel.affinity || 0, reverse?.affinity || 0);
if (score > bestScore) { bestId = id; bestScore = score; }
@ -239,11 +303,14 @@
if (this.relationCache?.friendCountKey === cacheKey && now < (this.relationCache.friendCountUntil || 0)) return this.relationCache.friendCount;
let count = 0;
for (const id of relationIdSetFor(this)) {
const rel = this.relationships?.[id] || relationDefaults();
const other = this.world?.liveTarinaiById?.(id) || null;
if (liveOnly && (!other || id === this.id)) {
if (!other) lazyDropDeadRelationPeer(this, id);
continue;
}
const rel = this.relationships?.[id] || relationDefaults();
const reverse = other?.relationships?.[this.id] || null;
if (Math.max(rel.affinity || 0, reverse?.affinity || 0) <= minScore) continue;
if (liveOnly && (!other || id === this.id)) continue;
count += 1;
}
this.relationCache = { ...(this.relationCache || {}), friendCountKey: cacheKey, friendCount: count, friendCountUntil: now + 1.4 };
@ -264,12 +331,15 @@
}
let best = null, bestScore = minScore;
for (const id of relationIdSetFor(this)) {
const rel = this.relationships?.[id] || relationDefaults();
const t = this.world.liveTarinaiById?.(id) || null;
if (!t) {
lazyDropDeadRelationPeer(this, id);
continue;
}
const rel = this.relationships?.[id] || relationDefaults();
const reverse = t?.relationships?.[this.id] || null;
const score = Math.max(rel.affinity || 0, reverse?.affinity || 0);
if (score <= bestScore) continue;
if (!t) continue;
if (Number.isFinite(maxDist) && dist(this, t) > maxDist) continue;
best = t;
bestScore = score;
@ -286,7 +356,10 @@
if (this.world.time - when > maxAge) continue;
if (!(event.includes("fight") || event.includes("\u55a7\u5629") || (rel.fightsWon || 0) || (rel.fightsLost || 0))) continue;
const t = this.world.liveTarinaiById?.(id) || null;
if (!t) continue;
if (!t) {
lazyDropDeadRelationPeer(this, id);
continue;
}
if (Number.isFinite(maxDist) && dist(this, t) > maxDist) continue;
if (when > bestT) { best = t; bestT = when; }
}