"use strict"; (function (global) { const World = global.World; if (!World) throw new Error("World is not available for mixin: world_update.js"); Object.defineProperties(World.prototype, Object.getOwnPropertyDescriptors({ hourOfDay(timeValue = this.time || 0) { return ((((timeValue || 0) / Math.max(1, CONFIG.dayLength || 120)) * 24) % 24 + 24) % 24; }, colonyMoodDefinition(id = "relaxed") { const canonicalId = String(id || "relaxed"); const defs = { devastation: { id: "devastation", label: "\u58ca\u6ec5\u5bf8\u524d", description: "\u30b3\u30ed\u30cb\u30fc\u306e\u5b58\u7d9a\u304c\u304b\u306a\u308a\u5371\u3046\u3044\u72b6\u614b\u3067\u3059\u3002", effects: { personality: {} } }, crisis: { id: "crisis", label: "\u5371\u6a5f", description: "\u3051\u304c\u3084\u6d88\u8017\u304c\u76ee\u7acb\u3061\u3001\u7acb\u3066\u76f4\u3057\u304c\u5fc5\u8981\u306a\u72b6\u614b\u3067\u3059\u3002", effects: { personality: {} } }, confusion: { id: "confusion", label: "\u6df7\u4e71", description: "\u9a5a\u304d\u3084\u30d1\u30cb\u30c3\u30af\u3067\u843d\u3061\u7740\u304d\u304c\u5931\u308f\u308c\u3066\u3044\u307e\u3059\u3002", effects: { personality: {} } }, fearful: { id: "fearful", label: "\u6050\u6016", description: "\u5b89\u5168\u3092\u6c42\u3081\u308b\u500b\u4f53\u304c\u591a\u304f\u3001\u5371\u967a\u3092\u907f\u3051\u3088\u3046\u3068\u3057\u3066\u3044\u307e\u3059\u3002", effects: { personality: {} } }, weakened: { id: "weakened", label: "\u8870\u5f31", description: "\u4f53\u529b\u3084\u5143\u6c17\u304c\u843d\u3061\u3001\u52d5\u304d\u304c\u5f31\u304f\u306a\u3063\u3066\u3044\u307e\u3059\u3002", effects: { personality: {} } }, disease_spread: { id: "disease_spread", label: "\u75c5\u6c17\u8513\u5ef6", description: "\u75c5\u6c17\u306e\u500b\u4f53\u304c\u76ee\u7acb\u3061\u3001\u8abf\u5b50\u3092\u5d29\u3057\u3084\u3059\u3044\u72b6\u614b\u3067\u3059\u3002", effects: { personality: {} } }, overcrowded: { id: "overcrowded", label: "\u904e\u5bc6", description: "\u500b\u4f53\u540c\u58eb\u306e\u8ddd\u96e2\u304c\u8fd1\u304f\u3001\u3076\u3064\u304b\u308a\u3084\u3059\u3044\u72b6\u614b\u3067\u3059\u3002", effects: { personality: {} } }, isolated: { id: "isolated", label: "\u5b64\u7acb", description: "\u4ef2\u9593\u304c\u5c11\u306a\u3044\u3001\u307e\u305f\u306f\u8ddd\u96e2\u304c\u96e2\u308c\u3066\u5bc2\u3057\u3044\u72b6\u614b\u3067\u3059\u3002", effects: { personality: {} } }, breeding: { id: "breeding", label: "\u7e41\u6b96", description: "\u7e41\u6b96\u3084\u89aa\u5bc6\u306a\u884c\u52d5\u304c\u8d77\u3053\u308a\u3084\u3059\u3044\u72b6\u614b\u3067\u3059\u3002", effects: { personality: {} } }, happy: { id: "happy", label: "\u5e78\u798f", description: "\u30b9\u30c8\u30ec\u30b9\u304c\u5c11\u306a\u304f\u3001\u4f59\u88d5\u306e\u3042\u308b\u72b6\u614b\u3067\u3059\u3002", effects: { personality: {} } }, relaxed: { id: "relaxed", label: "\u5b89\u5b9a", description: "\u5927\u304d\u306a\u554f\u984c\u304c\u5c11\u306a\u304f\u3001\u843d\u3061\u7740\u3044\u305f\u72b6\u614b\u3067\u3059\u3002", effects: { personality: {} } }, }; return defs[canonicalId] || defs.relaxed; }, evaluateColonyMood(force = false) { return global.TarinaiColonySituationSystem.evaluate(this, force); }, observeHighlightKind(t) { const s = this.selected; if (!s || !t || t === s || t.dead || this.tool !== "observe") return ""; const tFamilyKey = this.tarinaiFamilyKey(t); if ((s.parents || []).includes(tFamilyKey)) return "parent"; if ((s.children || []).includes(tFamilyKey)) return "child"; const friendThreshold = typeof FRIEND_AFFINITY_THRESHOLD === "number" ? FRIEND_AFFINITY_THRESHOLD : 14; const rel = s.relationTo ? s.relationTo(t.id) : null; const reverse = t.relationTo ? t.relationTo(s.id) : null; if ((rel && (rel.affinity || 0) >= friendThreshold) || (reverse && (reverse.affinity || 0) >= friendThreshold)) return "friend"; if ((rel && (rel.fear || 0) >= 8) || (reverse && (reverse.fear || 0) >= 8)) return "enemy"; return ""; }, resolveOwnedBedSleepConflicts() { const liveCount = (this.tarinai || []).reduce((count, t) => count + (t && !t.dead ? 1 : 0), 0); if (liveCount < 2) return 0; const friendThreshold = typeof FRIEND_AFFINITY_THRESHOLD === "number" ? FRIEND_AFFINITY_THRESHOLD : 14; const isFriendPair = (a, b) => { const relAB = a?.relationTo ? a.relationTo(b?.id) : null; const relBA = b?.relationTo ? b.relationTo(a?.id) : null; return Boolean((relAB && (relAB.affinity || 0) >= friendThreshold) || (relBA && (relBA.affinity || 0) >= friendThreshold)); }; const beds = typeof this.itemsOfType === "function" ? this.itemsOfType("grass_bed") : (this.items || []).filter(it => it && !it.dead && it.type === "grass_bed"); let conflicts = 0; for (const bed of beds || []) { if (!bed || bed.dead || bed.type !== "grass_bed") continue; const sleepReach = Math.max((bed.r || 18) * 1.55, 32); const intrudeReach = Math.max((bed.r || 18) * 2.2, 54); const candidates = typeof this.nearbyTarinai === "function" ? this.nearbyTarinai(bed.x, bed.y, intrudeReach + 16, true) : (this.tarinai || []); const sleepers = []; const intruders = []; for (const t of candidates || []) { if (!t || t.dead || t.target !== bed) continue; const d = distXY(t.x, t.y, bed.x, bed.y); if ((t.sleeping || t.state === "sleep") && d <= sleepReach) sleepers.push(t); if ((t.state === "seek_bed" || t.state === "sleep") && d <= intrudeReach) intruders.push(t); } if (!sleepers.length || !intruders.length) continue; for (const intruder of intruders) { const defender = sleepers.find(other => other && other !== intruder && !this.areParentChild?.(intruder, other) && !isFriendPair(intruder, other)); if (!defender) continue; if (this.startForcedFight?.(defender, intruder)) conflicts += 1; } } return conflicts; }, blastZunchiFrom(x, y, radius, strength = 1, options = {}) { let moved = 0; const limit = Math.max(1, Number(options.limit || 180) || 180); const effectLimit = Math.max(0, Number(options.effectLimit || 12) || 12); for (const it of this.nearbyItems(x, y, radius + 60)) { if (moved >= limit) break; if (!it || it.dead || it.type !== "zunchi") continue; let dx = it.x - x; let dy = it.y - y; let d = Math.hypot(dx, dy) || 1; if (d > radius) continue; if (d < 0.001) { const a = rand(0, Math.PI * 2); dx = Math.cos(a); dy = Math.sin(a); d = 1; } const p = clamp(1 - d / radius, 0, 1); it.vx = (it.vx || 0) + dx / d * (130 + p * 430) * strength + rand(-45, 45); it.vy = (it.vy || 0) + dy / d * (130 + p * 430) * strength + rand(-45, 45); it.amount = Math.max(10, (it.amount || 80) - p * 8); it.stage = "fresh"; moved += 1; if (moved < effectLimit) this.effects.push(new Effect("zunchi_miasma", it.x, it.y - 4, { vx: (it.vx || 0) * 0.15, vy: (it.vy || 0) * 0.15 - 18, size: rand(9, 18), life: rand(0.36, 0.62), color: "rgba(58,102,38,0.48)" })); } if (moved) this.drawListDirty = true; return moved; }, update(dt) { const simulation = window.TarinaiSimulation; if (!simulation?.update) throw new Error("TarinaiSimulation is not available"); return simulation.update(this, dt); }, sanitizeOutOfBounds() { const pad = Math.max(28, CONFIG.worldPadding || 30); const safeW = Math.max(pad * 2 + 1, Number(this.w || 1000) || 1000); const safeH = Math.max(pad * 2 + 1, Number(this.h || 720) || 720); let changedItems = false; let changedTarinai = false; let changedAnts = false; const repairPoint = (obj, radius = 12) => { if (!obj) return "missing"; if (!Number.isFinite(obj.x)) obj.x = safeW * 0.5; if (!Number.isFinite(obj.y)) obj.y = safeH * 0.5; const minX = pad - radius; const maxX = safeW - pad + radius; const minY = pad - radius; const maxY = safeH - pad + radius; if (obj.x >= minX && obj.x <= maxX && obj.y >= minY && obj.y <= maxY) return "ok"; obj.x = clamp(obj.x, pad, safeW - pad); obj.y = clamp(obj.y, pad, safeH - pad); if (Number.isFinite(obj.vx)) obj.vx *= -0.18; if (Number.isFinite(obj.vy)) obj.vy *= -0.18; return "moved"; }; const forEachMaintenanceSlice = (list, cursorKey, fullLimit, fn) => { const arr = Array.isArray(list) ? list : []; const len = arr.length; if (!len) { this[cursorKey] = 0; return; } const limit = Math.max(1, Number(fullLimit || len) || len); if (len <= limit) { for (let i = 0; i < len; i++) fn(arr[i], i); this[cursorKey] = 0; return; } let cursor = Math.max(0, Math.floor(Number(this[cursorKey] || 0) || 0)) % len; for (let n = 0; n < limit; n++) { const idx = (cursor + n) % len; fn(arr[idx], idx); } this[cursorKey] = (cursor + limit) % len; }; forEachMaintenanceSlice(this.items, "_sanitizeItemsCursor", 900, (it) => { const result = repairPoint(it, it?.r || 12); if (result === "moved") changedItems = true; }); forEachMaintenanceSlice(this.effects, "_sanitizeEffectsCursor", 900, (ef) => { repairPoint(ef, ef?.size || 12); }); const repairTarinai = (t) => { t.x = clamp(Number.isFinite(t.x) ? t.x : safeW * 0.5, pad, safeW - pad); t.y = clamp(Number.isFinite(t.y) ? t.y : safeH * 0.5, pad, safeH - pad); t.vx = 0; t.vy = 0; t.entryTimer = 0; }; forEachMaintenanceSlice(this.tarinai, "_sanitizeTarinaiCursor", 360, (t) => { if (!t || t.dead) return; if (t.state === "sunbath" || (t.sunbathTimer || 0) > 0.04) { if (!Number.isFinite(t.x)) t.x = Number.isFinite(t.sunbathAnchorX) ? t.sunbathAnchorX : pad; if (!Number.isFinite(t.y)) t.y = Number.isFinite(t.sunbathAnchorY) ? t.sunbathAnchorY : pad; t.sunbathAnchorX = clamp(t.x, pad, safeW - pad); t.sunbathAnchorY = clamp(t.y, pad, safeH - pad); } const result = repairPoint(t, t.radius || 22); if (result === "missing" || result === "moved") { repairTarinai(t); changedTarinai = true; } if (t.state === "sunbath" || (t.sunbathTimer || 0) > 0.04) { t.sunbathAnchorX = clamp(Number.isFinite(t.x) ? t.x : (Number.isFinite(t.sunbathAnchorX) ? t.sunbathAnchorX : pad), pad, safeW - pad); t.sunbathAnchorY = clamp(Number.isFinite(t.y) ? t.y : (Number.isFinite(t.sunbathAnchorY) ? t.sunbathAnchorY : pad), pad, safeH - pad); t.x = t.sunbathAnchorX; t.y = t.sunbathAnchorY; t._lastSunbathDrawX = t.x; t._lastSunbathDrawY = t.y; t._lastValidX = t.x; t._lastValidY = t.y; } }); forEachMaintenanceSlice(this.ants, "_sanitizeAntsCursor", 900, (ant) => { if (!ant || ant.dead) return; const result = repairPoint(ant, ant.kind === "queen" ? 18 : 10); if (result === "missing") return; if (result === "moved") { ant.vx = 0; ant.vy = 0; if (ant.state === "drag") { ant.state = "return"; ant.targetId = ""; ant.targetToken = 0; ant.targetRef = null; } changedAnts = true; } }); if (changedItems) { this.markSpatialDirty?.("sanitize-out-of-bounds"); this.ensureSpatial?.("sanitize-out-of-bounds"); } else if (changedTarinai || changedAnts) { this.markSpatialDirty?.("sanitize-out-of-bounds-mobile"); } }, updateWeather(dt) { if (window.TarinaiWeatherSystem.enforceFixedWeather(this)) return; this.nextWeatherChange -= dt; if (this.nextWeatherChange > 0) return; window.TarinaiWeatherSystem.applyNextWeather(this); } })); })(typeof window !== "undefined" ? window : globalThis);