"use strict"; // Consumable/drink/medicine behavior runtime. function finishBehaviorConsumption(t, item, foodType, eating, dt, label, color = "#f1dfb7") { if (!t || !item || !(eating > 0)) return false; t.beginEating?.(item, label || "\u98df\u3079\u3066\u3044\u308b"); t.vx *= Math.pow(0.25, Math.max(0.1, dt || 0.1) * 60); t.vy *= Math.pow(0.25, Math.max(0.1, dt || 0.1) * 60); t.eatTimer = Math.max(t.eatTimer || 0, 1.0); t.eatCooldown = rand(0.96, 1.12); t.foodReactTimer = Math.max(t.foodReactTimer || 0, 0.16); if (foodType !== "first_aid" && typeof applyNeedSatisfaction === "function") applyNeedSatisfaction(t, { food: Math.max(18, Math.min(54, (Number(eating) || 0) * 1.1)) }, "meal"); t.lastMealColor = color; if (Math.random() < Math.max(0.1, dt || 0.1) * 2.2) { const mouth = t.mouthPosition?.(item) || { x: t.x, y: t.y }; t.world?.spawnEatEffect?.(mouth.x, mouth.y, color); } if ((t.world?.time || 0) > (t.nextEatSound || -999)) { audio.eat?.(); t.nextEatSound = (t.world?.time || 0) + 0.42; } return true; } function consumeBehaviorTarget(t, world, target, role = "food", dt = 0.12) { if (!t || !world || !target || target.dead) return false; const foodType = effectiveFoodTypeForItem(target); const labelFor = type => typeof toolLabel === "function" ? toolLabel(type || target.type) : "\u98df\u3079\u7269"; const effectiveDt = Math.max(0.10, Number(dt) || 0.12); if (isDuplicatorFoodSource(target) && (t.eatCooldown || 0) > 0.04) { t.setActionState?.(role === "medicine" ? "seek_food" : "eat", { target, reason: `${labelFor(foodType)}\u3092\u98df\u3079\u3066\u3044\u308b`, sleeping: false }); return true; } if ((role === "drink" || role === "medicine") && target.type === "water") { const text = "\u6c34\u3092\u98f2\u3093\u3067\u3044\u308b"; t.setActionState?.("seek_water", { target, reason: text, sleeping: false }); t.thought = text; t.applyWaterEffect?.(0.55, target.type); if (target.type === "water") target.amount -= 1.2; if ((world.time || 0) > (t.nextDrinkSound || -999)) { audio.play?.("sfx_drink", { category: "ops", minGap: 0.20 }); t.nextDrinkSound = (world.time || 0) + 0.72; } applyNeedRelief(t, role === "medicine" ? { food: -2, health: -5, safety: -1 } : { food: -4, health: -1 }); return true; } if ((role === "drink" || role === "medicine") && isDuplicatorFoodSource(target) && foodType === "water") { const text = "\u8907\u88fd\u6a5f\u306e\u6c34\u6ef4\u3092\u98f2\u3093\u3067\u3044\u308b"; t.setActionState?.("seek_water", { target, reason: text, sleeping: false }); t.thought = text; t.applyWaterEffect?.(0.55, "water"); if ((world.time || 0) > (t.nextDrinkSound || -999)) { audio.play?.("sfx_drink", { category: "ops", minGap: 0.20 }); t.nextDrinkSound = (world.time || 0) + 0.72; } applyNeedRelief(t, role === "medicine" ? { food: -2, health: -5, safety: -1 } : { food: -4, health: -1 }); return true; } if (target.type === "ant_corpse" && (target.amount || 0) > 0) { const bite = Math.min(target.amount, rand(3.2, 5.4)); if (!(bite > 0)) return false; target.amount -= bite; finishBehaviorConsumption(t, target, "ant_corpse", bite, effectiveDt, "\u30a2\u30ea\u306e\u6b7b\u9ab8\u3092\u98df\u3079\u3066\u3044\u308b", "#4a3f36"); t.applyFoodHungerRelief ? t.applyFoodHungerRelief(bite * 0.62) : (t.hunger -= bite * 0.62); t.recoverHealth?.(bite * 0.12); applyNeedShock(t, { safety: bite * 0.12 }); t.makePoop?.(normalPoopUnitsFor("ant_corpse", bite)); return true; } if ((foodType === "sweet" || foodType === "food" || (isDuplicatorFoodSource(target) && foodType === "grass") || (isServingFoodType(foodType)) || (isParamEffectItemType(foodType))) && t.foodItemHasServingLeft?.(target)) { const eating = t.consumeFoodServing?.(target); if (!(eating > 0)) return false; const isParam = isParamEffectItemType(foodType); const hpOnlyMedicine = foodType === "first_aid"; const isSpecial = foodType === "love_mochi" || foodType === "fight_mochi" || foodType === "sleep_drug" || isParam; const color = ({ sweet: "#78b957", love_mochi: "#ff7bab", fight_mochi: "#e07c43", sleep_drug: "#b89cff", first_aid: "#d24852", sedative: "#5c8eae", protein: "#f28d3d", niteropu: "#6255a8", ammo: "#d0942f", laxative: "#788c3b", mercury: "#6e95a8", giant_drug: "#e47b3a", dwarf_drug: "#7773c8", zunda_juice: "#61b94f" })[foodType] || "#f1dfb7"; finishBehaviorConsumption(t, target, foodType, eating, effectiveDt, `${labelFor(foodType)}\u3092\u98df\u3079\u3066\u3044\u308b`, color); if (!hpOnlyMedicine) { const hungerRelief = t.foodHungerReliefFor?.(foodType || target.type, eating, foodType === "sweet" ? 0.75 : (isSpecial ? 0.82 : 0.95)) ?? eating * 0.8; t.applyFoodHungerRelief ? t.applyFoodHungerRelief(hungerRelief) : (t.hunger -= hungerRelief); t.recoverHealth?.(eating * (foodType === "sweet" ? 0.28 : (isSpecial ? 0.24 : 0.46))); t.affection += foodType === "sweet" ? eating * 0.03 : eating * 0.012; } if (isSpecial && t.applyParamItemEffect) t.applyParamItemEffect(foodType || target.type, target, { source: "eating", nutrition: eating, silentLog: foodType === "love_mochi" || foodType === "fight_mochi" || foodType === "sleep_drug" }); globalThis.TarinaiAchievements?.recordConsumableUse?.({ world, tarinai: t, item: target, type: foodType || target.type, source: "eating" }); if (foodType === "sweet") { applyNeedRelief(t, { food: -eating * 0.4, fulfill: -eating * 0.2 }); if (t.explosionDisease) t.recoverExplosionDisease?.("\u305a\u3093\u3060\u9905\u3067\u7206\u767a\u75c5\u304c\u6cbb\u3063\u305f"); if (t.fightDisease) t.recoverFightDisease?.("\u305a\u3093\u3060\u9905\u3067\u304d\u305a\u3064\u304d\u75c5\u304c\u6cbb\u3063\u305f"); } const fromDuplicator = isDuplicatorFoodSource(target); if (!hpOnlyMedicine) t.makePoop?.(normalPoopUnitsFor(foodType || target.type, eating)); if (fromDuplicator) { t.mealCooldownUntil = Math.max(t.mealCooldownUntil || 0, (world.time || 0) + 1.2); t.behaviorLockTimer = Math.max(0, Math.min(t.behaviorLockTimer || 0, 0.18)); } return true; } if (target.type === "grass" && (target.amount || 0) > 0) { const bite = window.TarinaiGrass.regress(target, 1) ?? 0; if (!(bite > 0)) return false; target.eatenAmount = (target.eatenAmount || 0) + bite; world.markTerrainDirty?.("grass-eaten-stage"); world.markItemBucketsDirty?.("grass-eaten-stage"); finishBehaviorConsumption(t, target, "grass", bite, effectiveDt, "\u8349\u3092\u98df\u3079\u3066\u3044\u308b", "#5b9d39"); t.applyFoodHungerRelief ? t.applyFoodHungerRelief(bite * 0.46) : (t.hunger -= bite * 0.46); t.recoverHealth?.(bite * 0.18); applyNeedRelief(t, { food: -2, fulfill: -1 }); t.makePoop?.(normalPoopUnitsFor("grass", bite)); return true; } if (target.type === "zunchi" && (target.amount || 0) > 0) { const foodNeed = Number(t.needRaw?.food ?? t.needs?.food ?? 0) || 0; if (zunchiEatingBlocked(t, target, world)) return false; if (!t.isZunchiSlave && hasBetterFoodThanZunchiNearby(t, world, 900)) return false; const servingScale = Number.isFinite(target.foodServingScale) ? target.foodServingScale : (target.toolSize === "large" ? 1.18 : (target.toolSize === "small" ? 0.82 : 1)); const bite = Math.min(target.amount, (t.isZunchiSlave ? rand(4.8, 7.4) : rand(3.4, 5.8)) * servingScale); if (!(bite > 0)) return false; target.amount -= bite; finishBehaviorConsumption(t, target, "zunchi", bite, effectiveDt, t.isZunchiSlave ? "\u305a\u3093\u3061\u3069\u308c\u3044\u306a\u306e\u3067\u305a\u3093\u3061\u3092\u98df\u3079\u3066\u3044\u308b" : "\u305a\u3093\u3061\u3092\u98df\u3079\u3066\u3044\u308b", "#6b8d3f"); t.applyFoodHungerRelief ? t.applyFoodHungerRelief(bite * (t.isZunchiSlave ? 0.66 : 0.18)) : (t.hunger -= bite * (t.isZunchiSlave ? 0.66 : 0.18)); t.recoverHealth?.(bite * (t.isZunchiSlave ? 0.10 : 0.02)); applyNeedShock(t, { health: bite * (t.isZunchiSlave ? 0.2 : 1.2), safety: bite * (t.isZunchiSlave ? 0.1 : 0.6) }); t.makePoop?.(normalPoopUnitsFor("zunchi", bite)); return true; } return false; } function behaviorFoodTarget(t, world, role = "food", maxDist = 860) { const best = findNearestItemWithRole(world, t, role, maxDist); if (role === "food") return best; const current = currentBehaviorTarget(t) || t.target; if (current && !current.dead && (roleMatches(current, role) || (role === "food" && current.type === "duplicator" && current.storedFoodType))) return current; return best; } function updateConsumableBehavior(t, world, dt, role = "food") { if (!t || !world) return false; const target = behaviorFoodTarget(t, world, role, role === "food" ? 900 : 700); if (!target || target.dead) return false; const state = role === "drink" ? "seek_water" : "seek_food"; const label = role === "drink" ? "\u6c34\u3092\u63a2\u3057\u3066\u3044\u308b" : (role === "medicine" ? "\u56de\u5fa9\u3067\u304d\u308b\u3082\u306e\u3092\u63a2\u3057\u3066\u3044\u308b" : "\u98df\u3079\u7269\u3092\u63a2\u3057\u3066\u3044\u308b"); const contactDistance = target.type === "duplicator" ? foodInteractionDistance(t, target) : dist(t, target); const contactReach = foodInteractionReach(t, target, role); if (contactDistance > contactReach) { const approachTarget = target.type === "duplicator" ? { x: foodInteractionPoint(target).x, y: foodInteractionPoint(target).y, id: `${target.id || "duplicator"}:dish`, dead: false, hostItem: target } : target; t.target = target; moveToOrUse(t, approachTarget, state, label); return true; } t.target = target; if (consumeBehaviorTarget(t, world, target, role, dt)) return "finished"; t.setActionState?.(state, { target, reason: label, sleeping: false }); return t.foodItemHasServingLeft?.(target) ? true : false; }