"use strict"; // Layer: entity-runtime/tarinai/update-system // Owns local family/friend/grass/nest comfort environment effects. (function (global) { function parasolFootprint(item = null) { if (!item || item.dead || item.type !== "rain_shelter") return null; const r = Math.max(30, Number(item.r || 50) || 50); // The pole foot is the visual and physical center of the shade. return { centerX: Number(item.x || 0), centerY: Number(item.y || 0) + r * 0.84, radiusX: r * 1.58, radiusY: r * 0.68, }; } function parasolRange(item = null) { return parasolFootprint(item)?.radiusX || 0; } function parasolContains(item = null, x = 0, y = 0) { const footprint = parasolFootprint(item); if (!footprint) return false; const nx = (Number(x || 0) - footprint.centerX) / Math.max(1, footprint.radiusX); const ny = (Number(y || 0) - footprint.centerY) / Math.max(1, footprint.radiusY); return nx * nx + ny * ny <= 1; } function parasolAt(worldRef, x, y) { if (!worldRef) return null; const candidates = typeof worldRef.itemsOfType === "function" ? worldRef.itemsOfType("rain_shelter") : (worldRef.nearbyItems?.(x, y, 240) || []); let best = null; let bestRatio = Infinity; for (const item of candidates || []) { const footprint = parasolFootprint(item); if (!footprint) continue; const nx = (Number(x || 0) - footprint.centerX) / Math.max(1, footprint.radiusX); const ny = (Number(y || 0) - footprint.centerY) / Math.max(1, footprint.radiusY); const ratio = Math.sqrt(nx * nx + ny * ny); if (ratio <= 1 && ratio < bestRatio) { best = item; bestRatio = ratio; } } return best; } function enclosedShelterFor(worldRef, target = null) { if (!worldRef || !target?.insideNestBoxId) return null; const holder = worldRef.nestBoxById?.(target.insideNestBoxId) || (worldRef.items || []).find(item => item && !item.dead && item.id === target.insideNestBoxId) || null; return holder && (holder.type === "nest_box" || holder.type === "pipe") ? holder : null; } function shelterRange(item = null) { return parasolRange(item); } function shelterAt(worldRef, x, y, target = null) { return parasolAt(worldRef, x, y) || enclosedShelterFor(worldRef, target); } function isSheltered(worldRef, x, y, target = null) { return Boolean(shelterAt(worldRef, x, y, target)); } function blocksWeatherAt(worldRef, x, y, target = null) { return isSheltered(worldRef, x, y, target); } function updateBody(dt) { const parent = this.parentToFollow ? this.parentToFollow() : null; if (parent && dist(this, parent) < 96) { applyNeedRelief(this, { social: -dt * 4, safety: -dt * 2 }); this.loneliness = clamp(this.loneliness - dt * 0.32, 0, 110); this.adjustPersonality("sociability", dt * 0.004 * this.sociabilityFriendScale(), "after staying near family."); } const friend = this.bestFriendLive ? this.bestFriendLive(FRIEND_AFFINITY_THRESHOLD, 92) : null; const friendCount = this.currentFriendCount ? this.currentFriendCount(FRIEND_AFFINITY_THRESHOLD, true) : 0; if (friend) { applyNeedRelief(this, { social: -dt * 3, fulfill: -dt * 1 }); this.adjustPersonality("sociability", dt * 0.004 * this.sociabilityFriendScale(), "after staying near friends."); } else if (friendCount <= 0 && this.loneliness > 72 && this.age > 8) { this.adjustPersonality("sociability", -dt * 0.0018, "after having no friends."); } let grassComfort = 0; for (const it of this.world.nearbyItems(this.x, this.y, 105)) { if (it.type !== "grass" || it.dead) continue; const d = distXY(this.x, this.y, it.x, it.y); const lush = clamp((it.growth ?? it.amount / 120) * (it.health ?? 1), 0, 1.2); grassComfort += clamp(1 - d / 105, 0, 1) * lush; } let nestComfort = 0; for (const it of this.world.nearbyItems(this.x, this.y, 120)) { if (!it || it.dead || it.type !== "nest_box") continue; nestComfort += clamp(1 - distXY(this.x, this.y, it.x, it.y) / 120, 0, 1); } let rainShelterComfort = 0; const rainShelter = parasolAt(this.world, this.x, this.y); if (rainShelter && (this.world?.weather || "") === "light_rain") { const range = parasolRange(rainShelter); rainShelterComfort = clamp(1 - distXY(this.x, this.y, rainShelter.x, rainShelter.y) / range, 0.25, 1); } if (nestComfort > 0) applyNeedRelief(this, { sleep: -dt * Math.min(4, nestComfort * 3), safety: -dt * Math.min(5, nestComfort * 4) }); if (rainShelterComfort > 0) { applyNeedRelief(this, { safety: -dt * Math.min(6, rainShelterComfort * 5), health: -dt * Math.min(3, rainShelterComfort * 2), fulfill: -dt * Math.min(2, rainShelterComfort * 1.2) }); if (this.state === "panic") this.fearTimer = Math.max(0, this.fearTimer - dt * Math.min(0.35, rainShelterComfort * 0.08)); } if (grassComfort > 0) { applyNeedRelief(this, { fulfill: -dt * Math.min(2, grassComfort * 0.5), safety: -dt * Math.min(1.5, grassComfort * 0.4) }); if (this.state === "panic") this.fearTimer = Math.max(0, this.fearTimer - dt * Math.min(0.22, grassComfort * 0.06)); if (this.shouldApplyPersonalityBehavior("neuroticism", 1)) this.affection += dt * Math.min(0.06, grassComfort * 0.012); } } function updateOne(tarinai, dt) { if (!tarinai || tarinai.dead) return false; updateBody.call(tarinai, dt); return true; } const parasolSystem = Object.freeze({ parasolFootprint, parasolRange, parasolContains, parasolAt, enclosedShelterFor, shelterRange, shelterAt, isSheltered, blocksWeatherAt }); global.TarinaiParasolSystem = parasolSystem; global.TarinaiRainShelterSystem = parasolSystem; global.TarinaiLocalEnvironmentSystem = Object.freeze({ updateOne, }); })(typeof window !== "undefined" ? window : globalThis);