"use strict"; // Food, medicine, grass, ant-corpse, and zunchi eating branch for Tarinai item interaction. (function (global) { const OCHIBI_DIRTY_SPEECHES = [ { id: "voice_ochibi_dirty_001", text: "くちゃいのだ" }, { id: "voice_ochibi_dirty_002", text: "ずんちがめだまについたのだ!" }, { id: "voice_ochibi_dirty_003", text: "おえええ!" }, { id: "voice_ochibi_dirty_004", text: "ずんちがおくちについたのだ!" } ]; function maybePlayOchibiDirtySpeech(tarinai, delta = 0) { if (!tarinai || !tarinai.isAcquiredTarinai || tarinai.dead) return false; if (!(delta > 0.01)) return false; const now = Number(tarinai.world?.time || 0) || 0; const nextAllowed = Number(tarinai.ochibiDirtySpeechCooldownUntil || 0) || 0; const lastLevel = Number(tarinai.ochibiDirtySpeechLastLevel || 0) || 0; const currentLevel = Number(tarinai.zunchiStain || 0) || 0; if (now < nextAllowed && currentLevel < lastLevel + 5) return false; if ((tarinai.nextBubbleAt || 0) > now) return false; const idx = Math.floor(Math.random() * OCHIBI_DIRTY_SPEECHES.length) % OCHIBI_DIRTY_SPEECHES.length; const line = OCHIBI_DIRTY_SPEECHES[idx]; if (!line) return false; const bubbled = tarinai.bubble?.(line.text, 4.8, "rgba(82,66,42,0.84)"); if (!bubbled) return false; const audio = global.TarinaiAudio; if (audio?.playSample) audio.playSample(line.id, 1.5, "voice") || audio.play?.(line.id, { category: "voice", minGap: 1.5 }); else if (audio?.play) audio.play(line.id, { category: "voice", minGap: 1.5 }); tarinai.ochibiDirtySpeechCooldownUntil = now + 3.2; tarinai.ochibiDirtySpeechLastLevel = currentLevel; return true; } function updateCandidate(tarinai, ctx, it, dt = 0) { if (!tarinai || tarinai.dead || !ctx || !it || it.dead) return false; if (it.type === "duplicator" && global.TarinaiSignalSystem?.powerOverride?.(it) === false) return false; const { foodNeed = 0, foodActionActive = false, canBite = false, canSweetBite = false, canZunchiBite = false } = ctx; const contact = global.TarinaiItemInteractionContext.contactForCandidate(tarinai, ctx, it, { role: "food" }); if (!contact) return false; const d = contact.distance; const foodType = it.type === "duplicator" ? (it.storedFoodType || "") : it.type; if (tarinai.isZunchiSlave && foodType === "sweet") return false; const duplicatorConsumable = it.type === "duplicator" && foodType && (foodType === "sweet" ? canSweetBite : (canBite || foodActionActive)) && !(isParamEffectItemType(foodType)) && foodType !== "sleep_drug" && foodType !== "love_mochi" && foodType !== "fight_mochi"; if (((foodType === "sweet" && canSweetBite) || (foodType === "food" && (canBite || foodActionActive)) || duplicatorConsumable) && tarinai.foodItemHasServingLeft(it)) { const eating = tarinai.consumeFoodServing(it); if (eating > 0) { const eatLabel = it.type === "duplicator" ? (it.storedFoodLabel || toolLabel(foodType || it.type)) : toolLabel(it.type); tarinai.beginEating(it, `${eatLabel}\u3092\u98df\u3079\u3066\u3044\u308b`); tarinai.vx *= Math.pow(0.25, dt * 60); tarinai.vy *= Math.pow(0.25, dt * 60); const hungerRelief = tarinai.foodHungerReliefFor(foodType || it.type, eating, foodType === "sweet" ? 0.75 : 0.95); tarinai.applyFoodHungerRelief ? tarinai.applyFoodHungerRelief(hungerRelief) : (tarinai.hunger -= hungerRelief); tarinai.recoverHealth(eating * (foodType === "sweet" ? 0.28 : 0.46)); tarinai.affection += foodType === "sweet" ? eating * 0.03 : eating * 0.012; if (foodType === "sweet") { applyNeedRelief(tarinai, { food: -eating * 0.4, fulfill: -eating * 0.2 }); tarinai.fearTimer = Math.max(0, tarinai.fearTimer - eating * 0.035); if (tarinai.explosionDisease) tarinai.recoverExplosionDisease("\u305a\u3093\u3060\u9905\u3067\u7206\u767a\u75c5\u304c\u6cbb\u3063\u305f"); if (tarinai.fightDisease) tarinai.recoverFightDisease("\u305a\u3093\u3060\u9905\u3067\u304d\u305a\u3064\u304d\u75c5\u304c\u6cbb\u3063\u305f"); } tarinai.eatTimer = Math.max(tarinai.eatTimer, 1.0); tarinai.eatCooldown = rand(0.96, 1.12); tarinai.bubble("\u3082\u3050", 2.0, "rgba(88,70,42,0.78)"); tarinai.foodReactTimer = Math.max(tarinai.foodReactTimer, 0.16); if (Math.random() < dt * 2.2) { const mouth = tarinai.mouthPosition(it); tarinai.world.spawnEatEffect(mouth.x, mouth.y, foodType === "sweet" ? "#78b957" : "#f1dfb7"); } tarinai.lastMealColor = foodType === "sweet" ? "#78b957" : "#f1dfb7"; const fromDuplicator = isDuplicatorFoodSource(it); tarinai.makePoop(normalPoopUnitsFor(foodType || it.type, eating)); if (fromDuplicator) tarinai.mealCooldownUntil = Math.max(tarinai.mealCooldownUntil || 0, tarinai.world.time + 1.2); if (tarinai.world.time > tarinai.nextEatSound) { audio.eat(); tarinai.nextEatSound = tarinai.world.time + 0.42; } if (tarinai.world.time - tarinai.lastLog > 9 && Math.random() < 0.014) { tarinai.world.log(`${tarinai.name}\u306f${it.type === "sweet" ? "\u305a\u3093\u3060\u9905" : "\u98df\u3079\u7269"}\u3092\u3057\u3070\u3089\u304f\u5473\u308f\u3063\u305f\u3002`, null, { participants: [tarinai] }); tarinai.lastLog = tarinai.world.time; } return true; } } if (canSweetBite && (foodType === "love_mochi" || foodType === "fight_mochi" || foodType === "sleep_drug" || (isParamEffectItemType(foodType))) && tarinai.foodItemHasServingLeft(it)) { const eating = tarinai.consumeFoodServing(it); if (eating <= 0) return false; const eatLabel = it.type === "duplicator" ? (it.storedFoodLabel || toolLabel(foodType || it.type)) : toolLabel(it.type); tarinai.beginEating(it, `${eatLabel}\u3092\u98df\u3079\u3066\u3044\u308b`); tarinai.vx *= Math.pow(0.25, dt * 60); tarinai.vy *= Math.pow(0.25, dt * 60); const hpOnlyMedicine = foodType === "first_aid"; if (!hpOnlyMedicine) { const hungerRelief = tarinai.foodHungerReliefFor(foodType || it.type, eating, 0.82); tarinai.applyFoodHungerRelief ? tarinai.applyFoodHungerRelief(hungerRelief) : (tarinai.hunger -= hungerRelief); tarinai.recoverHealth(eating * 0.24); tarinai.affection += eating * 0.04; } const mealColors = { 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", }; const mealBubbles = { love_mochi: ["\u2661", 2.4, "rgba(190,82,132,0.78)"], fight_mochi: ["!", 2.0, "rgba(180,86,52,0.80)"], sleep_drug: ["Zzz...", 2.3, "rgba(104,84,168,0.78)"], }; if (tarinai.applyParamItemEffect) tarinai.applyParamItemEffect(foodType || it.type, it, { source: "eating", nutrition: eating, silentLog: foodType === "love_mochi" || foodType === "fight_mochi" || foodType === "sleep_drug", }); if (mealBubbles[foodType]) tarinai.bubble(...mealBubbles[foodType]); tarinai.lastMealColor = mealColors[foodType] || "#41aa66"; tarinai.eatTimer = Math.max(tarinai.eatTimer, 1.0); tarinai.eatCooldown = rand(0.96, 1.12); tarinai.foodReactTimer = Math.max(tarinai.foodReactTimer, 0.16); if (Math.random() < dt * 2.2) { const mouth = tarinai.mouthPosition(it); tarinai.world.spawnEatEffect(mouth.x, mouth.y, foodType === "love_mochi" ? "#ff7bab" : (foodType === "fight_mochi" ? "#e07c43" : (foodType === "sleep_drug" ? "#b89cff" : (tarinai.lastMealColor || "#41aa66")))); } const fromDuplicator = isDuplicatorFoodSource(it); if (!hpOnlyMedicine) tarinai.makePoop(normalPoopUnitsFor(foodType || it.type, eating)); if (fromDuplicator) tarinai.mealCooldownUntil = Math.max(tarinai.mealCooldownUntil || 0, tarinai.world.time + 1.2); if (tarinai.world.time > tarinai.nextEatSound) { audio.eat(); tarinai.nextEatSound = tarinai.world.time + 0.42; } if (tarinai.world.time - tarinai.lastLog > 9 && Math.random() < 0.02) { tarinai.world.log(`${tarinai.name}\u306f${toolLabel(foodType || it.type)}\u3092\u3057\u3070\u3089\u304f\u5473\u308f\u3063\u305f\u3002`, "food", { participants: [tarinai] }); tarinai.lastLog = tarinai.world.time; } return true; } if (canBite && it.type === "grass") { const servingScale = Number.isFinite(it.foodServingScale) ? it.foodServingScale : (it.toolSize === "large" ? 1.18 : (it.toolSize === "small" ? 0.82 : 1)); const bite = Math.min(it.amount, rand(4.2, 7.2) * servingScale); tarinai.beginEating(it, "\u8349\u3092\u98df\u3079\u3066\u3044\u308b"); tarinai.grassEatTimer = Math.max(tarinai.grassEatTimer, 1.0); tarinai.vx *= Math.pow(0.20, dt * 60); tarinai.vy *= Math.pow(0.20, dt * 60); it.amount -= bite; it.eatenAmount = (it.eatenAmount || 0) + bite; it.growth = clamp((it.growth ?? it.amount / 120) - bite / 150, 0.04, 1.2); tarinai.applyFoodHungerRelief ? tarinai.applyFoodHungerRelief(bite * 0.46) : (tarinai.hunger -= bite * 0.46); tarinai.recoverHealth(bite * 0.18); applyNeedRelief(tarinai, { food: -dt * 2, fulfill: -dt * 1 }); tarinai.eatTimer = Math.max(tarinai.eatTimer, 1.0); tarinai.eatCooldown = rand(0.96, 1.14); tarinai.bubble("\u3082\u3050", 2.4, "rgba(72,96,48,0.78)"); if (Math.random() < dt * 2.0) { const mouth = tarinai.mouthPosition(it); tarinai.world.spawnEatEffect(mouth.x, mouth.y, "#5b9d39"); } if (tarinai.world.time > tarinai.nextEatSound) { audio.eat(); tarinai.nextEatSound = tarinai.world.time + 0.55; } tarinai.makePoop(normalPoopUnitsFor("grass", bite)); if (tarinai.type === "jito") tarinai.affection += dt * 0.25; return true; } if (canBite && it.type === "ant_corpse") { const bite = Math.min(it.amount, rand(3.2, 5.4)); if (bite > 0) { tarinai.beginEating(it, "\u30a2\u30ea\u306e\u6b7b\u9ab8\u3092\u98df\u3079\u3066\u3044\u308b"); tarinai.vx *= Math.pow(0.18, dt * 60); tarinai.vy *= Math.pow(0.18, dt * 60); it.amount -= bite; tarinai.applyFoodHungerRelief ? tarinai.applyFoodHungerRelief(bite * 0.62) : (tarinai.hunger -= bite * 0.62); tarinai.recoverHealth(bite * 0.12); applyNeedShock(tarinai, { safety: bite * 0.12 }); tarinai.eatTimer = Math.max(tarinai.eatTimer, 1.0); tarinai.eatCooldown = rand(0.96, 1.16); tarinai.bubble("\u3082\u3050", 2.2, "rgba(72,60,48,0.78)"); const mouth = tarinai.mouthPosition(it); tarinai.world.spawnEatEffect(mouth.x, mouth.y, "#4a3f36"); tarinai.lastMealColor = "#4a3f36"; tarinai.makePoop(normalPoopUnitsFor("ant_corpse", bite)); if (tarinai.world.time > tarinai.nextEatSound) { audio.eat(); tarinai.nextEatSound = tarinai.world.time + 0.62; } return true; } } if (it.type === "zunchi") { const touch = clamp(1 - d / (tarinai.radius + it.r + 12), 0, 1); if (!tarinai.isZunchiSlave) { applyNeedShock(tarinai, { health: touch * dt * 7, safety: touch * dt * 4 }); if (touch > 0.08) tarinai.adjustPersonality("neuroticism", -dt * touch * 0.0045, "after repeated contact with poop."); } { const __prev = Number(tarinai.zunchiStain || 0) || 0; tarinai.zunchiStain = clamp((tarinai.zunchiStain || 0) + touch * dt * (tarinai.isZunchiSlave ? 5.2 : 9.5), 0, 100); maybePlayOchibiDirtySpeech(tarinai, (Number(tarinai.zunchiStain || 0) || 0) - __prev); } const emergencyHunger = (tarinai.hunger || 0) >= 104 || foodNeed >= 94; const zunchiTooFresh = zunchiEatingBlocked(tarinai, it, tarinai.world) || (!emergencyHunger && !tarinai.isZunchiSlave && it.stage === "fresh" && (it.age || 0) < 150 && foodNeed < 94 && (tarinai.hunger || 0) < 104); if (canZunchiBite && !zunchiTooFresh) { const servingScale = Number.isFinite(it.foodServingScale) ? it.foodServingScale : (it.toolSize === "large" ? 1.18 : (it.toolSize === "small" ? 0.82 : 1)); const bite = Math.min(it.amount, (tarinai.isZunchiSlave ? rand(4.8, 7.4) : rand(3.4, 5.8)) * servingScale); tarinai.beginEating(it, tarinai.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"); tarinai.vx *= Math.pow(0.18, dt * 60); tarinai.vy *= Math.pow(0.18, dt * 60); it.amount -= bite; tarinai.applyFoodHungerRelief ? tarinai.applyFoodHungerRelief(bite * (tarinai.isZunchiSlave ? 0.66 : 0.18)) : (tarinai.hunger -= bite * (tarinai.isZunchiSlave ? 0.66 : 0.18)); tarinai.recoverHealth(bite * (tarinai.isZunchiSlave ? 0.10 : 0.02)); applyNeedShock(tarinai, { health: bite * (tarinai.isZunchiSlave ? 0.2 : 1.2), safety: bite * (tarinai.isZunchiSlave ? 0.1 : 0.6) }); if (!tarinai.isZunchiSlave) tarinai.adjustPersonality("neuroticism", -bite * 0.0015, "after repeated contact with poop."); { const __prev = Number(tarinai.zunchiStain || 0) || 0; tarinai.zunchiStain = clamp((tarinai.zunchiStain || 0) + bite * (tarinai.isZunchiSlave ? 0.75 : 1.75), 0, 100); maybePlayOchibiDirtySpeech(tarinai, (Number(tarinai.zunchiStain || 0) || 0) - __prev); } tarinai.lastMealColor = "#6b8d3f"; tarinai.eatTimer = Math.max(tarinai.eatTimer, 1.0); tarinai.eatCooldown = rand(1.0, 1.22); tarinai.bubble("\u3082\u3050", 2.2, "rgba(74,91,50,0.78)"); const mouth = tarinai.mouthPosition(it); tarinai.world.spawnEatEffect(mouth.x, mouth.y, "#6b8d3f"); if (tarinai.world.time > tarinai.nextEatSound) { audio.eat(); tarinai.nextEatSound = tarinai.world.time + 0.7; } return true; } } return false; } global.TarinaiFoodInteractionSystem = Object.freeze({ updateCandidate, }); })(typeof window !== "undefined" ? window : globalThis);