y
This commit is contained in:
parent
61718e2981
commit
f657b6a4a4
94 changed files with 3970 additions and 1241 deletions
|
|
@ -110,6 +110,104 @@ function pickSocialFightMate(world, key, weights, ...seeds) {
|
|||
return deterministicChance(world, key || "social-fight-mate-choice", fightWeight / (fightWeight + mateWeight), ...seeds, bucket) ? "fight" : "mate";
|
||||
}
|
||||
|
||||
|
||||
function socialCrowdThresholds(t) {
|
||||
const cur = t?.currentPersonality || {};
|
||||
const social = clamp(Number(cur.sociability || 0) || 0, -1, 1);
|
||||
const open = clamp(Number(cur.openness || 0) || 0, -1, 1);
|
||||
const social01 = (social + 1) * 0.5;
|
||||
const open01 = (open + 1) * 0.5;
|
||||
const preferred = 4 + social01 * 6;
|
||||
const lower = Math.floor(clamp(preferred - (3 + open01 * 3), 0, 7));
|
||||
const upper = Math.ceil(clamp(preferred + 5 + open01 * 7, Math.max(8, lower + 4), 24));
|
||||
return { lower, upper };
|
||||
}
|
||||
|
||||
function findSocialCrowdEscapeTarget(t, world = t?.world) {
|
||||
if (!t || !world) return null;
|
||||
const source = typeof world.nearbyTarinai === "function"
|
||||
? (world.nearbyTarinai(t.x, t.y, 118, true) || [])
|
||||
: (world.tarinai || []);
|
||||
let cx = 0, cy = 0, count = 0, inspected = 0;
|
||||
for (const o of source) {
|
||||
if (!o || o === t || o.dead) continue;
|
||||
const dx = o.x - t.x, dy = o.y - t.y;
|
||||
const d2 = dx * dx + dy * dy;
|
||||
if (d2 > 118 * 118 || d2 < 0.0001) continue;
|
||||
const w = 1 / Math.max(24, Math.sqrt(d2));
|
||||
cx += dx * w;
|
||||
cy += dy * w;
|
||||
count++;
|
||||
if (++inspected >= 28) break;
|
||||
}
|
||||
const seed = typeof stableUnit === "function" ? stableUnit(t.familyKey || t.id || "crowd", "crowd-escape-angle") : 0.5;
|
||||
let base = Math.atan2(-cy, -cx);
|
||||
if (!count || !Number.isFinite(base)) base = seed * Math.PI * 2;
|
||||
const pad = Math.max(30, (CONFIG.worldPadding || 30) + (t.radius || 22));
|
||||
const radius = Math.max(14, (t.radius || 22) * 0.72);
|
||||
for (const escapeDistance of [150, 215]) {
|
||||
for (const off of [0, 0.42, -0.42, 0.84, -0.84]) {
|
||||
const a = base + off + (seed - 0.5) * 0.18;
|
||||
const x = clamp(t.x + Math.cos(a) * escapeDistance, pad, world.w - pad);
|
||||
const y = clamp(t.y + Math.sin(a) * escapeDistance, pad, world.h - pad);
|
||||
if (world.pointBlockedByObstacle?.(x, y, radius, { maxChecks: 12, directionalOneWay: true })) continue;
|
||||
return { x, y, dead: false, detour: true, crowdEscape: true };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const SOCIAL_CROWD_ESCAPE_REASON = "\u4ed6\u306e\u305f\u308a\u306a\u3044\u304c\u591a\u3059\u304e\u308b\u306e\u3067\u3001\u5c11\u3057\u96e2\u308c\u3088\u3046\u3068\u3057\u3066\u3044\u308b\u3002";
|
||||
|
||||
function updateCrowdEscapeBehavior(t, world) {
|
||||
if (!t || !world) return false;
|
||||
const sample = t.socialCrowdSample || null;
|
||||
const thresholds = socialCrowdThresholds(t);
|
||||
const now = Number(world.time || 0) || 0;
|
||||
const age = sample ? now - (Number(sample.at || 0) || 0) : Infinity;
|
||||
const count = Math.max(0, Number(sample?.count || 0) || 0);
|
||||
const upper = Math.max(thresholds.lower + 4, Number(sample?.upper ?? thresholds.upper) || thresholds.upper);
|
||||
if (age > 3.5 || count < Math.max(thresholds.lower + 2, upper - 1)) return "finished";
|
||||
|
||||
let target = t.target?.crowdEscape ? t.target : null;
|
||||
if (!target || target.dead || distXY(t.x, t.y, target.x, target.y) <= Math.max(28, (t.radius || 20) + 8)) {
|
||||
target = findSocialCrowdEscapeTarget(t, world);
|
||||
if (!target) return "finished";
|
||||
t.target = target;
|
||||
t.setActionState?.("wander", {
|
||||
target,
|
||||
reason: SOCIAL_CROWD_ESCAPE_REASON,
|
||||
wake: true,
|
||||
sleeping: false,
|
||||
actionId: "crowd_escape",
|
||||
actionLabel: "\u6df7\u96d1\u304b\u3089\u5c11\u3057\u96e2\u308c\u3088\u3046\u3068\u3057\u3066\u3044\u308b",
|
||||
need: "social",
|
||||
subNeed: "crowd",
|
||||
});
|
||||
} else {
|
||||
t.setActionState?.("wander", {
|
||||
target,
|
||||
reason: SOCIAL_CROWD_ESCAPE_REASON,
|
||||
sleeping: false,
|
||||
actionId: "crowd_escape",
|
||||
actionLabel: "\u6df7\u96d1\u304b\u3089\u5c11\u3057\u96e2\u308c\u3088\u3046\u3068\u3057\u3066\u3044\u308b",
|
||||
need: "social",
|
||||
subNeed: "crowd",
|
||||
});
|
||||
}
|
||||
if (typeof setBehaviorText === "function") setBehaviorText(t, {
|
||||
need: "social",
|
||||
subNeed: "crowd",
|
||||
actionId: "crowd_escape",
|
||||
actionLabel: "\u6df7\u96d1\u304b\u3089\u5c11\u3057\u96e2\u308c\u3088\u3046\u3068\u3057\u3066\u3044\u308b",
|
||||
reasonText: SOCIAL_CROWD_ESCAPE_REASON,
|
||||
target,
|
||||
phase: "perform",
|
||||
source: "behavior",
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -205,6 +303,7 @@ function updatePanicBehavior(t, world, dt, needs) {
|
|||
const breakerDanger = activePanicBreaker(t, world, 260);
|
||||
const nearbyDanger = findNearbyDanger(world, t, 260) || null;
|
||||
const realDanger = breakerDanger || nearbyDanger || null;
|
||||
if (realDanger && realDanger !== t) t.lastPanicThreat = realDanger;
|
||||
const currentTarget = currentBehaviorTarget(t);
|
||||
const safety = Number(needs?.safety || 0) || 0;
|
||||
const now = Number(world?.time || 0) || 0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue