removed unused processesseessess

This commit is contained in:
33333-33333 2026-06-29 16:21:58 +09:00
commit 4fdc567e08
158 changed files with 1317 additions and 3351 deletions

View file

@ -74,7 +74,6 @@ JS_EXACT_DESCRIPTIONS = {
"main.js": "startup, restore, UI binding, update/render loop",
"math.js": "shared numeric helpers",
"mechanical_system.js": "mechanical item forces, rails, ropes, pins, fences",
"patch_helpers.js": "compatibility patch helpers",
"perf_profiler.js": "performance profiler and quality adaptation",
"registry_base.js": "registry normalization helper base",
"render.js": "main canvas renderer",

View file

@ -105,7 +105,7 @@ context.globalThis = context;
context.window = context;
vm.createContext(context);
vm.runInContext({source!r}, context, {{ filename: 'tarinai_action_spec.js' }});
const action = context.registerTarinaiActionSpec({{
const action = context.registerTarinaiActionSpecs([{{
id: 'smoke_action',
need: 'fulfill',
label: 'smoke',
@ -114,7 +114,7 @@ const action = context.registerTarinaiActionSpec({{
start(t, world, ctx) {{ t.startedWith = ctx.target.id; return true; }},
tick() {{ return 'finished'; }},
finish(t) {{ t.finished = true; return true; }},
}});
}}])[0];
const subject = {{}};
if (!context.canStartTarinaiAction('smoke_action', subject, {{}})) throw new Error('canStart failed');
if (!context.startTarinaiAction(action, subject, {{}})) throw new Error('start failed');
@ -300,12 +300,11 @@ def check_runtime_diagnostics_hooks() -> None:
spatial_budget = read_text(ROOT / "js/world_spatial_budget.js")
debug_tools = read_text(ROOT / "js/debug_tools.js")
world_update = read_text(ROOT / "js/world_update.js")
world_update_phases = read_text(ROOT / "js/world_update_phases.js")
simulation_systems = read_text(ROOT / "js/simulation_systems.js")
if "runtimeDiagnostics()" not in spatial_budget:
fail("World.runtimeDiagnostics hook missing")
if "lastRuntimeDiagnostics" not in (world_update + world_update_phases + simulation_systems):
fail("world update phases do not publish lastRuntimeDiagnostics")
if "lastRuntimeDiagnostics" not in (world_update + simulation_systems):
fail("world update systems do not publish lastRuntimeDiagnostics")
if "spatial rebuild" not in debug_tools or "behavior forced" not in debug_tools:
fail("debug overlay is missing runtime diagnostic lines")
spatial_budget = read_text(ROOT / "js/world_spatial_budget.js")
@ -504,15 +503,14 @@ def check_unified_item_registry() -> None:
facade_source = read_text(ROOT / "js/item_registry.js")
required_pairs = [
(tool_source, "const TOOL_DEFINITIONS"),
(tool_source, "global.TarinaiToolRegistry"),
(visual_source, "function itemVisualDefinition"),
(visual_source, "global.TarinaiItemVisualDefinitions"),
(food_source, "class FoodRegistry"),
(food_source, "global.TarinaiFoodRegistry"),
(effect_source, "class EffectRegistry"),
(effect_source, "global.TarinaiEffectRegistry"),
(facade_source, "global.TarinaiItemRegistry"),
(facade_source, "definition: itemTypeDefinition"),
(facade_source, "food: global.TarinaiFoodRegistry"),
(facade_source, "effect: global.TarinaiEffectRegistry"),
]
for source, token in required_pairs:
if token not in source:
@ -542,7 +540,6 @@ def check_phase_and_input_split() -> None:
"js/simulation_maintenance_system.js",
"js/simulation_ambient_system.js",
"js/simulation_systems.js",
"js/world_update_phases.js",
"js/ui_input_shared.js",
"js/ui_input_touch.js",
"js/ui_input_mouse.js",
@ -551,16 +548,15 @@ def check_phase_and_input_split() -> None:
if rel not in js_files:
fail(f"split runtime module missing from manifest: {rel}")
world_update = read_text(ROOT / "js/world_update.js")
phases = read_text(ROOT / "js/world_update_phases.js")
ui_bind = read_text(ROOT / "js/ui_bind.js")
touch = read_text(ROOT / "js/ui_input_touch.js")
mouse = read_text(ROOT / "js/ui_input_mouse.js")
shared = read_text(ROOT / "js/ui_input_shared.js")
systems = read_text(ROOT / "js/simulation_systems.js")
if "TarinaiSimulation" not in world_update or "function update(worldRef, dt)" not in phases:
fail("World.update is not delegated to world_update_phases.js")
if "TarinaiSimulation" not in world_update:
fail("World.update is not delegated to TarinaiSimulation")
for token in ["updateClock", "updateEnvironment", "updateItemsAndAnts", "updateCreatures", "runMaintenance", "spawnRainWater"]:
if token not in phases or token not in systems:
if token not in systems:
fail(f"world update phase missing: {token}")
if "touchstart" in ui_bind or "mousedown" in ui_bind or "touchmove" in ui_bind:
fail("ui_bind.js still owns field pointer input handlers")
@ -573,7 +569,7 @@ def check_phase_and_input_split() -> None:
fail("mouse input module missing mouse handlers")
if ui_bind.count("syncToolSizeBadges();") != 1:
fail("ui_bind.js should bind/sync tool size badges only once")
phase_birth_direct = "audio.birth" in phases
phase_birth_direct = False
placement_birth_direct = "audio.birth" in read_text(ROOT / "js/world_placement_log.js")
family_birth_direct = "audio.birth" in read_text(ROOT / "js/world_family_social.js")
if phase_birth_direct or placement_birth_direct or family_birth_direct:
@ -586,7 +582,6 @@ def check_processing_split() -> None:
js_files = data["js"]
required = [
"js/item_type_initializers.js",
"js/item_lifecycle_runtime.js",
"js/item_render_runtime.js",
"js/tarinai_item_targeting.js",
"js/tarinai_action_definitions.js",
@ -597,16 +592,15 @@ def check_processing_split() -> None:
for rel in required:
if rel not in js_files:
fail(f"processing split module missing from manifest: {rel}")
if not (js_files.index("js/items.js") < js_files.index("js/item_type_initializers.js") < js_files.index("js/item_lifecycle_runtime.js") < js_files.index("js/item_render_runtime.js") < js_files.index("js/ants.js")):
fail("item init/runtime/render split load order is invalid")
if not (js_files.index("js/items.js") < js_files.index("js/item_type_initializers.js") < js_files.index("js/item_render_runtime.js") < js_files.index("js/ants.js")):
fail("item init/render split load order is invalid")
if not (js_files.index("js/tarinai_forced_behavior.js") < js_files.index("js/tarinai_item_targeting.js") < js_files.index("js/tarinai_consumable_behavior.js") < js_files.index("js/tarinai_social_action_runtime.js") < js_files.index("js/tarinai_building_behavior.js") < js_files.index("js/tarinai_action_definitions.js") < js_files.index("js/tarinai_needs_items.js")):
fail("tarinai behavior split load order is invalid")
items = read_text(ROOT / "js/items.js")
init = read_text(ROOT / "js/item_type_initializers.js")
lifecycle = read_text(ROOT / "js/item_lifecycle_runtime.js")
item_render = read_text(ROOT / "js/item_render_runtime.js")
if "class Item" not in items or "function initializeItemTypeState" not in init or "Object.assign(Item.prototype" not in lifecycle or "Object.assign(Item.prototype" not in item_render:
fail("item construction/init/lifecycle/render split is incomplete")
if "class Item" not in items or "function initializeItemTypeState" not in init or "Object.assign(Item.prototype" not in item_render:
fail("item construction/init/render split is incomplete")
if "updateDuplicator" in items or "\ndraw(ctx" in items or "if (type === \"grass\")" in items:
fail("items.js still owns type init, runtime, or render methods")
defs = read_text(ROOT / "js/tarinai_action_definitions.js")
@ -666,7 +660,8 @@ if (!context.TarinaiCommands.dispatch(world, {{ type: 'selection.favorite.toggle
if (!context.TarinaiCommands.dispatch(world, {{ type: 'simulation.paused.toggle' }}).ok || world.paused !== true) throw new Error('pause failed');
if (!context.TarinaiCommands.dispatch(world, {{ type: 'simulation.speed.cycle', speeds: [1, 2, 4] }}).ok || world.speed !== 2) throw new Error('speed failed');
if (!context.TarinaiCommands.dispatch(world, {{ type: 'pointer.primary', x: 7, y: 9 }}).ok || world.clicked[0] !== 7 || world.clicked[1] !== 9) throw new Error('pointer failed');
if (typeof world.dispatchCommand !== 'function' || !world.dispatchCommand({{ type: 'selection.clear' }}).ok || world.selected !== null) throw new Error('World facade failed');
if (typeof world['dispatch' + 'Command'] !== 'undefined') throw new Error('World facade should not be installed');
if (!context.TarinaiCommands.dispatch(world, {{ type: 'selection.clear' }}).ok || world.selected !== null) throw new Error('selection clear failed');
if (!events.some(([type]) => type === 'selection:changed')) throw new Error('selection event missing');
"""
with tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".js", delete=False) as f:
@ -695,7 +690,6 @@ def check_system_order_boundary() -> None:
"js/simulation_maintenance_system.js",
"js/simulation_ambient_system.js",
"js/simulation_systems.js",
"js/world_update_phases.js",
"js/system_order.js",
"js/simulation.js",
"js/world_update.js",
@ -712,7 +706,6 @@ def check_system_order_boundary() -> None:
"js/simulation_maintenance_system.js",
"js/simulation_ambient_system.js",
"js/simulation_systems.js",
"js/world_update_phases.js",
"js/system_order.js",
"js/simulation.js",
"js/world_update.js",
@ -736,13 +729,11 @@ def check_system_order_boundary() -> None:
for rel, token in concrete_modules.items():
if token not in read_text(ROOT / rel):
fail(f"concrete simulation split module missing export: {rel}")
if "TarinaiSimulationSystems" not in read_text(ROOT / "js/world_update_phases.js"):
fail("world_update_phases.js is not delegating to the concrete simulation systems module")
for token in ["const SYSTEM_ORDER", "clock", "environment", "items_and_ants", "creatures", "maintenance", "diagnostics"]:
if token not in system_order:
fail(f"system order token missing: {token}")
if "TarinaiSystemOrder" not in simulation or "systemOrder" not in simulation:
fail("simulation.js does not expose the named system order boundary")
if "TarinaiSystemOrder" not in simulation or "Object.freeze({ update })" not in simulation:
fail("simulation.js does not expose the update boundary")
ok("named simulation system order present")
@ -756,7 +747,6 @@ def simulation_boundary_smoke() -> None:
"js/simulation_maintenance_system.js",
"js/simulation_ambient_system.js",
"js/simulation_systems.js",
"js/world_update_phases.js",
"js/system_order.js",
"js/simulation.js",
]}
@ -770,9 +760,8 @@ for (const [filename, source] of Object.entries({sources!r})) {{
vm.runInContext(source, context, {{ filename }});
}}
if (!context.TarinaiSimulationSystems?.phases?.updateClock) throw new Error('simulation systems missing');
if (!context.TarinaiWorldUpdatePhases?.phases?.updateClock) throw new Error('phase facade missing');
if (!context.TarinaiSystemOrder?.names?.includes('clock')) throw new Error('system order missing');
if (!context.TarinaiSimulation?.systemOrder) throw new Error('simulation facade missing');
if (!context.TarinaiSystemOrder?.update) throw new Error('system order missing');
if (!context.TarinaiSimulation?.update) throw new Error('simulation facade missing');
"""
with tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".js", delete=False) as f:
f.write(js)
@ -796,7 +785,7 @@ def check_update_policy_boundaries() -> None:
for rel in required:
if rel not in js_files:
fail(f"update policy boundary module missing from manifest: {rel}")
if not (js_files.index("js/item_lifecycle_runtime.js") < js_files.index("js/item_update_policy.js") < js_files.index("js/item_runtime.js")):
if not (js_files.index("js/item_lifecycle_support.js") < js_files.index("js/item_update_policy.js") < js_files.index("js/item_runtime.js")):
fail("item update policy load order is invalid")
if not (js_files.index("js/simulation_runtime_helpers.js") < js_files.index("js/tarinai_update_policy.js") < js_files.index("js/simulation_creature_system.js")):
fail("Tarinai update policy load order is invalid")
@ -810,7 +799,7 @@ def check_update_policy_boundaries() -> None:
(item_policy, "itemUpdateInterval"),
(item_policy, "forScheduledItems"),
(tarinai_policy, "global.TarinaiCreatureUpdatePolicy"),
(tarinai_policy, "updateInterval"),
(tarinai_policy, "updateCadence"),
(scheduler, "TarinaiItemUpdatePolicy"),
(sim_helpers, "TarinaiItemUpdatePolicy"),
(creature_system, "TarinaiCreatureUpdatePolicy"),
@ -834,6 +823,9 @@ context.isServingFoodType = (type) => type === 'food';
context.TarinaiItemRuntime = {{
updateOne(item, dt) {{ item.runs = (item.runs || 0) + 1; item.lastDt = dt; return true; }}
}};
context.TarinaiPerf = {{ begin() {{ return () => undefined; }}, renderQualityTier() {{ return "normal"; }} }};
context.TarinaiPhysicsWorldSystem = {{ updateWorld() {{ return {{ mechanical: 0, constraintRan: 0, postConstraintPairs: 0 }}; }} }};
context.TarinaiInputMode = {{ snapshot() {{ return {{ touchFirst: false }}; }} }};
vm.createContext(context);
for (const file of ['js/item_update_policy.js', 'js/item_update_scheduler.js']) {{
vm.runInContext(fs.readFileSync(file, 'utf8'), context, {{ filename: file }});
@ -864,7 +856,7 @@ def check_entity_runtime_boundary() -> None:
for rel in required:
if rel not in js_files:
fail(f"entity runtime boundary module missing from manifest: {rel}")
if not (js_files.index("js/item_lifecycle_runtime.js") < js_files.index("js/item_runtime.js") < js_files.index("js/structure_lifecycle.js")):
if not (js_files.index("js/item_lifecycle_pipeline.js") < js_files.index("js/item_runtime.js") < js_files.index("js/structure_lifecycle.js")):
fail("item runtime boundary load order is invalid")
if not (js_files.index("js/tarinai_needs_items.js") < js_files.index("js/tarinai_social_move_life.js") < js_files.index("js/tarinai_runtime.js") < js_files.index("js/tarinai_render.js")):
fail("Tarinai runtime boundary load order is invalid")
@ -878,10 +870,9 @@ def check_entity_runtime_boundary() -> None:
(item_runtime, "installPrototypeBoundary"),
(item_runtime, "updateScheduled"),
(tarinai_runtime, "global.TarinaiCreatureRuntime"),
(tarinai_runtime, "updateBatch"),
(item_scheduler, "TarinaiItemRuntime?.updateOne"),
(sim_helpers, "TarinaiItemRuntime?.updateScheduled"),
(creature_system, "TarinaiCreatureRuntime?.updateOne"),
(item_scheduler, "TarinaiItemRuntime.updateOne"),
(sim_helpers, "TarinaiItemRuntime.updateScheduled"),
(creature_system, "TarinaiCreatureRuntime.updateOne"),
]
for source, token in required_tokens:
if token not in source:
@ -896,8 +887,6 @@ def check_tarinai_update_pipeline_boundary() -> None:
"js/tarinai_sunbath_system.js",
"js/tarinai_cursor_care_system.js",
"js/tarinai_local_environment_system.js",
"js/tarinai_forced_action_planner_system.js",
"js/tarinai_action_planner_system.js",
"js/tarinai_need_planner_system.js",
"js/tarinai_item_interaction_context.js",
"js/tarinai_food_interaction_system.js",
@ -915,8 +904,6 @@ def check_tarinai_update_pipeline_boundary() -> None:
fail(f"Tarinai update pipeline module missing from manifest: {rel}")
expected_order = [
"js/tarinai_needs_items.js",
"js/tarinai_forced_action_planner_system.js",
"js/tarinai_action_planner_system.js",
"js/tarinai_need_planner_system.js",
"js/tarinai_item_interaction_context.js",
"js/tarinai_food_interaction_system.js",
@ -940,8 +927,6 @@ def check_tarinai_update_pipeline_boundary() -> None:
"js/tarinai_sunbath_system.js": "global.TarinaiSunbathSystem",
"js/tarinai_cursor_care_system.js": "global.TarinaiCursorCareSystem",
"js/tarinai_local_environment_system.js": "global.TarinaiLocalEnvironmentSystem",
"js/tarinai_forced_action_planner_system.js": "global.TarinaiForcedActionPlannerSystem",
"js/tarinai_action_planner_system.js": "global.TarinaiActionPlannerSystem",
"js/tarinai_need_planner_system.js": "global.TarinaiNeedPlannerSystem",
"js/tarinai_item_interaction_context.js": "global.TarinaiItemInteractionContext",
"js/tarinai_food_interaction_system.js": "global.TarinaiFoodInteractionSystem",
@ -966,11 +951,11 @@ def check_tarinai_update_pipeline_boundary() -> None:
cursor_system = read_text(ROOT / "js/tarinai_cursor_care_system.js")
local_env_system = read_text(ROOT / "js/tarinai_local_environment_system.js")
interaction_system = read_text(ROOT / "js/tarinai_item_interaction_system.js")
if "TarinaiUpdatePipeline?.updateOne" not in needs_items or "const minute = dt / 60" in needs_items:
fail("Tarinai.prototype.update is not a thin facade over the update pipeline")
if "TarinaiItemInteractionSystem?.updateOne" not in needs_items or "const foodNeed = Number(this.needRaw?.food" in needs_items:
if " update(dt)" in needs_items or "const minute = dt / 60" in needs_items:
fail("Tarinai.prototype.update residue remains in needs mixin")
if "TarinaiItemInteractionSystem.updateOne" not in needs_items or "const foodNeed = Number(this.needRaw?.food" in needs_items:
fail("Tarinai.prototype.interactWithItems is not a thin facade over the interaction system")
for facade_token in ["TarinaiSunbathSystem?.updateOne", "TarinaiCursorCareSystem?.updateOne", "TarinaiLocalEnvironmentSystem?.updateOne"]:
for facade_token in ["TarinaiSunbathSystem.updateOne", "TarinaiCursorCareSystem.updateOne", "TarinaiLocalEnvironmentSystem.updateOne"]:
if facade_token not in needs_items:
fail(f"Tarinai prototype subsystem facade missing: {facade_token}")
interaction_context = read_text(ROOT / "js/tarinai_item_interaction_context.js")
@ -986,11 +971,11 @@ def check_tarinai_update_pipeline_boundary() -> None:
]:
if token not in source:
fail(f"Tarinai update subsystem missing extracted token: {token}")
if "TarinaiItemInteractionContext?.create" not in interaction_system or "TarinaiFoodInteractionSystem?.updateCandidate" not in interaction_system or "TarinaiContactItemSystem?.updateCandidate" not in interaction_system:
if "TarinaiItemInteractionContext.create" not in interaction_system or "TarinaiFoodInteractionSystem.updateCandidate" not in interaction_system or "TarinaiContactItemSystem.updateCandidate" not in interaction_system:
fail("Tarinai item interaction orchestrator does not delegate to context/food/contact candidate subsystems")
if "for (const item of ctx.foodCandidates" not in interaction_system:
fail("Tarinai item interaction orchestrator does not preserve per-candidate interaction order")
if "TarinaiNeedPlannerSystem?.updateOne" not in needs_items or "function resolveNeedsCore" not in needs_items:
if "TarinaiNeedPlannerSystem.updateOne" not in needs_items or "function resolveNeedsCore" not in needs_items:
fail("Tarinai resolveNeeds is not routed through planner system")
if "TarinaiNeedPlannerCoreSystem" not in needs_items:
fail("Tarinai need planner core system export missing")
@ -1018,6 +1003,9 @@ context.clamp = (v, a, b) => Math.max(a, Math.min(b, v));
context.rand = (a, b) => (a + b) / 2;
context.pick = (arr) => arr[0];
context.stableUnit = () => 0.5;
context.deterministicRange = (world, salt, min, max) => (min + max) / 2;
context.deterministicSigned = () => 0;
context.deterministicChance = () => false;
context.applyNeedShock = () => undefined;
context.applyNeedRelief = () => undefined;
context.calculateStressFromNeeds = () => 0;
@ -1044,7 +1032,7 @@ const world = {{
}};
const t = {{
world, dead: false, x: 10, y: 12, vx: 0, vy: 0, radius: 20,
age: 0, reproductionTimer: 10, blink: 0, eatTimer: 0, eatCooldown: 0, foodReactTimer: 0,
age: 0, reproductionTimer: 10, eatTimer: 0, eatCooldown: 0, foodReactTimer: 0,
surpriseTimer: 0, fearTimer: 0, intimidateTimer: 0, intimidatedTimer: 0, stress: 0,
hpBarTimer: 0, stressBarTimer: 0, loveMochiTimer: 0, fightMochiTimer: 0,
birthRitualTimer: 0, pokeFlashTimer: 0, cursorPetting: 0, pinchThoughtTimer: 0,
@ -1064,7 +1052,6 @@ const t = {{
updateNestBoxPresence() {{ calls.push('nestBox'); }},
checkLife() {{ calls.push('checkLife'); }},
}};
if (!context.TarinaiUpdatePipeline.canRunConcretePipeline()) throw new Error('pipeline cannot run concrete steps');
if (!context.TarinaiUpdatePipeline.updateOne(t, 0.016, {{ legacyUpdate() {{ throw new Error('legacy should not run'); }} }})) throw new Error('pipeline update failed');
for (const expected of ['zunchiDisease', 'specialDisease', 'facing', 'targetProgress', 'move', 'nestBox', 'fence', 'checkLife']) {{
if (!calls.includes(expected)) throw new Error('missing pipeline call: ' + expected);
@ -1098,6 +1085,8 @@ def check_item_lifecycle_pipeline_boundary() -> None:
]
required = [
"js/item_lifecycle_support.js",
"js/physics_helpers.js",
"js/pin_attachment_system.js",
*dynamic_required,
"js/item_lifecycle_decay_system.js",
"js/item_lifecycle_growth_system.js",
@ -1111,8 +1100,9 @@ def check_item_lifecycle_pipeline_boundary() -> None:
if rel not in js_files:
fail(f"Item lifecycle pipeline module missing from manifest: {rel}")
expected_order = [
"js/physics_helpers.js",
"js/pin_attachment_system.js",
"js/item_lifecycle_support.js",
"js/item_lifecycle_runtime.js",
"js/item_update_policy.js",
*dynamic_required,
"js/item_lifecycle_decay_system.js",
@ -1154,7 +1144,7 @@ def check_item_lifecycle_pipeline_boundary() -> None:
fail(f"Item lifecycle pipeline missing step token: {token}")
dynamic_step = read_text(ROOT / "js/item_lifecycle_step_dynamic.js")
dynamic_system = read_text(ROOT / "js/item_dynamic_system.js")
if "TarinaiItemDynamicSystem?.update" not in dynamic_step:
if "TarinaiItemDynamicSystem.update" not in dynamic_step:
fail("Item dynamic lifecycle step does not delegate to TarinaiItemDynamicSystem")
for token in ["TarinaiItemDynamicToolSystem", "TarinaiItemDynamicBallSystem", "TarinaiItemDynamicDuplicatorSystem", "TarinaiItemDynamicPinSystem", "TarinaiItemDynamicZunchiSystem"]:
if token not in dynamic_system:
@ -1164,20 +1154,15 @@ def check_item_lifecycle_pipeline_boundary() -> None:
for token in ["item.updateFan", "item.updateRotator", "item.updateBall", "item.updateDuplicator", "item.updatePushpin", "item.updateZunchiMotion", "item.updateZunchi"]:
if token in body:
fail(f"dynamic subsystem still delegates to prototype method {token} in {rel}")
lifecycle_runtime = read_text(ROOT / "js/item_lifecycle_runtime.js")
lifecycle_support = read_text(ROOT / "js/item_lifecycle_support.js")
lifecycle_decay = read_text(ROOT / "js/item_lifecycle_step_decay.js")
lifecycle_growth = read_text(ROOT / "js/item_lifecycle_step_growth.js")
if "TarinaiItemLifecycleRuntimeSupport" not in lifecycle_support or "TarinaiDuplicatorRuntime" not in lifecycle_support:
if "TarinaiItemLifecycleRuntimeSupport" not in lifecycle_support:
fail("item lifecycle support export missing")
if "TarinaiItemDecaySystem?.update" not in lifecycle_decay or "TarinaiItemGrowthSystem?.update" not in lifecycle_growth:
if "TarinaiDuplicatorRuntime" in lifecycle_support:
fail("duplicate duplicator runtime export still present")
if "TarinaiItemDecaySystem.update" not in lifecycle_decay or "TarinaiItemGrowthSystem.update" not in lifecycle_growth:
fail("item lifecycle decay/growth steps are not thin system adapters")
for helper_token in ["function duplicatorLoadTypeForItem", "function duplicatorApplyStoredPin"]:
if helper_token in lifecycle_runtime:
fail(f"support helper still lives in item_lifecycle_runtime.js: {helper_token}")
for token in ["updateBall(dt, worldRef) { return window.TarinaiItemDynamicBallSystem", "attachPushpin(t, worldRef"]:
if token not in lifecycle_runtime:
fail(f"prototype subsystem facade missing: {token}")
js = f"""
const vm = require('vm');
const fs = require('fs');
@ -1187,8 +1172,7 @@ context.window = context;
context.isServingFoodType = (type) => type === 'food';
context.passiveFoodDecayInterval = () => 0.5;
context.passiveFoodDecayRate = () => 0.1;
context.PASSIVE_FOOD_HYGIENE_PENALTY_PER_SERVING = 0.055;
context.TarinaiItemRegistry = {{ food: {{ hygienePenalty() {{ return 0.05; }} }} }};
context.TarinaiItemRegistry = {{ food: {{ }} }};
context.isPinType = (type) => type === 'pushpin';
context.normalizeGrassStage = (item) => {{ item.normalized = true; }};
context.stableUnit = () => 0.5;
@ -1196,17 +1180,15 @@ vm.createContext(context);
for (const file of {required!r}) {{
vm.runInContext(fs.readFileSync(file, 'utf8'), context, {{ filename: file }});
}}
if (!context.TarinaiItemLifecyclePipeline.canRunConcretePipeline()) throw new Error('item pipeline cannot run concrete steps');
const events = [];
const world = {{
itemDropImpact(item) {{ events.push(['drop', item.type]); }},
registerFoodSpoilage(value, item) {{ events.push(['spoil', value, item.type]); }},
markTerrainDirty(reason) {{ events.push(['terrain', reason]); }},
emit(type, payload) {{ events.push(['emit', type, payload.type]); }},
}};
const food = {{ type: 'food', dead: false, age: 0, dropTimer: 0.1, dropImpactDone: false, foodServingsRemaining: 1, amount: 1, passiveFoodDecayTimer: 0 }};
if (!context.TarinaiItemLifecyclePipeline.updateOne(food, 0.6, world, {{ legacyUpdate() {{ throw new Error('legacy should not run'); }} }})) throw new Error('food update failed');
if (food.age !== 0.6 || !food.dropImpactDone || !(food.foodServingsRemaining < 1) || !events.some(([kind]) => kind === 'spoil')) throw new Error('frame/decay step did not run');
if (food.age !== 0.6 || !food.dropImpactDone || !(food.foodServingsRemaining < 1) || !events.some(([kind, value]) => kind === 'terrain' && value === 'food-passive-decay')) throw new Error('frame/decay step did not run');
context.CONFIG = {{ worldPadding: 30 }};
context.clamp = (v, min, max) => Math.max(min, Math.min(max, v));
context.distXY = (ax, ay, bx, by) => Math.hypot(ax - bx, ay - by);
@ -1219,7 +1201,7 @@ context.TarinaiItemLifecyclePipeline.updateOne(dynamic, 0.25, ballWorld, {{ lega
if (!(dynamic.x > 80) || !(dynamic.spin > 0)) throw new Error('dynamic step did not run');
const grass = {{ type: 'grass', dead: false, age: 0 }};
context.TarinaiItemLifecyclePipeline.updateOne(grass, 0.1, world, {{ legacyUpdate() {{ throw new Error('legacy should not run'); }} }});
if (!grass.normalized) throw new Error('growth step did not run');
if (grass.normalized) throw new Error('grass should not be normalized by per-item growth step');
"""
with tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".js", delete=False) as f:
f.write(js)