"use strict"; // Layer: entity-runtime/items/support // Owns shared lifecycle support helpers used by dynamic item systems and // placement/tool commands. The runtime imports these via // globals instead of carrying helper implementations inline. (function (global) { const DUPLICATOR_STORABLE_TYPES = new Set([ "food", "sweet", "love_mochi", "fight_mochi", "sleep_drug", "first_aid", "sedative", "laxative", "protein", "niteropu", "ammo", "mystery_drug", "mercury", "giant_drug", "dwarf_drug", "zunda_juice", "water", "pushpin", "oshibyo" ]); function duplicatorLoadTypeForItem(item) { if (!item || item.dead || item.type === "duplicator") return ""; const type = String(item.type || ""); if (!type || type === "grass") return ""; if ((isPinType(type)) && item.pinState === "lodged") return ""; if (type === "slave_chain" || type === "golden_crown") return ""; // Explicit list first. \u3053\u308c\u3067\u300c\u305a\u3093\u3060\u9905\u300d\u300c\u3078\u3053\u9905\u300d\u300c\u3051\u3093\u304b\u9905\u300d\u304c // serving-food \u5224\u5b9a\u3084 medicine role \u306e\u63fa\u308c\u306b\u5de6\u53f3\u3055\u308c\u305a\u8907\u88fd\u6a5f\u3078\u5165\u308b\u3002 if (DUPLICATOR_STORABLE_TYPES.has(type)) return type; if (isServingFoodType(type)) return type; if (isParamEffectItemType(type)) return type; const foodDef = global.TarinaiItemRegistry.food?.get?.(type); if (foodDef && (Number(foodDef.nutrition || 0) > 0 || foodDef.effectId)) return type; if (typeof roleMatches === "function" && roleMatches(item, "medicine")) return type; return ""; } function duplicatorLoadPoint(duplicator) { const r = duplicator?.r || 34; return { x: (duplicator?.x || 0) + r * 0.62, y: (duplicator?.y || 0) + r * 0.24 }; } function syncDuplicatorRoles(item) { if (!item || item.type !== "duplicator") return; const type = String(item.storedFoodType || ""); const pinLoaded = Boolean(type && isPinType(type)); item.roles.food = Boolean(type && !pinLoaded); item.roles.medicine = Boolean(type && !pinLoaded && (type === "sweet" || type === "water" || type === "zunda_juice" || type === "sleep_drug" || (isParamEffectItemType(type)))); item.roles.drink = Boolean(!pinLoaded && (type === "water" || type === "zunda_juice")); item.roles.danger = pinLoaded; } function duplicatorSetStoredType(duplicator, storedType = "", worldRef = null, options = {}) { if (!duplicator || duplicator.type !== "duplicator") return false; if (options.direct !== true && options.force !== true) return false; const type = String(storedType || ""); if (!type) return false; const probe = { type, amount: 1, foodServingsRemaining: 1, dead: false }; const loadType = duplicatorLoadTypeForItem(probe); if (!loadType) return false; const changed = duplicator.storedFoodType !== loadType; if (!changed && duplicator.storedFoodType && !options.force) return true; const def = itemDefinition(loadType); const newLabel = def?.label || toolLabel(loadType); duplicator.storedFoodType = loadType; duplicator.storedFoodLabel = newLabel; syncDuplicatorRoles(duplicator); duplicator.duplicatorPinFlash = isPinType(loadType) ? 0.28 : (duplicator.duplicatorPinFlash || 0); const consume = options.consumeItem; if (consume && typeof consume === "object") { consume.amount = 0; consume.foodServingsRemaining = 0; } worldRef?.markItemBucketsDirty?.(changed ? "duplicator-direct-reloaded" : "duplicator-direct-loaded"); worldRef?.markSpatialDirty?.(changed ? "duplicator-direct-reloaded" : "duplicator-direct-loaded"); worldRef?.markTerrainDirty?.(changed ? "duplicator-direct-reloaded" : "duplicator-direct-loaded"); if (options.log !== false) worldRef?.log?.(`\u8907\u88fd\u6a5f\u306b${duplicator.storedFoodLabel}\u3092\u30bb\u30c3\u30c8\u3057\u305f\u3002`, isPinType(loadType) ? "observe" : "food"); return true; } function duplicatorApplyStoredPin(duplicator, worldRef) { const storedType = String(duplicator?.storedFoodType || ""); if (!duplicator || duplicator.type !== "duplicator" || !isPinType(storedType)) return false; if (globalThis.TarinaiSignalSystem?.powerOverride?.(duplicator) === false) return false; const now = Number(worldRef?.time || 0); if (now < Number(duplicator.nextDuplicatedPinAt || 0)) return false; const dish = duplicatorLoadPoint(duplicator); const reach = Math.max(24, (duplicator.r || 34) * 0.56); const candidates = worldRef?.nearbyTarinai?.(dish.x, dish.y, reach + 72, true) || []; for (const t of candidates) { if (!t || t.dead || worldRef?.isTarinaiHiddenInNestBox?.(t)) continue; if (t.stuckPushpinId || t.currentLodgedPin?.()) continue; const hit = reach + Math.max(10, (t.radius || 18) * 0.78); if (distXY(dish.x, dish.y, t.x, t.y) > hit) continue; const pin = new Item(storedType, dish.x, dish.y); pin.amount = 999; pin.noStickUntil = 0; pin.noStickTargetId = ""; pin.noStickTargetUntil = 0; const added = worldRef?.addItem?.(pin, "duplicator-pin", { terrain: false }) || null; if (!added) return false; const ok = global.TarinaiItemDynamicPinSystem.attach(pin, t, worldRef, distXY(dish.x, dish.y, t.x, t.y)); if (!ok) { pin.amount = 0; continue; } duplicator.usedCount = (duplicator.usedCount || 0) + 1; duplicator.nextDuplicatedPinAt = now + 0.82; duplicator.duplicatorPinFlash = 0.24; worldRef.effects?.push(new Effect("ring", dish.x, dish.y, { size: Math.max(15, (duplicator.r || 34) * 0.62), life: 0.18, color: storedType === "oshibyo" ? "rgba(73,119,205,0.40)" : "rgba(214,72,72,0.42)" })); worldRef.drawListDirty = true; worldRef.markSpatialDirty?.("duplicator-pin-lodged"); return true; } return false; } global.TarinaiItemLifecycleRuntimeSupport = Object.freeze({ duplicatorLoadTypeForItem, duplicatorLoadPoint, syncDuplicatorRoles, duplicatorSetStoredType, duplicatorApplyStoredPin, }); })(typeof window !== "undefined" ? window : globalThis);