This commit is contained in:
33333-33333 2026-06-29 21:02:04 +09:00
commit e00bfd8879
51 changed files with 1203 additions and 833 deletions

View file

@ -101,6 +101,7 @@ function hasBetterFoodThanZunchiNearby(tarinai, world, maxDist = 180) {
for (const it of world.nearbyItems?.(tarinai.x, tarinai.y, maxDist) || world.items || []) {
if (!it || it.dead || it.type === "zunchi") continue;
if (tarinai.shouldAvoidTarget?.(it)) continue;
if (tarinaiTargetBlockedByObstacle(world, tarinai, it, "food")) continue;
const type = effectiveFoodTypeForItem(it);
if (!type) continue;
if (["sweet", "food", "grass", "water", "zunda_juice", "ant_corpse"].includes(type) || (isServingFoodType(type))) {
@ -130,25 +131,48 @@ function zunchiEatingBlocked(tarinai, item, world) {
return false;
}
function tarinaiTargetBlockedByObstacle(world, tarinai, item, role = "") {
if (!world || !tarinai || !item) return false;
// ずんだ餅だけは障害物越しでも食べに行く。
if ((role === "food" || role === "medicine") && effectiveFoodTypeForItem(item) === "sweet") return false;
return Boolean(world.targetBlockedByObstacle?.(tarinai, item, Math.max(18, (tarinai.radius || 20) * 0.65)));
}
function foodRoleSearchTypes(role = "") {
const set = new Set();
if (role === "food") {
for (const type of ["food", "sweet", "love_mochi", "fight_mochi", "sleep_drug", "laxative", "protein", "niteropu", "ammo", "mystery_drug", "mercury", "giant_drug", "dwarf_drug", "grass", "zunchi", "ant_corpse", "zunda_juice", "duplicator"]) set.add(type);
if (typeof FOOD_SERVING_TYPES !== "undefined") for (const type of FOOD_SERVING_TYPES) set.add(type);
} else if (role === "drink") {
for (const type of ["water", "zunda_juice", "duplicator"]) set.add(type);
} else if (role === "medicine") {
for (const type of ["sweet", "water", "zunda_juice", "sleep_drug", "love_mochi", "fight_mochi", "laxative", "protein", "niteropu", "ammo", "mystery_drug", "mercury", "giant_drug", "dwarf_drug", "duplicator"]) set.add(type);
if (typeof PARAM_EFFECT_ITEM_TYPES !== "undefined") for (const type of PARAM_EFFECT_ITEM_TYPES) set.add(type);
}
return set;
}
function findNearestItemWithRole(world, tarinai, role, maxDist = 520) {
let best = null, bestScore = Infinity;
const hunger = Number(tarinai?.hunger || 0) || 0;
const foodNeed = Number(tarinai?.needRaw?.food ?? tarinai?.needs?.food ?? 0) || 0;
const foodEmergency = role === "food" && (hunger >= 96 || foodNeed >= 84);
const searchDist = foodEmergency ? Math.max(maxDist, 1180) : maxDist;
const primary = ((role === "food" || role === "drink" || role === "medicine") && world?.nearbyFood)
const searchDist = Math.max(1, Number(maxDist) || 520);
const foodLikeRole = role === "food" || role === "drink" || role === "medicine";
const primary = (foodLikeRole && world?.nearbyFood)
? world.nearbyFood(tarinai.x, tarinai.y, searchDist, true)
: (world?.nearbyItems?.(tarinai.x, tarinai.y, searchDist, true) || world?.items || []);
const candidates = Array.isArray(primary) ? primary.slice() : [];
if (foodEmergency && world?.itemsOfType) {
const emergencyTypes = new Set(["food", "sweet", "love_mochi", "fight_mochi", "sleep_drug", "laxative", "protein", "niteropu", "ammo", "mystery_drug", "mercury", "giant_drug", "dwarf_drug", "grass", "zunchi", "ant_corpse", "zunda_juice", "duplicator"]);
if (typeof FOOD_SERVING_TYPES !== "undefined") for (const type of FOOD_SERVING_TYPES) emergencyTypes.add(type);
for (const type of emergencyTypes) {
for (const it of world.itemsOfType(type) || []) {
if (it && !it.dead && !candidates.includes(it)) candidates.push(it);
}
const addCandidate = (it) => {
if (it && !it.dead && !candidates.includes(it)) candidates.push(it);
};
// 食料・水・薬は、通常時から空間インデックスだけに依存しない。
// 生成直後/移動直後のアイテムが nearbyFood のキャッシュから漏れても、距離と roleMatches で最終判定する。
if (foodLikeRole) {
if (Array.isArray(world?.items)) for (const it of world.items) addCandidate(it);
if (world?.itemsOfType) {
for (const type of foodRoleSearchTypes(role)) for (const it of world.itemsOfType(type) || []) addCandidate(it);
}
}
for (const item of candidates) {
if (!roleMatches(item, role)) continue;
if (role === "food" && typeof tarinai?.foodItemHasServingLeft === "function") {
@ -160,11 +184,16 @@ function findNearestItemWithRole(world, tarinai, role, maxDist = 520) {
if (tarinai?.shouldAvoidTarget?.(item)) continue;
const d = item.type === "duplicator" ? foodInteractionDistance(tarinai, item) : dist(tarinai, item);
if (d > searchDist) continue;
const blocked = tarinaiTargetBlockedByObstacle(world, tarinai, item, role);
let score = d;
if (role === "food" || role === "medicine") {
const priority = foodPriorityScore(tarinai, item, d);
if (!Number.isFinite(priority)) continue;
score = -priority;
score = -priority + (blocked ? 260 : 0);
} else if (blocked && !foodLikeRole) {
continue;
} else if (blocked) {
score += 180;
}
if (score < bestScore) { best = item; bestScore = score; }
}
@ -295,6 +324,7 @@ function findOwnedStructure(world, tarinai, type = "", maxDist = Infinity) {
for (const item of world?.items || []) {
if (!item?.isStructure || item.dead || item.ownerId !== tarinai.id) continue;
if (type && item.type !== type) continue;
if (tarinaiTargetBlockedByObstacle(world, tarinai, item, "structure")) continue;
const d = dist(tarinai, item);
if (d < bestD) { best = item; bestD = d; }
}
@ -307,6 +337,7 @@ function findUnownedStructure(world, tarinai, type = "", maxDist = Infinity) {
if (!item?.isStructure || item.dead || item.ownerId) continue;
if (type && item.type !== type) continue;
if (tarinai?.shouldAvoidTarget?.(item)) continue;
if (tarinaiTargetBlockedByObstacle(world, tarinai, item, "structure")) continue;
const d = dist(tarinai, item);
if (d < bestD) { best = item; bestD = d; }
}