aa
This commit is contained in:
parent
f941200504
commit
ad2cc76429
34 changed files with 616 additions and 365 deletions
|
|
@ -11,6 +11,93 @@ function mateTargetFor(t, world, maxDist = 520) {
|
|||
return world.nearestOther?.(t, maxDist, ok) || null;
|
||||
}
|
||||
|
||||
function socialFightMateWeights(t, other, world, options = {}) {
|
||||
if (!t || !other || !world || t === other || t.dead || other.dead) return { fightWeight: 0, mateWeight: 0, fightEligible: false, mateEligible: false, fightProbability: 0 };
|
||||
const d = Number.isFinite(Number(options.distance)) ? Number(options.distance) : dist(t, other);
|
||||
const fightMochi = (t.fightMochiTimer || 0) > 0.04 || (other.fightMochiTimer || 0) > 0.04 || t.hasFightMochiEffect?.() || other.hasFightMochiEffect?.();
|
||||
const loveMochi = (t.loveMochiTimer || 0) > 0.04 || (other.loveMochiTimer || 0) > 0.04 || t.hasLoveMochiEffect?.() || other.hasLoveMochiEffect?.();
|
||||
const aProf = t.personalityProfile?.() || {};
|
||||
const bProf = other.personalityProfile?.() || {};
|
||||
const aCur = t.currentPersonality || {};
|
||||
const bCur = other.currentPersonality || {};
|
||||
const avg = (a, b, fallback = 0) => ((Number.isFinite(Number(a)) ? Number(a) : fallback) + (Number.isFinite(Number(b)) ? Number(b) : fallback)) * 0.5;
|
||||
const aggression = avg(aCur.aggression ?? aProf.fight, bCur.aggression ?? bProf.fight, 0);
|
||||
const openness = avg(aCur.openness ?? aProf.play, bCur.openness ?? bProf.play, 0);
|
||||
const sociability = avg(aCur.sociability ?? aProf.social, bCur.sociability ?? bProf.social, 0);
|
||||
const fearTrait = avg(aProf.fear, bProf.fear, 1);
|
||||
const relationA = typeof t.relationTo === "function" ? t.relationTo(other.id) : {};
|
||||
const relationB = typeof other.relationTo === "function" ? other.relationTo(t.id) : {};
|
||||
const affinity = Math.max(Number(relationA.affinity || 0) || 0, Number(relationB.affinity || 0) || 0);
|
||||
const fear = Math.max(Number(relationA.fear || 0) || 0, Number(relationB.fear || 0) || 0);
|
||||
const postBirthPeace = (t.postBirthPeaceTimer || 0) > 0.04 || (other.postBirthPeaceTimer || 0) > 0.04;
|
||||
const postConflictPeace = (t.postConflictPeaceTimer || 0) > 0.04 || (other.postConflictPeaceTimer || 0) > 0.04;
|
||||
const familyFightBlocked = world.areParentChild?.(t, other) || world.areCoParents?.(t, other);
|
||||
const fightEligible = Boolean(world.canFightPair?.(t, other))
|
||||
&& !familyFightBlocked
|
||||
&& !postBirthPeace
|
||||
&& !postConflictPeace
|
||||
&& (t.fightCooldown || 0) <= 0.04
|
||||
&& (other.fightCooldown || 0) <= 0.04
|
||||
&& (t.fightTimer || 0) <= 0.04
|
||||
&& (other.fightTimer || 0) <= 0.04
|
||||
&& (t.birthRitualTimer || 0) <= 0.04
|
||||
&& (other.birthRitualTimer || 0) <= 0.04
|
||||
&& (world.fightPairCooldownRemaining?.(t, other) || 0) <= 0.04
|
||||
&& d <= Math.max(Number(options.fightRange || 58) || 58, (t.radius || 20) + (other.radius || 20) + 18);
|
||||
const sameStatus = !!t.isZunchiSlave === !!other.isZunchiSlave;
|
||||
const mateWindow = Number.isFinite(Number(options.mateTimerWindow)) ? Number(options.mateTimerWindow) : 10;
|
||||
const requireBirthReady = options.requireBirthReady !== false;
|
||||
const timerOk = requireBirthReady ? ((t.reproductionTimer || 0) <= 0.04 && (other.reproductionTimer || 0) <= 0.04) : ((t.reproductionTimer || 0) <= mateWindow && (other.reproductionTimer || 0) <= mateWindow);
|
||||
const mateDistanceOk = d <= Math.max(loveMochi ? 54 : 44, (t.radius || 20) + (other.radius || 20) + 14);
|
||||
const enough = loveMochi ? ((t.hunger || 0) < 90 && (other.hunger || 0) < 92 && (t.energy || 0) > 18 && (other.energy || 0) > 18) : ((t.hunger || 0) < 55 && (other.hunger || 0) < 60 && (t.energy || 0) > 44 && (other.energy || 0) > 44);
|
||||
const lonelyBond = loveMochi ? ((t.loneliness || 0) > 4 || (other.loneliness || 0) > 4 || (t.affection || 0) > 0 || (other.affection || 0) > 0) : ((t.loneliness || 0) > 24 || (other.loneliness || 0) > 24);
|
||||
const mateEligible = sameStatus
|
||||
&& timerOk
|
||||
&& mateDistanceOk
|
||||
&& !world.areParentChild?.(t, other)
|
||||
&& !t.sleepDisease && !other.sleepDisease && !t.fightDisease && !other.fightDisease
|
||||
&& (t.fightTimer || 0) <= 0.04 && (other.fightTimer || 0) <= 0.04
|
||||
&& (t.birthRitualTimer || 0) <= 0.04 && (other.birthRitualTimer || 0) <= 0.04
|
||||
&& (!requireBirthReady || (enough && lonelyBond));
|
||||
let fightWeight = fightEligible ? 4 : 0;
|
||||
let mateWeight = mateEligible ? 6 : 0;
|
||||
if (fightWeight > 0) {
|
||||
const stressPressure = clamp((((t.stress || 0) + (other.stress || 0)) * 0.5 - 36) / 72, 0, 1.8);
|
||||
const safetyPressure = clamp((((t.needs?.safety || 0) + (other.needs?.safety || 0)) * 0.5 - 36) / 72, 0, 1.4);
|
||||
fightWeight *= clamp(1 + aggression * 0.55, 0.25, 2.8);
|
||||
fightWeight *= 1 + stressPressure * 0.58 + safetyPressure * 0.24;
|
||||
if (t.type === "angry" || other.type === "angry") fightWeight *= 1.42;
|
||||
if (t.isZunchiSlave || other.isZunchiSlave) fightWeight *= 1.22;
|
||||
if (fightMochi) fightWeight *= 3.4;
|
||||
if (loveMochi) fightWeight *= 0.58;
|
||||
fightWeight *= clamp(1 - affinity / 150, 0.34, 1.08);
|
||||
fightWeight *= clamp(1 - fear / 170, fightMochi ? 0.72 : 0.42, 1.05);
|
||||
fightWeight *= clamp(1.14 - Math.max(0, fearTrait - 1) * 0.16, 0.62, 1.22);
|
||||
if (options.mode === "conflict") fightWeight *= 1.35;
|
||||
}
|
||||
if (mateWeight > 0) {
|
||||
const stressPressure = clamp((((t.stress || 0) + (other.stress || 0)) * 0.5 - 42) / 84, 0, 1.4);
|
||||
mateWeight *= clamp(1 + openness * 0.34 + sociability * 0.22, 0.34, 2.4);
|
||||
mateWeight *= clamp(1 - stressPressure * 0.22, 0.55, 1.0);
|
||||
mateWeight *= 1 + Math.min(0.70, affinity / 140);
|
||||
if (loveMochi) mateWeight *= 3.15;
|
||||
if (fightMochi) mateWeight *= 0.52;
|
||||
if (options.mode === "mate") mateWeight *= 1.42;
|
||||
}
|
||||
const total = Math.max(0, fightWeight) + Math.max(0, mateWeight);
|
||||
return { fightWeight, mateWeight, fightEligible, mateEligible, fightProbability: total > 0 ? fightWeight / total : 0, totalWeight: total };
|
||||
}
|
||||
|
||||
function pickSocialFightMate(world, key, weights, ...seeds) {
|
||||
const fightWeight = Math.max(0, Number(weights?.fightWeight || 0) || 0);
|
||||
const mateWeight = Math.max(0, Number(weights?.mateWeight || 0) || 0);
|
||||
if (fightWeight <= 0 && mateWeight <= 0) return "none";
|
||||
if (fightWeight > 0 && mateWeight <= 0) return "fight";
|
||||
if (mateWeight > 0 && fightWeight <= 0) return "mate";
|
||||
const bucket = Math.floor((Number(world?.time || 0) || 0) / 3);
|
||||
return deterministicChance(world, key || "social-fight-mate-choice", fightWeight / (fightWeight + mateWeight), ...seeds, bucket) ? "fight" : "mate";
|
||||
}
|
||||
|
||||
function triggerFriendContact(t, other, world, dt = 0) {
|
||||
if (!t || !other || !world) return false;
|
||||
const amount = 0.30 + Math.max(0, Number(dt) || 0) * 0.85;
|
||||
|
|
@ -34,22 +121,14 @@ function updateSocialContactBehavior(t, world, dt, mode = "bond") {
|
|||
const near = dist(t, other) <= Math.max(48, (t.radius || 20) + (other.radius || 20) + 12);
|
||||
if (!near) return moveToOrUse(t, other, mode === "family" ? "follow_parent" : "seek_friend", mode === "family" ? "\u5bb6\u65cf\u306b\u8fd1\u3065\u3044\u3066\u3044\u308b" : "\u4ef2\u9593\u306b\u8fd1\u3065\u3044\u3066\u3044\u308b");
|
||||
|
||||
const conflict = conflictTargetFor(t, world, 30);
|
||||
const forcedConflict = (t.fightMochiTimer || 0) > 0.04 || (other.fightMochiTimer || 0) > 0.04;
|
||||
const socialPartsForContact = t.socialReasonParts || {};
|
||||
const conflictDominant = Number(socialPartsForContact.conflict || 0) >= Math.max(Number(socialPartsForContact.bond || 0), Number(socialPartsForContact.mate || 0), Number(socialPartsForContact.family || 0)) + 8;
|
||||
if (forcedConflict || (conflict?.target === other && conflict.score >= 34 && conflictDominant)) {
|
||||
const weights = socialFightMateWeights(t, other, world, { mode, requireBirthReady: true, distance: dist(t, other) });
|
||||
const choice = pickSocialFightMate(world, "social-contact-weighted-fight-mate", weights, t, other, mode);
|
||||
if (choice === "fight") {
|
||||
t.conflictTargetId = other.id;
|
||||
other.conflictTargetId = t.id;
|
||||
return updateFightBehavior(t, world, dt) || true;
|
||||
}
|
||||
const mate = mateTargetFor(t, world, 150);
|
||||
const parts = t.socialReasonParts || {};
|
||||
const selfBehaviorId = getTarinaiBehaviorId(t);
|
||||
const otherBehaviorId = getTarinaiBehaviorId(other);
|
||||
const mateIntent = mode === "mate" || selfBehaviorId === "approach_mate" || otherBehaviorId === "approach_mate" || (t.loveMochiTimer || 0) > 0.04 || (other.loveMochiTimer || 0) > 0.04;
|
||||
const mateDominant = Number(parts.mate || 0) >= Math.max(Number(parts.bond || 0), Number(parts.conflict || 0)) + 10;
|
||||
if (mate === other && (t.reproductionTimer || 0) <= 8 && (other.reproductionTimer || 0) <= 10 && (mateIntent || mateDominant)) {
|
||||
if (world.startBirthRitual?.(t, other)) return "finished";
|
||||
}
|
||||
if (choice === "mate" && world.startBirthRitual?.(t, other)) return "finished";
|
||||
return triggerFriendContact(t, other, world, dt);
|
||||
}
|
||||
|
||||
|
|
@ -58,9 +137,17 @@ function updateMateBehavior(t, world, dt) {
|
|||
if (!other) return false;
|
||||
const d = dist(t, other);
|
||||
if (d <= Math.max(50, (t.radius || 20) + (other.radius || 20) + 14)) {
|
||||
const otherWantsMate = (getTarinaiBehaviorId(other)) === "approach_mate" || (other.loveMochiTimer || 0) > 0.04 || (other.reproductionTimer || 0) <= 4;
|
||||
const selfForced = isCurrentBehaviorForced(t) || (t.loveMochiTimer || 0) > 0.04;
|
||||
if ((otherWantsMate || selfForced || (t.reproductionTimer || 0) <= 4) && world.startBirthRitual?.(t, other)) return "finished";
|
||||
const forced = isCurrentBehaviorForced(t);
|
||||
if (!forced) {
|
||||
const weights = socialFightMateWeights(t, other, world, { mode: "mate", requireBirthReady: true, distance: d });
|
||||
const choice = pickSocialFightMate(world, "mate-contact-weighted-fight-mate", weights, t, other);
|
||||
if (choice === "fight") {
|
||||
t.conflictTargetId = other.id;
|
||||
other.conflictTargetId = t.id;
|
||||
return updateFightBehavior(t, world, dt) || true;
|
||||
}
|
||||
if (choice === "mate" && world.startBirthRitual?.(t, other)) return "finished";
|
||||
} else if (world.startBirthRitual?.(t, other)) return "finished";
|
||||
moveToOrUse(t, other, "seek_friend", "\u7e41\u6b96\u3067\u304d\u308b\u76f8\u624b\u306b\u8fd1\u3065\u3044\u3066\u3044\u308b");
|
||||
if (typeof applyNeedShock === "function") applyNeedShock(other, { social: dt * 10 }, t);
|
||||
return true;
|
||||
|
|
@ -87,13 +174,17 @@ function updateFightBehavior(t, world, dt) {
|
|||
t.setActionState?.("fight", { target: rival || t.target, reason: rival?.name ? `${rival.name}\u3068\u55a7\u5629\u3057\u3066\u3044\u308b` : "\u55a7\u5629\u3057\u3066\u3044\u308b", sleeping: false });
|
||||
return true;
|
||||
}
|
||||
const candidate = conflictTargetFor(t, world, (t?.fightMochiTimer || 0) > 0.04 || isCurrentBehaviorForced(t) ? 24 : 42);
|
||||
const candidate = conflictTargetFor(t, world, (t?.fightMochiTimer || 0) > 0.04 || isCurrentBehaviorForced(t) ? 24 : 14);
|
||||
const current = currentBehaviorTarget(t);
|
||||
const rival = isLiveTarinaiEntity(candidate?.target) ? candidate.target : (isLiveTarinaiEntity(current) ? current : nearestForcedFightTarget(t, world, isCurrentBehaviorForced(t) ? 260 : 220));
|
||||
if (!isLiveTarinaiEntity(rival)) return false;
|
||||
t.conflictTargetId = rival.id;
|
||||
if (dist(t, rival) > Math.max(42, (t.radius || 20) + (rival.radius || 20) + 12)) return moveToOrUse(t, rival, "seek_enemy", "\u6c17\u306b\u5165\u3089\u306a\u3044\u76f8\u624b\u306b\u5411\u304b\u3063\u3066\u3044\u308b");
|
||||
const nearDistance = dist(t, rival);
|
||||
if (nearDistance > Math.max(42, (t.radius || 20) + (rival.radius || 20) + 12)) return moveToOrUse(t, rival, "seek_enemy", "\u6c17\u306b\u5165\u3089\u306a\u3044\u76f8\u624b\u306b\u5411\u304b\u3063\u3066\u3044\u308b");
|
||||
if (candidate?.forced || isCurrentBehaviorForced(t) || (t.fightMochiTimer || 0) > 0.04 || (rival.fightMochiTimer || 0) > 0.04) return !!world.startForcedFight?.(t, rival);
|
||||
const weights = socialFightMateWeights(t, rival, world, { mode: "conflict", requireBirthReady: true, distance: nearDistance });
|
||||
const choice = pickSocialFightMate(world, "fight-contact-weighted-fight-mate", weights, t, rival);
|
||||
if (choice === "mate" && world.startBirthRitual?.(t, rival)) return true;
|
||||
world.startConflict?.(t, rival);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue