tarinai/js/tarinai_action_definitions.js

542 lines
22 KiB
JavaScript
Raw Normal View History

2026-06-25 14:51:58 +09:00
"use strict";
// ActionSpec factories and TARINAI_ACTIONS registry.
function selectRoleTarget(role, range) {
return function selectTarget(tarinai, world) {
return findNearestItemWithRole(world, tarinai, role, range);
};
}
2026-06-28 13:40:41 +09:00
function startMoveToTarget(state, fallbackLabel = "\u884c\u52d5\u3057\u3066\u3044\u308b") {
2026-06-25 14:51:58 +09:00
return function start(tarinai, world, ctx = {}) {
const target = ctx.target ?? this.selectTarget?.(tarinai, world, ctx) ?? null;
return moveToOrUse(tarinai, target, state, this.label || fallbackLabel);
};
}
function selectOwnedStructure(type, range = Infinity) {
return function selectTarget(tarinai, world) {
return findOwnedStructure(world, tarinai, type, range);
};
}
function selectMateTarget(tarinai, world, range = 440) {
return world?.nearestOther?.(tarinai, range, other => !world.areParentChild?.(tarinai, other) && !!tarinai.isZunchiSlave === !!other.isZunchiSlave && !other.sleepDisease && !other.fightDisease) || null;
}
function basicCanStartWithTarget(t, world, ctx = {}) {
return !!(ctx.target ?? this.selectTarget?.(t, world, ctx));
}
function createConsumableActionSpec({ id, subNeed = "", state, label, phrase, weight, role, range }) {
return {
id,
need: role === "medicine" ? "health" : "food",
subNeed,
state,
label,
phrase,
weight,
timerBacked: true,
selectTarget: selectRoleTarget(role, range),
canStart: basicCanStartWithTarget,
start: startMoveToTarget(state, label),
tick(t, world, dt) { return updateConsumableBehavior(t, world, dt, role); },
finish(t) {
if (role === "drink" && typeof applyNeedSatisfaction === "function") applyNeedSatisfaction(t, { food: 8, health: 2 }, "drink");
return true;
},
fail(t) {
if (t) t.target = null;
return false;
},
};
}
function createSleepInBedActionSpec() {
return {
id: "sleep_in_bed",
need: "sleep",
state: "seek_bed",
2026-06-28 13:40:41 +09:00
label: "\u5bdd\u308b\u5834\u6240\u3092\u63a2\u3057\u3066\u3044\u308b",
phrase: "\u5bdd\u308b\u5834\u6240\u3078\u623b\u3063\u305f",
2026-06-25 14:51:58 +09:00
weight: 42,
timerBacked: true,
selectTarget(t, world) { return findNearestSleepPlace(world, t, 760); },
canStart: basicCanStartWithTarget,
start(t, world, ctx = {}) {
const bed = ctx.target ?? this.selectTarget(t, world, ctx);
if (!bed) return false;
if (dist(t, bed) > (t.radius || 20) + 18) return moveToOrUse(t, bed, "seek_bed", this.label);
const isEasyBed = bed.type === "grass_bed";
bed.use?.(t, world, isEasyBed ? { purpose: "sleep" } : undefined);
2026-06-28 13:40:41 +09:00
t.startSleeping?.(bed, bed.type === "nest_box" ? "\u5de3\u7bb1\u306e\u4e2d\u3067\u5bdd\u3066\u3044\u308b" : (isEasyBed ? "\u304b\u3093\u305f\u3093\u30d9\u30c3\u30c9\u3067\u5bdd\u3066\u3044\u308b" : "\u30d9\u30c3\u30c9\u3067\u5bdd\u3066\u3044\u308b"));
2026-06-25 14:51:58 +09:00
return true;
},
tick(t) {
if (t?.state === "sleep" || t?.sleeping) return true;
return basicCanStartWithTarget.call(this, t, t?.world || null, {}) ? true : false;
},
};
}
function createSleepBuildGrassBedActionSpec() {
return {
id: "sleep_build_grass_bed",
need: "sleep",
subNeed: "bed",
state: "build",
2026-06-28 13:40:41 +09:00
label: "\u5bdd\u308b\u5834\u6240\u304c\u306a\u3044\u306e\u3067\u304b\u3093\u305f\u3093\u30d9\u30c3\u30c9\u3092\u4f5c\u3063\u3066\u3044\u308b",
phrase: "\u304b\u3093\u305f\u3093\u30d9\u30c3\u30c9\u3092\u4f5c\u3063\u305f",
2026-06-25 14:51:58 +09:00
weight: 18,
timerBacked: true,
2026-06-28 23:07:40 +09:00
selectTarget(t, world) { return (typeof findNearbyBuildMaterial === "function" ? findNearbyBuildMaterial(world, t, "grass_bed", 520) : null) || findNearbyMaterial(world, t, "grassMaterial", 520); },
2026-06-25 14:51:58 +09:00
canStart(t, world, ctx = {}) {
2026-06-28 23:07:40 +09:00
const higherPriorityPlace = typeof findPrioritySleepPlaceBeforeGrassBedBuild === "function"
? findPrioritySleepPlaceBeforeGrassBedBuild(world, t, 760)
: findNearestSleepPlace(world, t, 760);
2026-06-25 14:51:58 +09:00
return canBuildOwnedStructureByAge(t)
2026-06-28 23:07:40 +09:00
&& !higherPriorityPlace
2026-06-25 14:51:58 +09:00
&& !findOwnedStructure(world, t, "grass_bed")
&& !!(ctx.target ?? this.selectTarget(t, world, ctx))
2026-06-28 23:07:40 +09:00
&& !(Number(t?.actionIdCooldowns?.sleep_build_grass_bed || 0) > 0.01)
2026-06-25 14:51:58 +09:00
&& !["fight", "panic", "intimidate", "birth_ritual"].includes(t.state);
},
start(t, world) { return buildOwnedStructureFromGrass(t, world, "grass_bed", this.label); },
tick(t, world, dt) {
if (findOwnedStructure(world, t, "grass_bed")) return "finished";
return continueBuildPlan(t, world, dt) ? true : false;
},
};
}
function createSleepAnywhereActionSpec() {
return {
id: "sleep_anywhere",
need: "sleep",
state: "sleep",
2026-06-28 13:40:41 +09:00
label: "\u306a\u306b\u3082\u306a\u3044\u6240\u3067\u5bdd\u3066\u3044\u308b",
phrase: "\u305d\u306e\u307e\u307e\u5bdd\u305f",
2026-06-25 14:51:58 +09:00
weight: 8,
timerBacked: true,
canStart(t, world) {
if (findNearestSleepPlace(world, t, 760)) {
if (t) t.noBedNoBuildSince = NaN;
return false;
}
const now = world?.time || 0;
const failedRecently = (t?.lastFailedGrassBedBuildAt || -999) + 14 > now;
const buildTarget = this.selectBuildTarget ? this.selectBuildTarget(t, world, {}) : findNearbyMaterial(world, t, "grassMaterial", 520);
const canTryBuild = canBuildOwnedStructureByAge(t) && !!buildTarget && !(Number(t?.actionIdCooldowns?.sleep_build_grass_bed || 0) > 0.01);
if (canTryBuild && !failedRecently) {
if (t) t.noBedNoBuildSince = NaN;
return false;
}
if (t && !Number.isFinite(t.noBedNoBuildSince)) t.noBedNoBuildSince = now;
const since = Number.isFinite(t?.noBedNoBuildSince) ? t.noBedNoBuildSince : now;
const waited = now - since;
const urgent = (t?.energy || 100) < 18 || (t?.needRaw?.sleep || t?.needs?.sleep || 0) >= 92;
return failedRecently || urgent || waited >= 6.0;
},
selectBuildTarget: selectRoleTarget("grassMaterial", 520),
start(t) { t.startSleeping?.(null, this.label); return true; },
tick(t) { return (t?.state === "sleep" || t?.sleeping) ? true : false; },
};
}
function createRestActionSpec() {
return {
id: "rest_to_recover",
need: "health",
state: "idle",
2026-06-28 13:40:41 +09:00
label: "\u5143\u6c17\u306b\u306a\u308b\u307e\u3067\u4f11\u3093\u3067\u3044\u308b",
phrase: "\u4f11\u3093\u3060",
2026-06-25 14:51:58 +09:00
weight: 24,
canStart() { return true; },
start(t) {
t.setActionState?.("idle", { target: null, reason: this.label, sleeping: false });
2026-06-26 12:46:32 +09:00
t.energy = clamp((t.energy || 0) + 1.6, 0, typeof tarinaiMaxEnergy === "function" ? tarinaiMaxEnergy(t) : (Number(t.maxEnergy) || 100));
2026-06-25 14:51:58 +09:00
return true;
},
tick(t) {
2026-06-26 12:46:32 +09:00
t.energy = clamp((t.energy || 0) + 0.8, 0, typeof tarinaiMaxEnergy === "function" ? tarinaiMaxEnergy(t) : (Number(t.maxEnergy) || 100));
2026-06-25 14:51:58 +09:00
return true;
},
};
}
function createSunbathActionSpec() {
return {
id: "sunbath",
need: "health",
subNeed: "sunbath",
state: "sunbath",
2026-06-28 13:40:41 +09:00
label: "\u65e5\u5149\u6d74\u3057\u3066\u3044\u308b",
phrase: "\u65e5\u5149\u6d74\u3057\u3066\u3044\u308b",
2026-06-25 14:51:58 +09:00
weight: 32,
timerBacked: true,
canStart(t, world, ctx = {}) {
const healthNeed = Number(t?.needRaw?.health ?? t?.needs?.health ?? 0) || 0;
return (ctx.leisure || healthNeed >= needThreshold("health", "start"))
&& !!t?.canStartSunbath?.({ leisure: !!ctx.leisure });
},
start(t, world, ctx = {}) {
return !!t?.startSunbath?.({ leisure: !!ctx.leisure });
},
tick(t) {
return (t?.sunbathTimer || 0) > 0.04 ? true : "finished";
},
finish(t) {
if ((t?.sunbathTimer || 0) <= 0 && t?.state === "sunbath") t.finishSunbath?.();
return true;
},
fail(t) {
if (t && t.state === "sunbath") {
t.sunbathTimer = 0;
2026-06-28 13:40:41 +09:00
t.goIdle?.("\u65e5\u5149\u6d74\u3092\u3084\u3081\u305f");
2026-06-25 14:51:58 +09:00
}
return false;
},
};
}
function createWanderActionSpec() {
function targetFor(t, world) {
const radius = rand(80, 180);
return {
x: clamp(t.x + Math.cos(t.wanderAngle) * radius, t.radius || 20, (world?.w || 1600) - (t.radius || 20)),
y: clamp(t.y + Math.sin(t.wanderAngle) * radius, t.radius || 20, (world?.h || 900) - (t.radius || 20)),
dead: false,
detour: true,
};
}
return {
id: "wander_lightly",
need: "fulfill",
state: "wander",
2026-06-28 13:40:41 +09:00
label: "\u5c11\u3057\u6b69\u3044\u3066\u3044\u308b",
phrase: "\u5c11\u3057\u6b69\u3044\u305f",
2026-06-25 14:51:58 +09:00
weight: 14,
canStart() { return true; },
selectTarget: targetFor,
start(t, world, ctx = {}) {
t.wanderAngle += rand(-1.2, 1.2);
const target = ctx.target && Number.isFinite(ctx.target.x) ? ctx.target : targetFor(t, world);
t.wanderTarget = target;
t.setActionState?.("wander", { target, reason: this.label, sleeping: false });
return true;
},
tick(t, world) {
const target = t?.wanderTarget || currentBehaviorTarget(t) || null;
if (!target || !Number.isFinite(target.x) || dist(t, target) <= Math.max(28, (t.radius || 20) + 10)) return "finished";
moveToOrUse(t, target, "wander", this.label);
return true;
},
};
}
function selectFightRivalTarget(tarinai, world, range = 360) {
const candidate = conflictTargetFor(tarinai, world, (tarinai?.fightMochiTimer || 0) > 0.04 || isCurrentBehaviorForced(tarinai) ? 18 : 34);
const current = currentBehaviorTarget(tarinai);
return isLiveTarinaiEntity(candidate?.target)
? candidate.target
: (isLiveTarinaiEntity(current) ? current : nearestForcedFightTarget(tarinai, world, isCurrentBehaviorForced(tarinai) ? 760 : range));
}
function createPanicEscapeActionSpec() {
return {
id: "panic_escape",
need: "safety",
subNeed: "panic",
state: "panic",
2026-06-28 13:40:41 +09:00
label: "\u6016\u304f\u3066\u9003\u3052\u3066\u3044\u308b",
phrase: "\u9003\u3052\u305f",
2026-06-25 14:51:58 +09:00
weight: 62,
timerBacked: true,
selectTarget(t, world) {
return t.lastNeedShockBreaker || findNearbyDanger(world, t, 240) || ((t.defeatedById && world?.tarinai) ? world.liveTarinaiById?.(t.defeatedById) : null);
},
canStart(t, world, ctx = {}) {
const threat = ctx.target ?? this.selectTarget(t, world, ctx);
const safety = Number(t?.needRaw?.safety ?? t?.needs?.safety ?? 0) || 0;
// Do not start panic from ordinary safety pressure alone. Panic needs a
// concrete trigger, lingering fear, defeat, or an extreme safety spike.
return !!threat
|| (t?.defeatedTimer || 0) > 0.04
|| (safety >= 92 && !!activePanicBreaker(t, world, 260));
},
start(t, world, ctx = {}) {
const threat = ctx.target ?? this.selectTarget(t, world, ctx);
t.setActionState?.("panic", { target: t.panicDestination?.(threat, true), reason: this.label, wake: true, sleeping: false });
t.fearTimer = Math.max(t.fearTimer || 0, 1.6);
return true;
},
tick: updatePanicBehavior,
};
}
function createApproachFriendActionSpec() {
return {
id: "approach_friend",
need: "social",
subNeed: "bond",
state: "seek_friend",
2026-06-28 13:40:41 +09:00
label: "\u4ef2\u9593\u306b\u8fd1\u3065\u3044\u3066\u3044\u308b",
phrase: "\u4ef2\u9593\u306b\u8fd1\u3065\u3044\u305f",
2026-06-25 14:51:58 +09:00
weight: 46,
selectTarget(t, world) { return t.bestFriendLive?.(FRIEND_AFFINITY_THRESHOLD, 520) || world.nearestOther?.(t, 420) || null; },
canStart: basicCanStartWithTarget,
2026-06-28 13:40:41 +09:00
start: startMoveToTarget("seek_friend", "\u4ef2\u9593\u306b\u8fd1\u3065\u3044\u3066\u3044\u308b"),
2026-06-25 14:51:58 +09:00
tick(t, world, dt) { return updateSocialContactBehavior(t, world, dt, "bond"); },
};
}
function createApproachFamilyActionSpec() {
return {
id: "approach_parent_or_child",
need: "social",
subNeed: "family",
state: "follow_parent",
2026-06-28 13:40:41 +09:00
label: "\u5bb6\u65cf\u306b\u8fd1\u3065\u3044\u3066\u3044\u308b",
phrase: "\u5bb6\u65cf\u306b\u8fd1\u3065\u3044\u305f",
2026-06-25 14:51:58 +09:00
weight: 28,
selectTarget(t) { return t.parentToFollow?.() || null; },
canStart: basicCanStartWithTarget,
2026-06-28 13:40:41 +09:00
start: startMoveToTarget("follow_parent", "\u5bb6\u65cf\u306b\u8fd1\u3065\u3044\u3066\u3044\u308b"),
2026-06-25 14:51:58 +09:00
tick(t, world, dt) { return updateSocialContactBehavior(t, world, dt, "family"); },
};
}
function createApproachMateActionSpec() {
return {
id: "approach_mate",
need: "social",
subNeed: "mate",
state: "seek_friend",
2026-06-28 13:40:41 +09:00
label: "\u7e41\u6b96\u3067\u304d\u308b\u76f8\u624b\u306b\u8fd1\u3065\u3044\u3066\u3044\u308b",
phrase: "\u7e41\u6b96\u3067\u304d\u308b\u76f8\u624b\u306b\u8fd1\u3065\u3044\u3066\u3044\u308b",
2026-06-25 14:51:58 +09:00
weight: 48,
selectTarget(t, world) { return selectMateTarget(t, world, 520); },
canStart(t, world, ctx = {}) { return (t.reproductionTimer || 0) <= 12 && !!(ctx.target ?? this.selectTarget(t, world, ctx)); },
start(t, world, ctx = {}) {
const other = ctx.target ?? this.selectTarget(t, world, ctx);
if (!other) return false;
if (dist(t, other) <= Math.max(48, (t.radius || 20) + (other.radius || 20) + 12) && world.startBirthRitual?.(t, other)) return true;
return moveToOrUse(t, other, "seek_friend", this.label);
},
tick: updateMateBehavior,
};
}
function createFightRivalActionSpec() {
return {
id: "fight_rival",
need: "social",
subNeed: "conflict",
state: "seek_enemy",
2026-06-28 13:40:41 +09:00
label: "\u6c17\u306b\u5165\u3089\u306a\u3044\u76f8\u624b\u306b\u5411\u304b\u3063\u3066\u3044\u308b",
phrase: "\u55a7\u5629\u3092\u59cb\u3081\u305f",
2026-06-25 14:51:58 +09:00
weight: 18,
timerBacked: true,
selectTarget(t, world) { return selectFightRivalTarget(t, world); },
canStart(t, world, ctx = {}) {
if ((t.fightCooldown || 0) > 0.04 || (t.postConflictPeaceTimer || 0) > 0.04) return false;
const forced = (t?.fightMochiTimer || 0) > 0.04 || isCurrentBehaviorForced(t);
const candidate = conflictTargetFor(t, world, forced ? 24 : 42);
if (!forced) {
const parts = t?.socialReasonParts || {};
const conflict = Number(parts.conflict || 0) || 0;
const socialPull = Math.max(Number(parts.bond || 0) || 0, Number(parts.mate || 0) || 0, Number(parts.family || 0) || 0);
if (!candidate?.target || conflict < 30 || conflict < socialPull * 0.82) return false;
}
return !!(ctx.target ?? candidate?.target ?? this.selectTarget(t, world, ctx));
},
start(t, world, ctx = {}) {
const candidate = conflictTargetFor(t, world, (t?.fightMochiTimer || 0) > 0.04 || isCurrentBehaviorForced(t) ? 18 : 34);
const rival = ctx.target ?? this.selectTarget(t, world, ctx);
if (!isLiveTarinaiEntity(rival)) return false;
t.conflictTargetId = rival.id;
t.conflictUrge = Math.max(t.conflictUrge || 0, candidate?.score || 48);
if (dist(t, rival) > Math.max(42, (t.radius || 20) + (rival.radius || 20) + 12)) return moveToOrUse(t, rival, "seek_enemy", this.label);
if (candidate?.forced || isCurrentBehaviorForced(t) || (t.fightMochiTimer || 0) > 0.04 || (rival.fightMochiTimer || 0) > 0.04) return !!world.startForcedFight?.(t, rival);
world.startConflict?.(t, rival);
return true;
},
tick: updateFightBehavior,
};
}
function createIntimidateEnemyActionSpec() {
return {
id: "intimidate_enemy",
need: "social",
subNeed: "conflict",
state: "intimidate",
2026-06-28 13:40:41 +09:00
label: "\u6c17\u306b\u306a\u308b\u76f8\u624b\u3092\u5a01\u5687\u3057\u3066\u3044\u308b",
phrase: "\u5a01\u5687\u3057\u305f",
2026-06-25 14:51:58 +09:00
weight: 8,
timerBacked: true,
selectTarget(t) { return t.recentFightRival?.(80, 180) || null; },
canStart(t, world, ctx = {}) {
if ((t.intimidateTimer || 0) > 0.04 || (t.postConflictPeaceTimer || 0) > 0.04) return false;
const parts = t?.socialReasonParts || {};
if ((t.fightMochiTimer || 0) <= 0.04 && Number(parts.conflict || 0) < 24) return false;
return basicCanStartWithTarget.call(this, t, world, ctx);
},
start(t, world, ctx = {}) {
const rival = ctx.target ?? this.selectTarget(t, world, ctx);
t.setActionState?.("intimidate", { target: rival, reason: this.label, sleeping: false });
t.intimidateTimer = Math.max(t.intimidateTimer || 0, 0.85);
return true;
},
tick(t, world) {
if ((t.intimidateTimer || 0) > 0.04) return true;
const rival = currentBehaviorTarget(t) || t.recentFightRival?.(80, 180);
if (!rival) return false;
return world?.startIntimidation?.(t, rival) || true;
},
};
}
function createBirthRitualActionSpec() {
return {
id: "birth_ritual",
need: "social",
subNeed: "mate",
2026-06-28 13:40:41 +09:00
label: "\u3078\u3053\u3078\u3053\u3057\u3066\u3044\u308b",
phrase: "\u3078\u3053\u3078\u3053\u3057\u3066\u3044\u308b",
2026-06-25 14:51:58 +09:00
weight: 0,
state: "birth_ritual",
timerBacked: true,
canStart(t) { return (t.birthRitualTimer || 0) > 0.04; },
start() { return true; },
tick(t) { return (t.birthRitualTimer || 0) > 0.04 ? true : "finished"; },
};
}
const TARINAI_ACTION_DEFINITIONS = [
createConsumableActionSpec({
id: "eat_food",
subNeed: "meal",
state: "seek_food",
2026-06-28 13:40:41 +09:00
label: "\u98df\u3079\u7269\u3092\u63a2\u3057\u3066\u3044\u308b",
phrase: "\u98df\u3079\u7269\u3092\u98df\u3079\u3066\u3044\u308b",
2026-06-25 14:51:58 +09:00
weight: 62,
role: "food",
range: 760,
}),
createConsumableActionSpec({
id: "drink_water",
subNeed: "water",
state: "seek_water",
2026-06-28 13:40:41 +09:00
label: "\u6c34\u3092\u63a2\u3057\u3066\u3044\u308b",
phrase: "\u6c34\u3092\u98f2\u3093\u3060",
2026-06-25 14:51:58 +09:00
weight: 24,
role: "drink",
range: 560,
}),
createSleepInBedActionSpec(),
createSleepBuildGrassBedActionSpec(),
createSleepAnywhereActionSpec(),
createConsumableActionSpec({
id: "use_medicine",
subNeed: "medicine",
state: "seek_food",
2026-06-28 13:40:41 +09:00
label: "\u5143\u6c17\u306b\u306a\u308b\u3082\u306e\u3092\u63a2\u3057\u3066\u3044\u308b",
phrase: "\u5143\u6c17\u306b\u306a\u3063\u305f",
2026-06-25 14:51:58 +09:00
weight: 34,
role: "medicine",
range: 620,
}),
createRestActionSpec(),
createSunbathActionSpec(),
createPanicEscapeActionSpec(),
{
id: "hide_at_owned_structure",
need: "safety",
state: "seek_bed",
2026-06-28 13:40:41 +09:00
label: "\u81ea\u5206\u306e\u5834\u6240\u306b\u96a0\u308c\u3066\u3044\u308b",
phrase: "\u81ea\u5206\u306e\u5834\u6240\u3078\u623b\u3063\u305f",
2026-06-25 14:51:58 +09:00
weight: 30,
selectTarget: selectOwnedStructure("grass_bed", 620),
canStart(t, world, ctx = {}) { return !!(ctx.target ?? this.selectTarget(t, world, ctx)); },
2026-06-28 13:40:41 +09:00
start: startMoveToTarget("seek_bed", "\u81ea\u5206\u306e\u5834\u6240\u306b\u96a0\u308c\u3066\u3044\u308b"),
2026-06-25 14:51:58 +09:00
},
createApproachFriendActionSpec(),
createApproachFamilyActionSpec(),
createApproachMateActionSpec(),
createFightRivalActionSpec(),
createIntimidateEnemyActionSpec(),
{
id: "play",
need: "fulfill",
state: "play_ball",
2026-06-28 13:40:41 +09:00
label: "\u904a\u3093\u3067\u3044\u308b",
phrase: "\u904a\u3093\u3060",
2026-06-25 14:51:58 +09:00
weight: 24,
selectTarget(t, world) { return world.nearest?.(t, ["ball"], 520) || null; },
canStart(t, world, ctx = {}) { return !!(ctx.target ?? this.selectTarget(t, world, ctx)); },
2026-06-28 13:40:41 +09:00
start: startMoveToTarget("play_ball", "\u904a\u3093\u3067\u3044\u308b"),
2026-06-25 14:51:58 +09:00
},
{
id: "build_grass_bed",
need: "fulfill",
state: "build",
2026-06-28 13:40:41 +09:00
label: "\u304b\u3093\u305f\u3093\u30d9\u30c3\u30c9\u3092\u4f5c\u3063\u3066\u3044\u308b",
phrase: "\u304b\u3093\u305f\u3093\u30d9\u30c3\u30c9\u3092\u4f5c\u3063\u305f",
2026-06-25 14:51:58 +09:00
weight: 34,
2026-06-28 23:07:40 +09:00
selectTarget(t, world) { return (typeof findNearbyBuildMaterial === "function" ? findNearbyBuildMaterial(world, t, "grass_bed", 520) : null) || findNearbyMaterial(world, t, "grassMaterial", 520); },
2026-06-25 14:51:58 +09:00
canStart(t, world, ctx = {}) { return canBuildOwnedStructureByAge(t) && !findOwnedStructure(world, t, "grass_bed") && !!(ctx.target ?? this.selectTarget(t, world, ctx)) && !["sleep", "fight", "panic", "intimidate"].includes(t.state); },
start(t, world) { return buildOwnedStructureFromGrass(t, world, "grass_bed", this.label); },
},
{
id: "build_plushie",
need: "fulfill",
state: "build",
2026-06-28 13:40:41 +09:00
label: "\u306c\u3044\u3050\u308b\u307f\u3092\u4f5c\u3063\u3066\u3044\u308b",
phrase: "\u306c\u3044\u3050\u308b\u307f\u3092\u4f5c\u3063\u305f",
2026-06-25 14:51:58 +09:00
weight: 28,
2026-06-28 23:07:40 +09:00
selectTarget(t, world) { return (typeof findNearbyBuildMaterial === "function" ? findNearbyBuildMaterial(world, t, "plushie", 520) : null) || findNearbyMaterial(world, t, "grassMaterial", 520); },
2026-06-25 14:51:58 +09:00
canStart(t, world, ctx = {}) { return canBuildOwnedStructureByAge(t) && !findOwnedStructure(world, t, "plushie") && !!(ctx.target ?? this.selectTarget(t, world, ctx)) && !["sleep", "fight", "panic", "intimidate"].includes(t.state); },
start(t, world) { return buildOwnedStructureFromGrass(t, world, "plushie", this.label); },
},
{
id: "return_owned_structure",
need: "fulfill",
state: "seek_bed",
2026-06-28 13:40:41 +09:00
label: "\u81ea\u5206\u306e\u5834\u6240\u3078\u623b\u3063\u3066\u3044\u308b",
phrase: "\u81ea\u5206\u306e\u5834\u6240\u3078\u623b\u3063\u305f",
2026-06-25 14:51:58 +09:00
weight: 22,
selectTarget: selectOwnedStructure("grass_bed", 620),
canStart(t, world, ctx = {}) { return !!(ctx.target ?? this.selectTarget(t, world, ctx)); },
2026-06-28 13:40:41 +09:00
start: startMoveToTarget("seek_bed", "\u81ea\u5206\u306e\u5834\u6240\u3078\u623b\u3063\u3066\u3044\u308b"),
2026-06-25 14:51:58 +09:00
},
{
id: "use_plushie",
need: "fulfill",
state: "idle",
2026-06-28 13:40:41 +09:00
label: "\u306c\u3044\u3050\u308b\u307f\u3092\u62b1\u3048\u3066\u3044\u308b",
phrase: "\u306c\u3044\u3050\u308b\u307f\u3092\u62b1\u3048\u305f",
2026-06-25 14:51:58 +09:00
weight: 18,
selectTarget: selectOwnedStructure("plushie", Infinity),
canStart(t, world, ctx = {}) { return !!(ctx.target ?? this.selectTarget(t, world, ctx)); },
start(t, world, ctx = {}) {
const plushie = ctx.target ?? this.selectTarget(t, world, ctx);
plushie?.use?.(t, world);
t.setActionState?.("idle", { target: plushie, reason: this.label, sleeping: false });
return true;
},
},
createBirthRitualActionSpec(),
createWanderActionSpec(),
];
const TARINAI_ACTIONS = typeof registerTarinaiActionSpecs === "function"
? registerTarinaiActionSpecs(TARINAI_ACTION_DEFINITIONS)
: TARINAI_ACTION_DEFINITIONS;
if (typeof window !== "undefined") Object.assign(window, { TARINAI_ACTION_DEFINITIONS, TARINAI_ACTIONS });