"use strict"; // Unified item definition source: tools, item traits, visuals, food metadata, and item effects. const TOOL_DEFINITIONS = Object.freeze({ observe: { id: "observe", label: "\u89b3\u5bdf", placeable: false, scalable: false, icon: "assets/ui/tool_observe.webp", tooltip: "\u500b\u4f53\u3092\u9078\u629e\u3057\u3001\u72b6\u614b\u30fb\u6027\u683c\u30fb\u75c5\u6c17\u30fb\u304a\u6c17\u306b\u5165\u308a\u3092\u78ba\u8a8d\u3059\u308b\u3002" }, delete: { id: "delete", label: "\u524a\u9664", placeable: false, scalable: false, icon: "assets/ui/tool_delete.webp", tooltip: "\u7f6e\u3044\u305f\u9053\u5177\u3084\u6c5a\u308c\u3092\u6d88\u3059\u3002" }, poke: { id: "poke", label: "\u3064\u3064\u304f", placeable: false, scalable: false, iconText: "\u{1F448}", tooltip: "\u305f\u308a\u306a\u3044\u3092\u3064\u3064\u304f\u3002\u30dc\u30fc\u30eb\u3082\u3064\u3064\u3044\u3066\u8ee2\u304c\u305b\u308b\u3002" }, pinch: { id: "pinch", label: "\u3064\u307e\u3080", placeable: false, scalable: false, iconText: "\u{1F90F}", tooltip: "\u305f\u308a\u306a\u3044\u3084\u7269\u3092\u79fb\u52d5\u3055\u305b\u308b\u3002\u51b7\u51cd\u5eab\u306b\u3082\u904b\u3079\u308b\u3002" }, new: { id: "new", label: "\u8ffd\u52a0", placeable: false, scalable: false, icon: "assets/ui/tool_new.webp", tooltip: "\u753b\u9762\u5916\u304b\u3089\u65b0\u3057\u3044\u305f\u308a\u306a\u3044\u3092\u547c\u3076\u3002" }, water_hose: { id: "water_hose", label: "\u6d17\u6d44", placeable: false, scalable: false, iconText: "\u{1F6BF}", tooltip: "\u30c9\u30e9\u30c3\u30b0\u3067\u6c5a\u308c\u3092\u304d\u308c\u3044\u306b\u3059\u308b\u3002" }, zunchi: { id: "zunchi", itemType: "zunchi", label: "\u305a\u3093\u3061", placeable: true, scalable: true, radius: 14, amount: 240, icon: "assets/ui/tool_zunchi.webp", tooltip: "\u305f\u308a\u306a\u3044\u306e\u305a\u3093\u3061\u3002\u75c5\u6c17\u3084\u8349\u306e\u80a5\u6599\u306b\u95a2\u308f\u308b\u3002" }, sweet: { id: "sweet", itemType: "sweet", label: "\u305a\u3093\u3060\u9905", placeable: true, scalable: true, radius: 13, amount: 62, icon: "assets/ui/tool_sweet.webp", tooltip: "\u305f\u308a\u306a\u3044\u306e\u5927\u597d\u7269\u3002\u4e00\u90e8\u306e\u75c5\u6c17\u3092\u6cbb\u305b\u308b\u3002" }, love_mochi: { id: "love_mochi", itemType: "love_mochi", label: "\u3078\u3053\u9905", placeable: true, scalable: true, radius: 13, amount: 62, icon: "assets/ui/tool_love_mochi.webp", tooltip: "\u98df\u5f8c\u3057\u3070\u3089\u304f\u7e41\u6b96\u884c\u52d5\u304c\u8d77\u304d\u3084\u3059\u304f\u306a\u308b\u3002" }, fight_mochi: { id: "fight_mochi", itemType: "fight_mochi", label: "\u3051\u3093\u304b\u9905", placeable: true, scalable: true, radius: 13, amount: 62, icon: "assets/ui/tool_fight_mochi.webp", tooltip: "\u98df\u5f8c\u3057\u3070\u3089\u304f\u3051\u3093\u304b\u304c\u8d77\u304d\u3084\u3059\u304f\u306a\u308b\u3002" }, water_bowl: { id: "water_bowl", itemType: "water_bowl", label: "\u6c34\u306e\u76bf", placeable: true, scalable: false, radius: 20, amount: 999, icon: "assets/ui/tool_water_bowl.webp", tooltip: "\u885b\u751f\u7684\u306a\u6c34\u304c\u5165\u3063\u305f\u76bf\u3002\u4e00\u90e8\u306e\u75c5\u6c17\u3092\u6cbb\u305b\u308b\u3002" }, sleep_drug: { id: "sleep_drug", itemType: "sleep_drug", label: "\u306d\u3080\u308a\u85ac", placeable: true, scalable: true, radius: 13, amount: 58, icon: "assets/ui/tool_sleep_drug.webp", tooltip: "\u98df\u3079\u308b\u3068\u306d\u3080\u308a\u75c5\u306b\u306a\u308b\u3002\u30c0\u30e1\u30fc\u30b8\u3067\u5b8c\u6cbb\u3059\u308b\u3002" }, laxative: { id: "laxative", itemType: "laxative", label: "\u4e0b\u5264", placeable: true, scalable: true, radius: 13, amount: 58, icon: "assets/ui/tool_laxative.webp", tooltip: "\u305a\u3093\u3061\u3092\u5927\u91cf\u306b\u6392\u6cc4\u3055\u305b\u308b\u3002\u4f55\u304b\u3092\u98df\u3079\u3066\u3082\u3059\u3050\u306b\u51fa\u3066\u884c\u3063\u3066\u3057\u307e\u3046\u3002" }, protein: { id: "protein", itemType: "protein", label: "\u30d7\u30ed\u30c6\u30a4\u30f3", placeable: true, scalable: true, radius: 14, amount: 58, icon: "assets/ui/tool_protein.webp", tooltip: "\u75c5\u6c17\u306b\u5f37\u304f\u3001\u305f\u304f\u307e\u3057\u304f\u3002" }, niteropu: { id: "niteropu", itemType: "niteropu", label: "\u30f3\u30a4\u30c6\u30ed\u30d7", placeable: true, scalable: true, radius: 14, amount: 58, icon: "assets/ui/tool_niteropu.webp", tooltip: "\u75c5\u6c17\u306b\u5f31\u304f\u3001\u305f\u3088\u308a\u306a\u304f\u3002" }, ammo: { id: "ammo", itemType: "ammo", label: "\u5f3e\u85ac", placeable: true, scalable: true, radius: 12, amount: 48, icon: "assets/ui/tool_ammo.webp", tooltip: "\u5168\u3066\u3092\u52a0\u901f\u3055\u305b\u308b\u3002" }, mystery_drug: { id: "mystery_drug", itemType: "mystery_drug", label: "\u602a\u3057\u3044\u85ac", placeable: true, scalable: true, radius: 13, amount: 52, icon: "assets/ui/tool_mystery_drug.webp", tooltip: "\u4f55\u304b\u304c\u8d77\u3053\u308b\u3002" }, mercury: { id: "mercury", itemType: "mercury", label: "\u6c34\u9280", placeable: true, scalable: true, radius: 13, amount: 44, icon: "assets/ui/tool_mercury.webp", tooltip: "\u9577\u751f\u304d\u306e\u79d8\u8a23\u3002" }, giant_drug: { id: "giant_drug", itemType: "giant_drug", label: "\u5de8\u5927\u85ac", placeable: true, scalable: true, radius: 22, amount: 52, icon: "assets/ui/tool_giant_drug.webp", tooltip: "\u4f53\u3092\u5927\u304d\u304f\u5f37\u304f\u3059\u308b\u304c\u3001\u8ca0\u62c5\u304c\u5927\u304d\u3044\u3002" }, dwarf_drug: { id: "dwarf_drug", itemType: "dwarf_drug", label: "\u77ee\u5c0f\u85ac", placeable: true, scalable: true, radius: 8, amount: 52, icon: "assets/ui/tool_dwarf_drug.webp", tooltip: "\u4f53\u3092\u5c0f\u3055\u304f\u5f31\u304f\u3059\u308b\u304c\u3001\u8ca0\u62c5\u3082\u5927\u304d\u3044\u3002" }, zunda_juice: { id: "zunda_juice", itemType: "zunda_juice", label: "\u305a\u3093\u3060\u6c41", placeable: true, scalable: true, radius: 15, amount: 70, icon: "assets/ui/tool_zunda_juice.webp", tooltip: "\u98df\u3079\u308b\u3068\u5168\u3066\u306e\u30a2\u30a4\u30c6\u30e0\u52b9\u679c\u3092\u9664\u53bb\u3059\u308b\u3002" }, grass: { id: "grass", itemType: "grass", label: "\u8349", placeable: true, scalable: true, radius: 17, amount: 120, icon: "assets/ui/tool_grass.webp", tooltip: "\u98df\u3079\u7269\u3002\u305f\u308a\u306a\u3044\u304c\u5b89\u5fc3\u3059\u308b\u3002" }, stone: { id: "stone", itemType: "stone", label: "\u77f3", placeable: true, scalable: true, radius: 20, amount: 999, icon: "assets/ui/tool_stone.webp", tooltip: "\u91cd\u3044\u77f3\u3002\u843d\u3068\u3059\u3068\u5371\u306a\u3044\u3002" }, genkotsu: { id: "genkotsu", itemType: "genkotsu", label: "\u3052\u3093\u3053\u3064", placeable: true, scalable: false, radius: 88, amount: 100, icon: "assets/ui/tool_genkotsu.webp", tooltip: "\u62f3\u3092\u5730\u9762\u306b\u305f\u305f\u304d\u3064\u3051\u308b\u3002" }, bed: { id: "bed", itemType: "bed", label: "\u5e72\u8349\u5bdd\u5e8a", placeable: true, scalable: false, radius: 37, amount: 999, icon: "assets/ui/tool_bed.webp", tooltip: "\u8fd1\u304f\u3067\u7720\u308b\u3002\u4f53\u529b\u304c\u56de\u5fa9\u3059\u308b\u3002" }, nest_box: { id: "nest_box", itemType: "nest_box", label: "\u5de3\u7bb1", placeable: true, scalable: false, radius: 42, amount: 999, icon: "assets/ui/tool_nest_box.webp", collisionShape: "nest_box_3x3", tooltip: "\u3044\u308b\u3060\u3051\u3067\u30b9\u30c8\u30ec\u30b9\u4f4e\u6e1b\u3002\u7720\u308b\u3068\u7761\u7720\u306e\u8cea\u304c\u4e0a\u304c\u308b\u3002" }, ant_nest: { id: "ant_nest", itemType: "ant_nest", label: "\u30a2\u30ea\u306e\u5de3", placeable: true, scalable: false, radius: 17, amount: 999, icon: "assets/ui/tool_ant_nest.webp", collisionShape: "circle", tooltip: "\u50cd\u304d\u30a2\u30ea\u304c\u305f\u308a\u306a\u3044\u3092\u63a2\u3057\u3001\u5de3\u3078\u904b\u3076\u3002" }, ball: { id: "ball", itemType: "ball", label: "\u30dc\u30fc\u30eb", placeable: true, scalable: false, radius: 18, amount: 999, iconText: "\u26bd", collisionShape: "circle", tooltip: "\u305f\u308a\u306a\u3044\u304c\u904a\u3076\u305f\u3081\u306e\u304a\u3082\u3061\u3083\u3002" }, signboard: { id: "signboard", itemType: "signboard", label: "\u770b\u677f", placeable: true, scalable: false, radius: 30, amount: 999, icon: "assets/ui/tool_signboard.webp", collisionShape: "circle", tooltip: "\u8a2d\u7f6e\u5f8c\u306b\u89b3\u5bdf\u30c4\u30fc\u30eb\u3067\u30af\u30ea\u30c3\u30af\u3059\u308b\u3068\u6587\u5b57\u3092\u66f8\u304d\u8fbc\u3081\u308b\u770b\u677f\u30026\u6587\u5b57\u00d73\u884c\u307e\u3067\u3002" }, duplicator: { id: "duplicator", itemType: "duplicator", label: "\u8907\u88fd\u6a5f", placeable: true, scalable: false, radius: 34, amount: 999, icon: "assets/ui/tool_duplicator.webp", collisionShape: "circle", tooltip: "\u98df\u3079\u7269\u3084\u85ac\u3092\u30bb\u30c3\u30c8\u3059\u308b\u3068\u3001\u81ea\u52d5\u4f9b\u7d66\u3057\u3066\u304f\u308c\u308b\u3002" }, firecracker: { id: "firecracker", itemType: "firecracker", label: "\u7206\u7af9", placeable: true, scalable: true, radius: 15, amount: 999, icon: "assets/ui/tool_firecracker.webp", tooltip: "\u7206\u767a\u3092\u8d77\u3053\u3059\u3002" }, pushpin: { id: "pushpin", itemType: "pushpin", label: "\u753b\u92f2", placeable: true, scalable: false, radius: 8, amount: 999, icon: "assets/ui/tool_pushpin.webp", tooltip: "\u843d\u3068\u3059\u3068\u3053\u308d\u304c\u308a\u3001\u305f\u308a\u306a\u3044\u306b\u523a\u3055\u308b\u3068\u30d1\u30cb\u30c3\u30af\u3068\u7d99\u7d9a\u30c0\u30e1\u30fc\u30b8\u3092\u4e0e\u3048\u308b\u3002" }, oshibyo: { id: "oshibyo", itemType: "oshibyo", label: "\u304a\u3057\u308a\u92f2", placeable: true, scalable: false, radius: 9, amount: 999, icon: "assets/ui/tool_oshibyo.webp", tooltip: "\u523a\u3055\u308b\u3068\u305a\u3093\u3061\u304c\u51fa\u306a\u304f\u306a\u308b\u3002\u3064\u307e\u3093\u3067\u629c\u304f\u3068\u6e9c\u307e\u3063\u305f\u305a\u3093\u3061\u304c\u5f3e\u3051\u98db\u3076\u3002" }, fence_v: { id: "fence_v", itemType: "fence_v", label: "\u7e26\u306e\u67f5", placeable: true, scalable: true, radius: 42, amount: 999, icon: "assets/ui/tool_fence_v.webp", collisionShape: "rect", tooltip: "\u305f\u308a\u306a\u3044\u3092\u901a\u305b\u3093\u307c\u3059\u308b\u3002" }, fence_h: { id: "fence_h", itemType: "fence_h", label: "\u6a2a\u306e\u67f5", placeable: true, scalable: true, radius: 42, amount: 999, icon: "assets/ui/tool_fence_h.webp", collisionShape: "rect", tooltip: "\u305f\u308a\u306a\u3044\u3092\u901a\u305b\u3093\u307c\u3059\u308b\u3002" }, water: { id: "water", itemType: "water", label: "\u6c34", placeable: false, scalable: false, radius: 12, amount: 64, simulationOnly: true }, trace: { id: "trace", itemType: "trace", label: "\u8db3\u8de1", placeable: false, scalable: false, radius: 16, amount: 220, simulationOnly: true }, splat: { id: "splat", itemType: "splat", label: "\u3057\u3076\u304d", placeable: false, scalable: false, radius: 26, amount: 260, simulationOnly: true }, food: { id: "food", itemType: "food", label: "\u98df\u3079\u7269", placeable: false, scalable: false, radius: 14, amount: 85, simulationOnly: true }, ant_corpse: { id: "ant_corpse", itemType: "ant_corpse", label: "\u30a2\u30ea\u306e\u6b7b\u9ab8", placeable: false, scalable: false, radius: 4, amount: 24, simulationOnly: true }, }); const ITEM_TRAITS = Object.freeze({ obstacle: new Set(["stone", "ball", "nest_box", "bed", "duplicator", "fence_v", "fence_h"]), food_interest: new Set(["sweet", "love_mochi", "fight_mochi", "grass", "zunchi", "water", "water_bowl", "ant_corpse", "duplicator", "laxative", "protein", "niteropu", "ammo", "mystery_drug", "mercury", "giant_drug", "dwarf_drug", "zunda_juice", "sleep_drug"]), hazard: new Set(["firecracker", "genkotsu", "pushpin", "sleep_drug", "zunchi", "splat", "mystery_drug", "laxative", "niteropu", "mercury", "giant_drug", "dwarf_drug"]), pin: new Set(["pushpin", "oshibyo"]), kinematic: new Set(["ball", "pushpin", "oshibyo", "zunchi"]), sleepFurniture: new Set(["bed", "nest_box"]), draggable: new Set(["ball", "stone", "bed", "nest_box", "signboard", "duplicator", "pushpin", "oshibyo"]), }); function itemHasTrait(type = "", trait = "") { return Boolean(ITEM_TRAITS[trait]?.has?.(String(type || ""))); } function isPinType(type = "") { return itemHasTrait(type, "pin"); } function isSleepFurnitureType(type = "") { return itemHasTrait(type, "sleepFurniture"); } function isLodgedPin(item = null) { return Boolean(item && !item.dead && isPinType(item.type) && item.pinState === "lodged"); } function lodgedPinFor(tarinai = null, worldRef = null) { if (!tarinai?.stuckPushpinId) return null; const items = worldRef?.items || tarinai.world?.items || []; return items.find(it => isLodgedPin(it) && it.id === tarinai.stuckPushpinId) || null; } function lodgedPinBehaviorFor(tarinai = null, worldRef = null) { return pinBehaviorFor(lodgedPinFor(tarinai, worldRef)?.type); } function hasLodgedPinEffect(tarinai = null, effectKey = "") { return Boolean(lodgedPinBehaviorFor(tarinai)?.[effectKey]); } const PIN_BEHAVIORS = Object.freeze({ pushpin: { type: "pushpin", looseAsset: "pushpin", lodgedAsset: "pushpin_stuck", attachMode: "impact", panicOnAttach: true, damageOnAttach: 6, damagePerTick: 1.4, stressOnAttach: 18, stressPerTick: 2.6, blocksZunchi: false, burstZunchiOnDetach: false }, oshibyo: { type: "oshibyo", looseAsset: "oshibyo", lodgedAsset: "oshibyo_stuck", attachMode: "butt", panicOnAttach: false, damageOnAttach: 0, damagePerTick: 0, stressOnAttach: 0, stressPerTick: 0, blocksZunchi: true, burstZunchiOnDetach: true }, }); function pinBehaviorFor(type = "") { return PIN_BEHAVIORS[String(type || "")] || null; } let ITEM_VISUAL_DEFINITIONS = null; let FOOD_REGISTRY = null; let FOOD_DEFINITIONS = null; let EFFECT_REGISTRY = null; let EFFECT_DEFINITIONS = null; const TOOL_CATEGORIES = Object.freeze([ { id: "operate", label: "\u64cd\u4f5c", themeClass: "tool-category-operate", order: 10, toolIds: ["observe", "delete", "poke", "pinch", "new", "water_hose"] }, { id: "food", label: "\u98df\u3079\u7269", themeClass: "tool-category-food", order: 20, toolIds: ["sweet", "love_mochi", "fight_mochi", "grass", "water_bowl", "zunda_juice", "duplicator"] }, { id: "medicine", label: "\u85ac", themeClass: "tool-category-medicine", order: 30, toolIds: ["sleep_drug", "laxative", "protein", "niteropu", "ammo", "mystery_drug", "mercury", "giant_drug", "dwarf_drug"] }, { id: "habitat", label: "\u751f\u6d3b", themeClass: "tool-category-habitat", order: 40, toolIds: ["zunchi", "bed", "nest_box", "ball", "signboard", "fence_v", "fence_h"] }, { id: "hazard", label: "\u5371\u967a", themeClass: "tool-category-hazard", order: 50, toolIds: ["stone", "genkotsu", "firecracker", "pushpin", "oshibyo", "ant_nest"] }, ]); function toolCategories() { return [...TOOL_CATEGORIES].sort((a, b) => (a.order || 0) - (b.order || 0)); } function toolDefinition(id = "") { return TOOL_DEFINITIONS[id] || null; } function toolLabel(id = "") { const def = toolDefinition(id); return def?.label || id || ""; } function toolItemType(id = "") { const def = toolDefinition(id); return def?.placeable ? (def.itemType || def.id) : null; } function itemDefinition(type = "") { return Object.values(TOOL_DEFINITIONS).find(def => (def.itemType || def.id) === type) || null; } function itemRadiusFor(type = "", fallback = 12) { return itemDefinition(type)?.radius ?? fallback; } function itemAmountFor(type = "", fallback = 80) { return itemDefinition(type)?.amount ?? fallback; } const TOOL_SIZE_ORDER = Object.freeze(["small", "medium", "large"]); const TOOL_SIZE_LABELS = Object.freeze({ small: "\u5c0f", medium: "\u4e2d", large: "\u5927" }); const TOOL_SIZE_SCALES = Object.freeze({ small: 0.68, medium: 1.0, large: 1.48 }); function normalizedToolSizeFor(worldRef = null, type = "") { const def = itemDefinition(type); if (!def?.scalable) return "medium"; const value = worldRef?.toolSizes?.[type] || worldRef?.toolSize || "medium"; return TOOL_SIZE_ORDER.includes(value) ? value : "medium"; } function toolSizeScaleForValue(size = "medium") { return TOOL_SIZE_SCALES[size] || TOOL_SIZE_SCALES.medium; } function toolSizeScaleFor(worldRef = null, type = "") { return itemDefinition(type)?.scalable ? toolSizeScaleForValue(normalizedToolSizeFor(worldRef, type)) : 1; } function scalableToolIds() { return Object.values(TOOL_DEFINITIONS).filter(def => def.scalable).map(def => def.id); } function toolTipsFromDefinitions() { return Object.fromEntries(Object.entries(TOOL_DEFINITIONS).filter(([, def]) => def.tooltip).map(([id, def]) => [id, def.tooltip])); } (function (global) { const raw = String.raw`{ "water": { "renderer": "oval_droplet", "shape": "horizontal_oval_liquid", "palette": { "fill": "rgba(86,157,224,0.82)", "stroke": "rgba(45,101,168,0.52)", "highlight": "rgba(255,255,255,0.64)" }, "scale": 1, "uiPreviewScale": 1, "fieldScale": 1, "accessibleShapeTraits": ["horizontal", "oval", "liquid", "blue"] }, "zunda_juice": { "renderer": "oval_droplet", "shape": "horizontal_oval_liquid", "palette": { "fill": "rgba(125,218,82,0.94)", "stroke": "rgba(63,150,50,0.62)", "highlight": "rgba(238,255,226,0.72)" }, "scale": 1, "uiPreviewScale": 1, "fieldScale": 1, "accessibleShapeTraits": ["horizontal", "oval", "liquid", "green"] }, "mercury": { "renderer": "oval_droplet", "shape": "horizontal_oval_liquid", "palette": { "fill": "rgba(210,216,222,0.94)", "stroke": "rgba(112,122,132,0.62)", "highlight": "rgba(255,255,255,0.72)" }, "scale": 1, "uiPreviewScale": 1, "fieldScale": 1, "accessibleShapeTraits": ["horizontal", "oval", "liquid", "silver"] }, "water_bowl": { "renderer": "water_bowl", "shape": "bowl_with_horizontal_oval_water", "palette": { "bowlFill": "rgba(171,120,76,0.72)", "bowlStroke": "rgba(94,69,48,0.55)", "waterFill": "rgba(86,157,224,0.58)", "waterStroke": "rgba(62,113,178,0.36)", "highlight": "rgba(255,255,255,0.54)" }, "scale": 1, "uiPreviewScale": 1, "fieldScale": 1, "accessibleShapeTraits": ["bowl", "horizontal", "oval", "water", "blue"] }, "sleep_drug": { "renderer": "sleep_tablet", "shape": "moon_tablet", "palette": { "fill": "#ebe4ff", "stroke": "rgba(74,60,144,0.72)", "accent": "rgba(112,91,188,0.82)" }, "scale": 1, "uiPreviewScale": 1, "fieldScale": 1, "accessibleShapeTraits": ["tablet", "moon", "purple"] }, "laxative": { "renderer": "laxative_tablet", "shape": "rounded_tablet_zunchi_motif", "palette": { "fill": "#f4d49a", "stroke": "rgba(98,61,29,0.82)", "accent": "rgba(96,70,38,0.88)" }, "scale": 1, "uiPreviewScale": 1, "fieldScale": 1, "accessibleShapeTraits": ["tablet", "zunchi motif", "tan"] }, "mystery_drug": { "renderer": "mystery_tablet", "shape": "question_mark_tablet", "palette": { "fill": "#f3ecff", "stroke": "rgba(91,55,150,0.84)", "accent": "rgba(91,55,150,0.92)" }, "scale": 1, "uiPreviewScale": 1, "fieldScale": 1, "accessibleShapeTraits": ["tablet", "question mark", "purple"] }, "ammo": { "renderer": "bullet", "shape": "bullet", "palette": { "fill": "#f4c46a", "stroke": "rgba(87,51,22,0.78)", "highlight": "rgba(255,237,176,0.62)" }, "scale": 1, "uiPreviewScale": 1, "fieldScale": 1, "accessibleShapeTraits": ["bullet", "gold"] }, "protein": { "renderer": "powder_pile", "shape": "powder_pile", "palette": { "fill": "#f4a24f", "stroke": "#b45b27", "highlight": "#ffe2b9" }, "scale": 1, "uiPreviewScale": 1, "fieldScale": 1, "accessibleShapeTraits": ["powder", "orange"] }, "niteropu": { "renderer": "powder_pile", "shape": "powder_pile", "palette": { "fill": "#8a79d0", "stroke": "#4c3f91", "highlight": "#d8d1ff" }, "scale": 1, "uiPreviewScale": 1, "fieldScale": 1, "accessibleShapeTraits": ["powder", "purple"] }, "giant_drug": { "renderer": "split_pill", "shape": "split_pill", "palette": { "fill": "#ffe3cf", "stroke": "#c9632f", "highlight": "#ffb47b" }, "scale": 1.08, "uiPreviewScale": 1.08, "fieldScale": 1.08, "accessibleShapeTraits": ["pill", "large", "orange"] }, "dwarf_drug": { "renderer": "split_pill", "shape": "split_pill", "palette": { "fill": "#e9e9ff", "stroke": "#6967ac", "highlight": "#b8b5f4" }, "scale": 0.92, "uiPreviewScale": 0.92, "fieldScale": 0.92, "accessibleShapeTraits": ["pill", "small", "purple"] }, "ball": { "renderer": "emoji", "shape": "soccer_ball", "palette": {}, "scale": 1, "uiPreviewScale": 1, "fieldScale": 1, "accessibleShapeTraits": ["round", "soccer ball"], "emojiFallback": "\u26bd" } }`; const defs = Object.freeze(JSON.parse(raw)); function itemVisualDefinition(type = "") { return defs[String(type || "")] || null; } function itemVisualPalette(type = "") { return itemVisualDefinition(type)?.palette || null; } ITEM_VISUAL_DEFINITIONS = defs; global.itemVisualDefinition = itemVisualDefinition; global.itemVisualPalette = itemVisualPalette; })(typeof window !== "undefined" ? window : globalThis); (function (global) { const rawDefinitions = { food: { label: "\u98df\u3079\u7269", nutrition: 18.0, hungerRelief: 1.0, interest: "normal", decayRateMultiplier: 1.0, hygienePenalty: 0.055 }, sweet: { label: "\u305a\u3093\u3060\u9905", nutrition: 16.5, hungerRelief: 0.95, interest: "zunda_high", decayRateMultiplier: 0.82, hygienePenalty: 0.055, cures: ["disease_explosion", "disease_fight"] }, love_mochi: { label: "\u3078\u3053\u9905", nutrition: 15.0, hungerRelief: 0.92, interest: "medicine_low", decayRateMultiplier: 1.0, hygienePenalty: 0.055, effectId: "love_mochi" }, fight_mochi: { label: "\u3051\u3093\u304b\u9905", nutrition: 15.0, hungerRelief: 0.92, interest: "medicine_low", decayRateMultiplier: 1.0, hygienePenalty: 0.055, effectId: "fight_mochi" }, sleep_drug: { label: "\u306d\u3080\u308a\u85ac", nutrition: 15.0, hungerRelief: 0.92, interest: "medicine_low", decayRateMultiplier: 1.0, hygienePenalty: 0.055, effectId: "sleep_drug" }, laxative: { label: "\u4e0b\u5264", nutrition: 6.8, hungerRelief: 0, interest: "medicine_low", decayRateMultiplier: 1.0, hygienePenalty: 0.055, effectId: "laxative", passThrough: true }, protein: { label: "\u30d7\u30ed\u30c6\u30a4\u30f3", nutrition: 15.0, hungerRelief: 0.92, interest: "medicine_low", decayRateMultiplier: 1.0, hygienePenalty: 0.055, effectId: "protein" }, niteropu: { label: "\u30f3\u30a4\u30c6\u30ed\u30d7", nutrition: 15.0, hungerRelief: 0.92, interest: "medicine_low", decayRateMultiplier: 1.0, hygienePenalty: 0.055, effectId: "niteropu" }, ammo: { label: "\u5f3e\u85ac", nutrition: 15.0, hungerRelief: 0.92, interest: "medicine_low", decayRateMultiplier: 1.0, hygienePenalty: 0.055, effectId: "ammo" }, mystery_drug: { label: "\u602a\u3057\u3044\u85ac", nutrition: 15.0, hungerRelief: 0.92, interest: "medicine_low", decayRateMultiplier: 1.0, hygienePenalty: 0.055, effectId: "mystery_drug" }, mercury: { label: "\u6c34\u9280", nutrition: 15.0, hungerRelief: 0.92, interest: "medicine_low", decayRateMultiplier: 1.0, hygienePenalty: 0.055, effectId: "mercury" }, giant_drug: { label: "\u5de8\u5927\u85ac", nutrition: 15.0, hungerRelief: 0.92, interest: "medicine_low", decayRateMultiplier: 1.0, hygienePenalty: 0.055, effectId: "giant_drug" }, dwarf_drug: { label: "\u77ee\u5c0f\u85ac", nutrition: 15.0, hungerRelief: 0.92, interest: "medicine_low", decayRateMultiplier: 1.0, hygienePenalty: 0.055, effectId: "dwarf_drug" }, zunda_juice: { label: "\u305a\u3093\u3060\u6c41", nutrition: 15.0, hungerRelief: 0.92, interest: "medicine_low", decayRateMultiplier: 1.12, hygienePenalty: 0.055, effectId: "zunda_juice", removesItemEffects: true }, grass: { label: "\u8349", nutrition: 2.6, hungerRelief: 0.55, interest: "grass_like", decayRateMultiplier: 1.0, hygienePenalty: 0 }, water: { label: "\u6c34", nutrition: 0, hungerRelief: 0, interest: "water", decayRateMultiplier: 1.0, hygienePenalty: 0 }, water_bowl: { label: "\u6c34\u306e\u76bf", nutrition: 0, hungerRelief: 0, interest: "water", decayRateMultiplier: 1.0, hygienePenalty: 0 }, ant_corpse: { label: "\u30a2\u30ea\u306e\u6b7b\u9ab8", nutrition: 4.2, hungerRelief: 0.62, interest: "corpse", decayRateMultiplier: 1.0, hygienePenalty: 0.065 } }; class FoodRegistry extends global.TarinaiRegistryBase { servingTypes() { return [...this.defs.entries()] .filter(([, def]) => def.interest && !["grass_like", "water", "corpse"].includes(def.interest)) .map(([id]) => id); } typesByInterest(...interests) { const wanted = new Set(interests.flat().filter(Boolean)); return [...this.defs.entries()] .filter(([, def]) => wanted.has(def.interest)) .map(([id]) => id); } paramEffectTypes() { return [...this.defs.entries()].filter(([, def]) => def.effectId && !def.effectId.endsWith("_mochi") && def.effectId !== "sleep_drug").map(([id]) => id); } nutrition(type = "", fallback = 0) { const value = this.get(type)?.nutrition; return Number.isFinite(value) ? value : fallback; } hungerRelief(type = "", fallback = 0) { const value = this.get(type)?.hungerRelief; return Number.isFinite(value) ? value : fallback; } effectId(type = "") { return this.get(type)?.effectId || ""; } passiveDecayMultiplier(type = "") { return this.get(type)?.decayRateMultiplier ?? 1; } hygienePenalty(type = "") { return this.get(type)?.hygienePenalty ?? 0.055; } debugSnapshot() { return super.debugSnapshot((id, def) => ({ id, label: def.label, interest: def.interest, effectId: def.effectId || "" })); } } const registry = new FoodRegistry(rawDefinitions); FOOD_REGISTRY = registry; FOOD_DEFINITIONS = Object.freeze(rawDefinitions); })(typeof window !== "undefined" ? window : globalThis); (function (global) { const rawDefinitions = { laxative: { label: "\u4e0b\u5264", color: "rgba(119, 136, 58, 0.78)", kind: "timed", defaultDuration: 60, tags: ["item", "behavior", "food"], display: timer => `${Math.ceil(timer || 0)}\u79d2 / 3\u79d2\u3054\u3068\u306b\u305a\u3093\u3061 / \u98df\u4e8b\u306e\u7a7a\u8179\u56de\u5fa9\u306a\u3057`, }, ammo: { label: "\u5f3e\u85ac", color: "rgba(216, 142, 38, 0.82)", kind: "timed", defaultDuration: 18, tags: ["item", "behavior", "movement"], display: timer => `${Math.ceil(timer || 0)}\u79d2 / \u6b69\u884c\u901f\u5ea610\u500d / \u885d\u7a81\u3067\u5f3e\u304d\u98db\u3070\u3059`, }, protein: { label: "\u30d7\u30ed\u30c6\u30a4\u30f3", color: "rgba(239, 122, 51, 0.86)", kind: "permanent", exclusiveGroup: "power", tags: ["item", "behavior", "power"], display: () => "\u6c38\u7d9a / \u4e0e\u30c0\u30e1\u30fc\u30b81.75\u500d / \u75c5\u6c17\u78ba\u73870.5\u500d", }, niteropu: { label: "\u30f3\u30a4\u30c6\u30ed\u30d7", color: "rgba(88, 76, 166, 0.78)", kind: "permanent", exclusiveGroup: "power", tags: ["item", "behavior", "power"], display: () => "\u6c38\u7d9a / \u4e0e\u30c0\u30e1\u30fc\u30b80.5\u500d / \u75c5\u6c17\u78ba\u73872\u500d", }, mercury: { label: "\u6c34\u9280", color: "rgba(96, 146, 166, 0.82)", kind: "permanent", stack: "multiply", tags: ["item", "life"], display: (_timer, t) => `\u6c38\u7d9a / \u5bff\u547dx${((t && t.mercuryLifeMultiplier) || 1.3).toFixed(2)}\uff08\u98df\u3079\u308b\u305f\u3073\u4e57\u7b97\uff09`, }, giant_drug: { label: "\u5de8\u5927\u85ac", color: "rgba(228, 123, 58, 0.86)", kind: "permanent", exclusiveGroup: "body_size", tags: ["item", "body", "behavior"], display: () => "\u6c38\u7d9a / \u30b5\u30a4\u30ba3\u500d / \u653b\u64832\u500d / \u901f\u5ea61.5\u500d / \u6bce\u79d21\u30c0\u30e1\u30fc\u30b8", }, dwarf_drug: { label: "\u77ee\u5c0f\u85ac", color: "rgba(109, 107, 190, 0.82)", kind: "permanent", exclusiveGroup: "body_size", tags: ["item", "body", "behavior"], display: () => "\u6c38\u7d9a / \u30b5\u30a4\u30ba0.25\u500d / \u653b\u64830.25\u500d / \u901f\u5ea60.5\u500d / \u6bce\u79d21\u30c0\u30e1\u30fc\u30b8", }, love_mochi: { label: "\u3078\u3053\u9905", color: "rgba(190,82,132,0.78)", kind: "timer", timerProp: "loveMochiTimer", tags: ["item", "mochi", "behavior"], display: timer => `${Math.ceil(timer || 0)}\u79d2 / \u7e41\u6b96\u884c\u52d5\u304c\u8d77\u304d\u3084\u3059\u3044`, }, fight_mochi: { label: "\u3051\u3093\u304b\u9905", color: "rgba(180,86,52,0.80)", kind: "timer", timerProp: "fightMochiTimer", tags: ["item", "mochi", "behavior"], display: timer => `${Math.ceil(timer || 0)}\u79d2 / \u55a7\u5629\u304c\u8d77\u304d\u3084\u3059\u3044`, }, sleep_drug: { label: "\u306d\u3080\u308a\u85ac", color: "rgba(104,84,168,0.78)", kind: "disease", tags: ["item", "disease", "behavior"], }, mystery_drug: { label: "\u602a\u3057\u3044\u85ac", color: "rgba(60, 154, 87, 0.80)", kind: "instant", tags: ["item", "behavior"], }, zunda_juice: { label: "\u305a\u3093\u3060\u6c41", color: "rgba(87, 180, 79, 0.86)", kind: "cleanse", tags: ["item", "cleanse"], }, disease_zunchi: { label: "\u305a\u3093\u3061\u75c5", color: "rgba(113, 93, 59, 0.74)", kind: "disease", tags: ["disease", "behavior"] }, disease_sleep: { label: "\u306d\u3080\u308a\u75c5", color: "rgba(104,84,168,0.78)", kind: "disease", tags: ["disease", "behavior"] }, disease_explosion: { label: "\u7206\u767a\u75c5", color: "rgba(210, 90, 44, 0.78)", kind: "disease", tags: ["disease", "behavior"] }, disease_fight: { label: "\u304d\u305a\u3064\u304d\u75c5", color: "rgba(180,86,52,0.80)", kind: "disease", tags: ["disease", "behavior"] }, }; Object.assign(rawDefinitions.laxative, { modifiers: { hungerReliefMultiplier: 0 }, tickInterval: 3, bubblePolicy: "none", mysteryEligible: true, zundaJuiceRemovable: true, }); Object.assign(rawDefinitions.ammo, { modifiers: { speedMultiplier: 10 }, visualEffectId: "fall", bubblePolicy: "none", mysteryEligible: true, zundaJuiceRemovable: true, }); Object.assign(rawDefinitions.protein, { modifiers: { damageMultiplier: 1.75, diseaseChanceMultiplier: 0.5 }, visualEffectId: "fall_up_red", bubblePolicy: "none", mysteryEligible: true, zundaJuiceRemovable: true, }); Object.assign(rawDefinitions.niteropu, { modifiers: { damageMultiplier: 0.5, diseaseChanceMultiplier: 2 }, visualEffectId: "fall_down_purple", bubblePolicy: "none", mysteryEligible: true, zundaJuiceRemovable: true, }); Object.assign(rawDefinitions.mercury, { modifiers: { lifeMultiplierPerUse: 1.3 }, bubblePolicy: "none", mysteryEligible: true, zundaJuiceRemovable: true, }); Object.assign(rawDefinitions.giant_drug, { modifiers: { sizeMultiplier: 3, damageMultiplier: 2, speedMultiplier: 1.5, damagePerSecond: 1 }, bubblePolicy: "none", mysteryEligible: true, zundaJuiceRemovable: true, }); Object.assign(rawDefinitions.dwarf_drug, { modifiers: { sizeMultiplier: 0.25, damageMultiplier: 0.25, speedMultiplier: 0.5, damagePerSecond: 1 }, bubblePolicy: "none", mysteryEligible: true, zundaJuiceRemovable: true, }); for (const id of ["love_mochi", "fight_mochi", "sleep_drug"]) { Object.assign(rawDefinitions[id], { mysteryEligible: true, zundaJuiceRemovable: id !== "sleep_drug" }); } Object.assign(rawDefinitions.zunda_juice, { removesItemEffects: true, mysteryEligible: false }); Object.assign(rawDefinitions.love_mochi, { onApply(tarinai, context = {}) { const scale = context.scale ?? 1; tarinai.loveMochiTimer = Math.max(tarinai.loveMochiTimer || 0, 42 * scale + rand(4, 12)); const nutrition = Number(context.nutrition) || 0; if (typeof applyNeedRelief === "function") applyNeedRelief(tarinai, { fulfill: -(context.source === "eating" ? nutrition * 0.5 : 12), social: -4 }); if (context.source === "eating") { if (tarinai.forceBehavior) tarinai.forceBehavior("approach_mate", { source: "love_mochi", priority: 150, ttl: 24, searchRange: 820, causeText: "\u3078\u3053\u9905\u306e\u52b9\u679c" }); else if (typeof queueForcedTarinaiBehavior === "function") queueForcedTarinaiBehavior(tarinai, "approach_mate", { source: "love_mochi", priority: 150, ttl: 24, searchRange: 820, causeText: "\u3078\u3053\u9905\u306e\u52b9\u679c" }); } return true; }, }); Object.assign(rawDefinitions.fight_mochi, { onApply(tarinai, context = {}) { const scale = context.scale ?? 1; tarinai.fightMochiTimer = Math.max(tarinai.fightMochiTimer || 0, 52 * scale + rand(10, 18)); const nutrition = Number(context.nutrition) || 0; if (typeof applyNeedShock === "function") applyNeedShock(tarinai, { safety: context.source === "eating" ? nutrition * 0.5 : 16 }); if (context.source === "eating") { tarinai.fearTimer = Math.max(tarinai.fearTimer || 0, 0.32); if (tarinai.forceBehavior) tarinai.forceBehavior("fight_rival", { source: "fight_mochi", priority: 180, ttl: 24, searchRange: 820, causeText: "\u3051\u3093\u304b\u9905\u306e\u52b9\u679c" }); else if (typeof queueForcedTarinaiBehavior === "function") queueForcedTarinaiBehavior(tarinai, "fight_rival", { source: "fight_mochi", priority: 180, ttl: 24, searchRange: 820, causeText: "\u3051\u3093\u304b\u9905\u306e\u52b9\u679c" }); } return true; }, }); Object.assign(rawDefinitions.sleep_drug, { onApply(tarinai, context = {}) { if (context.source === "eating") { const nutrition = Number(context.nutrition) || 0; if (typeof applyNeedRelief === "function") applyNeedRelief(tarinai, { safety: -nutrition * 0.25, sleep: -nutrition * 0.15 }); tarinai.energy = clamp(tarinai.energy - nutrition * 0.20, 0, 100); tarinai.fearTimer = Math.max(0, (tarinai.fearTimer || 0) - 0.18); tarinai.fightMochiTimer = Math.max(0, (tarinai.fightMochiTimer || 0) - 2.5); const baseChance = Math.min(0.98, 0.35 + nutrition * 0.075); const sleepChance = tarinai.diseaseChance ? tarinai.diseaseChance(baseChance) : baseChance; if (tarinai.forceBehavior) tarinai.forceBehavior("sleep_anywhere", { source: "sleep_drug", priority: 145, ttl: 14, duration: 8, minDuration: 8, causeText: "\u306d\u3080\u308a\u85ac\u306e\u52b9\u679c" }); else if (typeof queueForcedTarinaiBehavior === "function") queueForcedTarinaiBehavior(tarinai, "sleep_anywhere", { source: "sleep_drug", priority: 145, ttl: 14, duration: 8, minDuration: 8, causeText: "\u306d\u3080\u308a\u85ac\u306e\u52b9\u679c" }); if (!tarinai.sleepDisease && Math.random() < Math.min(0.98, sleepChance)) return tarinai.infectSleepDisease?.(context.item || null) || true; return true; } return tarinai.infectSleepDisease?.(context.item || null) || true; }, }); Object.assign(rawDefinitions.laxative, { onApply(tarinai, context = {}) { return tarinai.setTimedItemEffect?.("laxative", Math.round((this.defaultDuration || 60) * (context.scale ?? 1))); }, onTick(tarinai) { tarinai.forceLaxativePoop?.(); return true; }, }); Object.assign(rawDefinitions.ammo, { onApply(tarinai, context = {}) { return tarinai.setTimedItemEffect?.("ammo", Math.round((this.defaultDuration || 18) * (context.scale ?? 1))); }, }); Object.assign(rawDefinitions.protein, { onApply(tarinai) { return tarinai.setPowerItemMode?.("protein"); }, }); Object.assign(rawDefinitions.niteropu, { onApply(tarinai) { return tarinai.setPowerItemMode?.("niteropu"); }, }); Object.assign(rawDefinitions.mercury, { onApply(tarinai) { return tarinai.setMercuryEffect?.(); }, }); Object.assign(rawDefinitions.giant_drug, { onApply(tarinai) { return tarinai.setSizeItemMode?.("giant_drug"); }, onTick(tarinai) { tarinai.damage?.(this.modifiers?.damagePerSecond || 1, this.label); if (tarinai.world?.effects) tarinai.spawnPowerItemEffect?.("giant_drug", 0.25); return true; }, }); Object.assign(rawDefinitions.dwarf_drug, { onApply(tarinai) { return tarinai.setSizeItemMode?.("dwarf_drug"); }, onTick(tarinai) { tarinai.damage?.(this.modifiers?.damagePerSecond || 1, this.label); if (tarinai.world?.effects) tarinai.spawnPowerItemEffect?.("dwarf_drug", 0.25); return true; }, }); Object.assign(rawDefinitions.zunda_juice, { onApply(tarinai) { return tarinai.clearAllItemEffects?.(); }, }); Object.assign(rawDefinitions.disease_zunchi, { onApply(tarinai, context = {}) { return tarinai.infectZunchiDisease?.(context.item || null, true) || true; }, }); Object.assign(rawDefinitions.disease_sleep, { onApply(tarinai, context = {}) { return tarinai.infectSleepDisease?.(context.item || null) || true; }, }); Object.assign(rawDefinitions.disease_explosion, { onApply(tarinai, context = {}) { return tarinai.infectExplosionDisease?.(context.item || null) || true; }, }); Object.assign(rawDefinitions.disease_fight, { onApply(tarinai, context = {}) { return tarinai.infectFightDisease?.(context.item || null) || true; }, }); class EffectRegistry extends global.TarinaiRegistryBase { label(id = "") { return super.label(id, id || "\u4e0d\u660e"); } color(id = "") { return super.color(id, "rgba(255,242,160,0.85)"); } display(id = "", timer = 0, tarinai = null) { const def = this.get(id); if (!def) return ""; return typeof def.display === "function" ? def.display(timer, tarinai, def) : (def.display || ""); } apply(id = "", tarinai = null, context = {}) { const def = this.get(id); if (!def || !tarinai || typeof def.onApply !== "function") return false; return Boolean(def.onApply.call(def, tarinai, { ...context, effectId: id, registry: this })); } tick(id = "", tarinai = null, dt = 0, context = {}) { const def = this.get(id); if (!def || !tarinai || typeof def.onTick !== "function") return false; return Boolean(def.onTick.call(def, tarinai, Math.max(0, Number(dt) || 0), { ...context, effectId: id, registry: this })); } remove(id = "", tarinai = null, context = {}) { const def = this.get(id); if (!def || !tarinai || typeof def.onRemove !== "function") return false; return Boolean(def.onRemove.call(def, tarinai, { ...context, effectId: id, registry: this })); } modifier(id = "", key = "", fallback = 1) { const value = this.get(id)?.modifiers?.[key]; return Number.isFinite(value) ? value : fallback; } timerValue(tarinai = null, id = "") { const def = this.get(id); if (!def || !tarinai) return 0; if (def.timerProp) return Number(tarinai[def.timerProp] || 0); if (def.kind === "timed") return Number(tarinai.itemEffectTimers?.[id] || 0); return 0; } isActive(tarinai = null, id = "") { if (!tarinai) return false; const def = this.get(id); if (!def) return false; if (id === "protein" || id === "niteropu") return tarinai.powerItemMode === id; if (id === "giant_drug" || id === "dwarf_drug") return tarinai.sizeItemMode === id; if (id === "mercury") return tarinai.lifeItemMode === "mercury"; return this.timerValue(tarinai, id) > 0.04; } activeSummary(tarinai = null) { const order = ["protein", "niteropu", "mercury", "giant_drug", "dwarf_drug", "laxative", "ammo", "love_mochi", "fight_mochi"]; return order .filter(id => this.isActive(tarinai, id)) .map(id => ({ id, label: this.label(id), detail: this.display(id, this.timerValue(tarinai, id), tarinai), })); } labels() { return super.labels(); } colors() { return super.colors(); } randomPool() { const itemEffects = [...this.defs.entries()] .filter(([id, def]) => Boolean(def.mysteryEligible && id !== "mystery_drug" && id !== "zunda_juice" && !(def.tags || []).includes("disease"))) .map(([id]) => id) .filter(id => id !== "mystery_drug" && id !== "zunda_juice"); const diseases = global.TarinaiDiseaseRegistry?.mysteryPool?.() || []; return [...new Set([...itemEffects, ...diseases])]; } permanentIds() { return [...this.defs.entries()].filter(([, def]) => def.kind === "permanent").map(([id]) => id); } timedIds() { return [...this.defs.entries()].filter(([, def]) => def.kind === "timed" || def.kind === "timer").map(([id]) => id); } debugSnapshot() { return [...this.defs.entries()].map(([id, def]) => ({ id, label: def.label, kind: def.kind, group: def.exclusiveGroup || "" })); } } const registry = new EffectRegistry(rawDefinitions); EFFECT_REGISTRY = registry; EFFECT_DEFINITIONS = Object.freeze(rawDefinitions); })(typeof window !== "undefined" ? window : globalThis); (function (global) { function itemTypeDefinition(type = "") { const id = String(type || ""); const tool = typeof itemDefinition === "function" ? itemDefinition(id) : null; const food = FOOD_DEFINITIONS?.[id] || null; const effectId = food?.effectId || (EFFECT_DEFINITIONS?.[id] ? id : ""); const effect = effectId ? (EFFECT_DEFINITIONS?.[effectId] || null) : null; const visual = ITEM_VISUAL_DEFINITIONS?.[id] || null; return { id, tool, food, effect, effectId, visual, label: tool?.label || food?.label || effect?.label || id, radius: tool?.radius, amount: tool?.amount, placeable: Boolean(tool?.placeable), scalable: Boolean(tool?.scalable), traits: Object.fromEntries(Object.keys(ITEM_TRAITS).map(trait => [trait, itemHasTrait(id, trait)])), }; } function itemTypeLabel(type = "") { return itemTypeDefinition(type).label; } function itemTypeDebugSnapshot() { const ids = new Set([ ...Object.values(TOOL_DEFINITIONS).map(def => def.itemType || def.id), ...Object.keys(FOOD_DEFINITIONS || {}), ...Object.keys(EFFECT_DEFINITIONS || {}), ...Object.keys(ITEM_VISUAL_DEFINITIONS || {}), ]); return [...ids].sort().map(id => itemTypeDefinition(id)); } global.TarinaiItemRegistry = Object.freeze({ definition: itemTypeDefinition, label: itemTypeLabel, debugSnapshot: itemTypeDebugSnapshot, tool: toolDefinition, item: itemDefinition, food: FOOD_REGISTRY, effect: EFFECT_REGISTRY, visual: itemVisualDefinition, hasTrait: itemHasTrait, radius: itemRadiusFor, amount: itemAmountFor, categories: toolCategories, scalableToolIds, pinBehavior: pinBehaviorFor, }); })(typeof window !== "undefined" ? window : globalThis);