191 lines
12 KiB
JavaScript
191 lines
12 KiB
JavaScript
"use strict";
|
|
|
|
// Food, medicine, grass, ant-corpse, and zunchi eating branch for Tarinai item interaction.
|
|
|
|
(function (global) {
|
|
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;
|
|
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.");
|
|
}
|
|
tarinai.zunchiStain = clamp((tarinai.zunchiStain || 0) + touch * dt * (tarinai.isZunchiSlave ? 5.2 : 9.5), 0, 100);
|
|
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.");
|
|
tarinai.zunchiStain = clamp((tarinai.zunchiStain || 0) + bite * (tarinai.isZunchiSlave ? 0.75 : 1.75), 0, 100);
|
|
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);
|