69 lines
3.9 KiB
JavaScript
69 lines
3.9 KiB
JavaScript
"use strict";
|
|
|
|
// Builds and finalizes Tarinai item-interaction scan context.
|
|
// This keeps scan radius, candidate collection, and post-interaction clamping in one place.
|
|
|
|
(function (global) {
|
|
function create(tarinai, dt = 0) {
|
|
if (!tarinai || !tarinai.world) return null;
|
|
const foodNeed = Number(tarinai.needRaw?.food ?? tarinai.needs?.food ?? 0) || 0;
|
|
const healthNeed = Number(tarinai.needRaw?.health ?? tarinai.needs?.health ?? 0) || 0;
|
|
const currentNeed = typeof getTarinaiBehaviorNeed === "function" ? getTarinaiBehaviorNeed(tarinai) : tarinai.behavior?.need;
|
|
const foodActionActive = currentNeed === "food" || ["seek_food", "eat", "seek_water"].includes(tarinai.state);
|
|
const healthActionActive = currentNeed === "health" || tarinai.state === "seek_food";
|
|
const mealReady = (tarinai.mealCooldownUntil || 0) <= (tarinai.world?.time || 0) || tarinai.hunger > 92 || foodNeed > 84;
|
|
const wantsFood = foodActionActive || foodNeed >= needThreshold("food", "start") || tarinai.hunger > 70;
|
|
const wantsMedicine = healthActionActive || healthNeed >= needThreshold("health", "start");
|
|
const canBite = !tarinai.isZunchiSlave && tarinai.eatCooldown <= 0 && mealReady && wantsFood && tarinai.hunger > 18;
|
|
const canSweetBite = !tarinai.isZunchiSlave && tarinai.eatCooldown <= 0 && mealReady && (wantsFood || wantsMedicine) && tarinai.hunger > 7;
|
|
const canZunchiBite = tarinai.eatCooldown <= 0 && (tarinai.isZunchiSlave ? tarinai.hunger > 16 : (tarinai.hunger >= 102 || foodNeed >= 92)) && (tarinai.isZunchiSlave || !hasBetterFoodThanZunchiNearby(tarinai, tarinai.world, 260));
|
|
const scanLimit = (tarinai.hunger >= 96 || foodNeed >= 84) ? 32 : 18;
|
|
const foodScanRadius = tarinai.radius + ((tarinai.hunger >= 96 || foodNeed >= 84) ? 104 : 56);
|
|
const nearbyFood = tarinai.world.nearbyFood?.(tarinai.x, tarinai.y, foodScanRadius, true) || [];
|
|
const nearbyItems = nearbyFood.length ? nearbyFood : tarinai.world.nearbyItems(tarinai.x, tarinai.y, foodScanRadius);
|
|
const foodCandidates = [];
|
|
if (tarinai.target && !tarinai.target.dead && (tarinai.target.type === "duplicator" || roleMatches(tarinai.target, "food") || roleMatches(tarinai.target, "medicine"))) foodCandidates.push(tarinai.target);
|
|
for (const it of nearbyItems) {
|
|
if (!it || it.dead || foodCandidates.includes(it)) continue;
|
|
if (!(it.type === "duplicator" || roleMatches(it, "food") || roleMatches(it, "medicine") || it.type === "water" || it.type === "water_bowl")) continue;
|
|
foodCandidates.push(it);
|
|
if (foodCandidates.length >= scanLimit) break;
|
|
}
|
|
if (foodCandidates.length > 1 && foodCandidates.length <= scanLimit) {
|
|
foodCandidates.sort((a, b) => foodPriorityScore(tarinai, b, b?.type === "duplicator" ? foodInteractionDistance(tarinai, b) : dist(tarinai, b)) - foodPriorityScore(tarinai, a, a?.type === "duplicator" ? foodInteractionDistance(tarinai, a) : dist(tarinai, a)));
|
|
}
|
|
return {
|
|
dt,
|
|
foodNeed,
|
|
healthNeed,
|
|
currentNeed,
|
|
foodActionActive,
|
|
healthActionActive,
|
|
mealReady,
|
|
wantsFood,
|
|
wantsMedicine,
|
|
canBite,
|
|
canSweetBite,
|
|
canZunchiBite,
|
|
scanLimit,
|
|
foodScanRadius,
|
|
nearbyFood,
|
|
nearbyItems,
|
|
foodCandidates,
|
|
};
|
|
}
|
|
|
|
function finalize(tarinai) {
|
|
if (!tarinai) return false;
|
|
tarinai.hunger = clamp(tarinai.hunger, 0, 115);
|
|
tarinai.energy = clamp(tarinai.energy, 0, typeof tarinaiMaxEnergy === "function" ? tarinaiMaxEnergy(tarinai) : (Number(tarinai.maxEnergy) || 100));
|
|
tarinai.stress = applyGroundStressModifier(tarinai, calculateStressFromNeeds(tarinai.needRaw || tarinai.needs || createDefaultNeeds()));
|
|
tarinai.loneliness = clamp(tarinai.loneliness, 0, 110);
|
|
return true;
|
|
}
|
|
|
|
global.TarinaiItemInteractionContext = Object.freeze({
|
|
create,
|
|
finalize,
|
|
});
|
|
})(typeof window !== "undefined" ? window : globalThis);
|