hm
This commit is contained in:
parent
4d3fc2cd47
commit
c9dd7182aa
35 changed files with 478 additions and 288 deletions
|
|
@ -6,7 +6,7 @@ Purpose: static Japanese browser simulation/game. Optimize context by reading th
|
|||
|
||||
- Entrypoint: `index.html` defines the app shell, canvas, dialogs, right panels, and exact script order.
|
||||
- Runtime style: plain browser JS, global IIFEs, exports on `window`, no bundler/module loader.
|
||||
- Version/cache: `app_manifest.json`, `js/version.js`, and generated `service-worker.js` share `39.15.101`.
|
||||
- Version/cache: `app_manifest.json`, `js/version.js`, and generated `service-worker.js` share `39.15.103`.
|
||||
- Main loop: `js/main.js` initializes assets/audio/world/UI/save, then drives update/render.
|
||||
- Update order: `js/system_order.js` calls phase facades from `js/simulation_systems.js`.
|
||||
- Command path: UI/input emits command objects; `js/command_dispatcher.js` routes tool, pointer, selection, save, reset, and UI intents.
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@ tarinai_king_fix_v10
|
|||
- Burning tarinai and ants draw lightweight flame-shaped overlays.
|
||||
- Flame firecracker is redder and shows a countdown timer; normal firecracker is darker.
|
||||
- Standalone flame and fire particles use a simpler flame silhouette.
|
||||
- Version: 39.15.101
|
||||
- Version: 39.15.102
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"purpose": "Token-efficient machine-readable routing for tarinai_. Use scripts/ai_inventory.py for exact current file expansion.",
|
||||
"version": "39.15.101",
|
||||
"version": "39.15.103",
|
||||
"entrypoints": {
|
||||
"app": "index.html",
|
||||
"runtime": "js/main.js",
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ The core loop is:
|
|||
## Main UI
|
||||
|
||||
- **Canvas / field**: the main simulation area. Tarinai, items, effects, ants, weather, and terrain are drawn here.
|
||||
- **Top buttons**: pause/resume, speed control, ecology guide, sound settings, credits, save, and reset.
|
||||
- **Top buttons**: pause/resume, speed control, ecology guide, sound settings, credits, save, display settings, and reset.
|
||||
- **Display settings**: manually adjust effects, shadows, and drawing detail. `High` keeps quality-preserving optimizations active, while lower tiers cut heavier visuals for crowded scenes without lowering object limits.
|
||||
- **Mobile mode controls**: auto, camera, and tool modes for touch screens.
|
||||
- **Right panel tabs**: jump between main game, selected tarinai data, tools, colony, events, and family tree.
|
||||
- **Tarinai data panel**: appears when a tarinai is selected. Shows health, needs, personality, active effects, relations, inventory, behavior reason, and records.
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ AI-first entrypoint for a static browser simulation/game. Keep this file small;
|
|||
|
||||
## Runtime Shape
|
||||
|
||||
- No bundler; `index.html` loads ordered global-IIFE scripts with `?v=39.15.101`.
|
||||
- No bundler; `index.html` loads ordered global-IIFE scripts with `?v=39.15.103`.
|
||||
- Mutable runtime lives on `window`; primary entities are `World`, `Tarinai`, items, structures, ants, family graph, weather, and save snapshots.
|
||||
- Update loop: `main.js` creates the world/UI loop; `World.update` delegates to `TarinaiSystemOrder.update`; `system_order.js` runs named phases.
|
||||
- UI loop: pointer/tool/UI intents go through `command_dispatcher.js`; canvas drawing is in render modules; side panels are `ui_*.js`.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "39.15.101",
|
||||
"version": "39.15.107",
|
||||
"css": [
|
||||
"css/base.css",
|
||||
"css/layout.css",
|
||||
|
|
|
|||
|
|
@ -412,27 +412,27 @@ body::before {
|
|||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
position: static !important;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
gap: 1px !important;
|
||||
}
|
||||
.lineage-family-tree .tree-node .family-copy strong,
|
||||
.lineage-family-tree .tree-node .family-copy > span:not(.family-personality-tags) {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
.lineage-family-tree .tree-node .family-copy > .family-personality-tags {
|
||||
position: absolute !important;
|
||||
left: 7px !important;
|
||||
right: 7px !important;
|
||||
bottom: 5px !important;
|
||||
width: auto !important;
|
||||
max-width: none !important;
|
||||
height: 26px !important;
|
||||
max-height: 26px !important;
|
||||
margin: 0 !important;
|
||||
grid-auto-rows: 12px !important;
|
||||
position: static !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
height: 24px !important;
|
||||
max-height: 24px !important;
|
||||
margin: 2px 0 0 !important;
|
||||
grid-auto-rows: 11px !important;
|
||||
gap: 1px 3px !important;
|
||||
z-index: 3 !important;
|
||||
}
|
||||
.lineage-family-tree .tree-node .family-personality-tags em {
|
||||
height: 12px !important;
|
||||
line-height: 10px !important;
|
||||
font-size: 7px !important;
|
||||
height: 11px !important;
|
||||
line-height: 9px !important;
|
||||
font-size: 6.8px !important;
|
||||
}
|
||||
|
|
|
|||
337
index.html
337
index.html
|
|
@ -7,11 +7,11 @@
|
|||
<link rel="icon" type="image/png" href="assets/ui/favicon.png?v=16.07.17" />
|
||||
<link rel="apple-touch-icon" href="assets/ui/apple-touch-icon.png?v=16.07.17" />
|
||||
<link rel="shortcut icon" href="favicon.ico?v=16.07.17" />
|
||||
<link rel="stylesheet" href="css/base.css?v=39.15.101" />
|
||||
<link rel="stylesheet" href="css/layout.css?v=39.15.101" />
|
||||
<link rel="stylesheet" href="css/panel.css?v=39.15.101" />
|
||||
<link rel="stylesheet" href="css/components.css?v=39.15.101" />
|
||||
<link rel="stylesheet" href="css/mobile.css?v=39.15.101" />
|
||||
<link rel="stylesheet" href="css/base.css?v=39.15.107" />
|
||||
<link rel="stylesheet" href="css/layout.css?v=39.15.107" />
|
||||
<link rel="stylesheet" href="css/panel.css?v=39.15.107" />
|
||||
<link rel="stylesheet" href="css/components.css?v=39.15.107" />
|
||||
<link rel="stylesheet" href="css/mobile.css?v=39.15.107" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="loadingScreen" class="loading-screen" aria-live="polite">
|
||||
|
|
@ -191,20 +191,25 @@
|
|||
<div><strong>エフェクト</strong><span>破片、雨、光の演出</span></div>
|
||||
<div class="visual-setting-options" role="group" aria-label="エフェクト">
|
||||
<button type="button" data-visual-setting="effects" data-visual-value="off">OFF</button>
|
||||
<button type="button" data-visual-setting="effects" data-visual-value="on">ON</button>
|
||||
<button type="button" data-visual-setting="effects" data-visual-value="low">低</button>
|
||||
<button type="button" data-visual-setting="effects" data-visual-value="mid">中</button>
|
||||
<button type="button" data-visual-setting="effects" data-visual-value="high">高</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="visual-setting-row">
|
||||
<div><strong>影</strong><span>地面の落ち影とスプライトの影</span></div>
|
||||
<div class="visual-setting-options" role="group" aria-label="影">
|
||||
<button type="button" data-visual-setting="shadows" data-visual-value="off">OFF</button>
|
||||
<button type="button" data-visual-setting="shadows" data-visual-value="on">ON</button>
|
||||
<button type="button" data-visual-setting="shadows" data-visual-value="low">低</button>
|
||||
<button type="button" data-visual-setting="shadows" data-visual-value="mid">中</button>
|
||||
<button type="button" data-visual-setting="shadows" data-visual-value="high">高</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="visual-setting-row">
|
||||
<div><strong>描画</strong><span>装飾と解像度の設定</span></div>
|
||||
<div class="visual-setting-options" role="group" aria-label="描画">
|
||||
<button type="button" data-visual-setting="details" data-visual-value="low">低</button>
|
||||
<button type="button" data-visual-setting="details" data-visual-value="mid">中</button>
|
||||
<button type="button" data-visual-setting="details" data-visual-value="high">高</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -252,164 +257,164 @@
|
|||
</div>
|
||||
|
||||
<div id="logPushOverlay" class="log-push-overlay" aria-live="polite" aria-atomic="false"></div>
|
||||
<script src="js/version.js?v=39.15.101" defer></script>
|
||||
<script src="js/event_bus.js?v=39.15.101" defer></script>
|
||||
<script src="js/registry_base.js?v=39.15.101" defer></script>
|
||||
<script src="js/disease_registry.js?v=39.15.101" defer></script>
|
||||
<script src="js/sound_pack.js?v=39.15.101" defer></script>
|
||||
<script src="js/deterministic_helpers.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_tool_metadata.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_tool_definitions.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_visual_definitions.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_food_definitions.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_effect_definitions.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_registry.js?v=39.15.101" defer></script>
|
||||
<script src="js/data.js?v=39.15.101" defer></script>
|
||||
<script src="js/ground_types.js?v=39.15.101" defer></script>
|
||||
<script src="js/input_mode_manager.js?v=39.15.101" defer></script>
|
||||
<script src="js/math.js?v=39.15.101" defer></script>
|
||||
<script src="js/geometry_helpers.js?v=39.15.101" defer></script>
|
||||
<script src="js/collision_footprint_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/physics_helpers.js?v=39.15.101" defer></script>
|
||||
<script src="js/placement_preview_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/pin_attachment_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/physics_shape_editor_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/mechanical_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/mechanical_shape_bridge.js?v=39.15.101" defer></script>
|
||||
<script src="js/constraint_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/physics_world_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/physics_projection_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/assets.js?v=39.15.101" defer></script>
|
||||
<script src="js/audio.js?v=39.15.101" defer></script>
|
||||
<script src="js/perf_profiler.js?v=39.15.101" defer></script>
|
||||
<script src="js/render.js?v=39.15.101" defer></script>
|
||||
<script src="js/sim_core.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_seed_factory.js?v=39.15.101" defer></script>
|
||||
<script src="js/structures.js?v=39.15.101" defer></script>
|
||||
<script src="js/items.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_type_initializers.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_lifecycle_support.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_update_policy.js?v=39.15.101" defer></script>
|
||||
<script src="js/fire_runtime_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/robot_cleaner_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_dynamic_tool_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_dynamic_ball_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_dynamic_duplicator_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_dynamic_pin_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_dynamic_zunchi_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_dynamic_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_lifecycle_decay_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_lifecycle_growth_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_lifecycle_step_frame.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_lifecycle_step_decay.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_lifecycle_step_dynamic.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_lifecycle_step_growth.js?v=39.15.101" defer></script>
|
||||
<script src="js/update_step_pipeline_runner.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_lifecycle_pipeline.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_runtime.js?v=39.15.101" defer></script>
|
||||
<script src="js/structure_lifecycle.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_render_helpers.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_render_runtime.js?v=39.15.101" defer></script>
|
||||
<script src="js/burn_motion_util.js?v=39.15.101" defer></script>
|
||||
<script src="js/ants.js?v=39.15.101" defer></script>
|
||||
<script src="js/health.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_action_spec.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_behavior_state.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_identity_social.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_action_state.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_disease_nest.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_item_effects.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_needs_core.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_behavior_text.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_forced_behavior.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_nest_sleep_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_item_targeting.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_consumable_behavior.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_social_action_runtime.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_building_behavior.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_action_definitions.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_needs_items.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_food_prototype_mixin.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_need_planner_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_item_interaction_context.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_food_interaction_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_contact_item_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_item_interaction_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_sunbath_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_cursor_care_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_local_environment_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_social_move_life.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_update_step_frame.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_update_step_ai.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_update_step_environment.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_update_step_movement.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_update_step_health.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_update_pipeline.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_runtime.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_render.js?v=39.15.101" defer></script>
|
||||
<script src="js/world.js?v=39.15.101" defer></script>
|
||||
<script src="js/world_view.js?v=39.15.101" defer></script>
|
||||
<script src="js/family_graph.js?v=39.15.101" defer></script>
|
||||
<script src="js/world_reset_presets.js?v=39.15.101" defer></script>
|
||||
<script src="js/world_family_social.js?v=39.15.101" defer></script>
|
||||
<script src="js/impact_core_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/impact_response_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/collision_response_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/world_combat_effects.js?v=39.15.101" defer></script>
|
||||
<script src="js/world_environment.js?v=39.15.101" defer></script>
|
||||
<script src="js/world_temperature_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/world_pathfinding_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/world_grass_placement_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/world_spatial_budget.js?v=39.15.101" defer></script>
|
||||
<script src="js/world_ants_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/weather_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/item_update_scheduler.js?v=39.15.101" defer></script>
|
||||
<script src="js/simulation_runtime_helpers.js?v=39.15.101" defer></script>
|
||||
<script src="js/tarinai_update_policy.js?v=39.15.101" defer></script>
|
||||
<script src="js/simulation_environment_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/simulation_item_ant_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/simulation_effects_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/simulation_creature_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/simulation_maintenance_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/simulation_ambient_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/simulation_systems.js?v=39.15.101" defer></script>
|
||||
<script src="js/system_order.js?v=39.15.101" defer></script>
|
||||
<script src="js/simulation.js?v=39.15.101" defer></script>
|
||||
<script src="js/colony_situation_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/world_update.js?v=39.15.101" defer></script>
|
||||
<script src="js/world_tool_actions.js?v=39.15.101" defer></script>
|
||||
<script src="js/world_placement_log.js?v=39.15.101" defer></script>
|
||||
<script src="js/world_event_effects.js?v=39.15.101" defer></script>
|
||||
<script src="js/command_dispatcher.js?v=39.15.101" defer></script>
|
||||
<script src="js/text_catalog.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_log.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_helpers.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_selected.js?v=39.15.101" defer></script>
|
||||
<script src="js/save_schema.js?v=39.15.101" defer></script>
|
||||
<script src="js/snapshot_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/restore_coordinator.js?v=39.15.101" defer></script>
|
||||
<script src="js/history_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/save_codec.js?v=39.15.101" defer></script>
|
||||
<script src="js/save_storage.js?v=39.15.101" defer></script>
|
||||
<script src="js/save_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_tooltips.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_layout_dialogs.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_ground.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_tools.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_input_shared.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_pointer_action_system.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_input_touch.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_input_mouse.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_bind.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_charts.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_family_data.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_family_async.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_family_layout.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_family_paths.js?v=39.15.101" defer></script>
|
||||
<script src="js/ui_family_render.js?v=39.15.101" defer></script>
|
||||
<script src="js/main.js?v=39.15.101" defer></script>
|
||||
<script src="js/debug_tools.js?v=39.15.101" defer></script>
|
||||
<script src="js/version.js?v=39.15.107" defer></script>
|
||||
<script src="js/event_bus.js?v=39.15.107" defer></script>
|
||||
<script src="js/registry_base.js?v=39.15.107" defer></script>
|
||||
<script src="js/disease_registry.js?v=39.15.107" defer></script>
|
||||
<script src="js/sound_pack.js?v=39.15.107" defer></script>
|
||||
<script src="js/deterministic_helpers.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_tool_metadata.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_tool_definitions.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_visual_definitions.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_food_definitions.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_effect_definitions.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_registry.js?v=39.15.107" defer></script>
|
||||
<script src="js/data.js?v=39.15.107" defer></script>
|
||||
<script src="js/ground_types.js?v=39.15.107" defer></script>
|
||||
<script src="js/input_mode_manager.js?v=39.15.107" defer></script>
|
||||
<script src="js/math.js?v=39.15.107" defer></script>
|
||||
<script src="js/geometry_helpers.js?v=39.15.107" defer></script>
|
||||
<script src="js/collision_footprint_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/physics_helpers.js?v=39.15.107" defer></script>
|
||||
<script src="js/placement_preview_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/pin_attachment_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/physics_shape_editor_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/mechanical_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/mechanical_shape_bridge.js?v=39.15.107" defer></script>
|
||||
<script src="js/constraint_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/physics_world_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/physics_projection_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/assets.js?v=39.15.107" defer></script>
|
||||
<script src="js/audio.js?v=39.15.107" defer></script>
|
||||
<script src="js/perf_profiler.js?v=39.15.107" defer></script>
|
||||
<script src="js/render.js?v=39.15.107" defer></script>
|
||||
<script src="js/sim_core.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_seed_factory.js?v=39.15.107" defer></script>
|
||||
<script src="js/structures.js?v=39.15.107" defer></script>
|
||||
<script src="js/items.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_type_initializers.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_lifecycle_support.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_update_policy.js?v=39.15.107" defer></script>
|
||||
<script src="js/fire_runtime_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/robot_cleaner_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_dynamic_tool_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_dynamic_ball_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_dynamic_duplicator_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_dynamic_pin_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_dynamic_zunchi_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_dynamic_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_lifecycle_decay_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_lifecycle_growth_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_lifecycle_step_frame.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_lifecycle_step_decay.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_lifecycle_step_dynamic.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_lifecycle_step_growth.js?v=39.15.107" defer></script>
|
||||
<script src="js/update_step_pipeline_runner.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_lifecycle_pipeline.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_runtime.js?v=39.15.107" defer></script>
|
||||
<script src="js/structure_lifecycle.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_render_helpers.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_render_runtime.js?v=39.15.107" defer></script>
|
||||
<script src="js/burn_motion_util.js?v=39.15.107" defer></script>
|
||||
<script src="js/ants.js?v=39.15.107" defer></script>
|
||||
<script src="js/health.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_action_spec.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_behavior_state.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_identity_social.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_action_state.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_disease_nest.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_item_effects.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_needs_core.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_behavior_text.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_forced_behavior.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_nest_sleep_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_item_targeting.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_consumable_behavior.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_social_action_runtime.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_building_behavior.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_action_definitions.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_needs_items.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_food_prototype_mixin.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_need_planner_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_item_interaction_context.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_food_interaction_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_contact_item_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_item_interaction_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_sunbath_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_cursor_care_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_local_environment_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_social_move_life.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_update_step_frame.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_update_step_ai.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_update_step_environment.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_update_step_movement.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_update_step_health.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_update_pipeline.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_runtime.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_render.js?v=39.15.107" defer></script>
|
||||
<script src="js/world.js?v=39.15.107" defer></script>
|
||||
<script src="js/world_view.js?v=39.15.107" defer></script>
|
||||
<script src="js/family_graph.js?v=39.15.107" defer></script>
|
||||
<script src="js/world_reset_presets.js?v=39.15.107" defer></script>
|
||||
<script src="js/world_family_social.js?v=39.15.107" defer></script>
|
||||
<script src="js/impact_core_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/impact_response_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/collision_response_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/world_combat_effects.js?v=39.15.107" defer></script>
|
||||
<script src="js/world_environment.js?v=39.15.107" defer></script>
|
||||
<script src="js/world_temperature_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/world_pathfinding_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/world_grass_placement_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/world_spatial_budget.js?v=39.15.107" defer></script>
|
||||
<script src="js/world_ants_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/weather_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/item_update_scheduler.js?v=39.15.107" defer></script>
|
||||
<script src="js/simulation_runtime_helpers.js?v=39.15.107" defer></script>
|
||||
<script src="js/tarinai_update_policy.js?v=39.15.107" defer></script>
|
||||
<script src="js/simulation_environment_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/simulation_item_ant_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/simulation_effects_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/simulation_creature_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/simulation_maintenance_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/simulation_ambient_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/simulation_systems.js?v=39.15.107" defer></script>
|
||||
<script src="js/system_order.js?v=39.15.107" defer></script>
|
||||
<script src="js/simulation.js?v=39.15.107" defer></script>
|
||||
<script src="js/colony_situation_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/world_update.js?v=39.15.107" defer></script>
|
||||
<script src="js/world_tool_actions.js?v=39.15.107" defer></script>
|
||||
<script src="js/world_placement_log.js?v=39.15.107" defer></script>
|
||||
<script src="js/world_event_effects.js?v=39.15.107" defer></script>
|
||||
<script src="js/command_dispatcher.js?v=39.15.107" defer></script>
|
||||
<script src="js/text_catalog.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_log.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_helpers.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_selected.js?v=39.15.107" defer></script>
|
||||
<script src="js/save_schema.js?v=39.15.107" defer></script>
|
||||
<script src="js/snapshot_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/restore_coordinator.js?v=39.15.107" defer></script>
|
||||
<script src="js/history_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/save_codec.js?v=39.15.107" defer></script>
|
||||
<script src="js/save_storage.js?v=39.15.107" defer></script>
|
||||
<script src="js/save_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_tooltips.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_layout_dialogs.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_ground.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_tools.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_input_shared.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_pointer_action_system.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_input_touch.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_input_mouse.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_bind.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_charts.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_family_data.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_family_async.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_family_layout.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_family_paths.js?v=39.15.107" defer></script>
|
||||
<script src="js/ui_family_render.js?v=39.15.107" defer></script>
|
||||
<script src="js/main.js?v=39.15.107" defer></script>
|
||||
<script src="js/debug_tools.js?v=39.15.107" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -428,6 +428,7 @@ function updateFirecracker(item, dt, worldRef) {
|
|||
if (fire) {
|
||||
fire.vx = 0;
|
||||
fire.vy = 0;
|
||||
updateFireContacts(fire, FIRE_CONTACT_INTERVAL, worldRef);
|
||||
}
|
||||
pushFireEffect(worldRef, item.x + Math.cos(a) * Math.min(d, 28), item.y + Math.sin(a) * Math.min(d, 28), 14, item);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ const HEALTH = (() => {
|
|||
stress: "\u30b9\u30c8\u30ec\u30b9\u904e\u591a",
|
||||
poke: "\u3064\u3064\u304b\u308c\u3059\u304e",
|
||||
firecracker: "\u7206\u7af9",
|
||||
fire: "\u708e\u4e0a",
|
||||
genkotsu: "\u3052\u3093\u3053\u3064",
|
||||
shooting: "\u5c04\u6483",
|
||||
stoneCrush: "\u77f3\u306b\u6f70\u3055\u308c\u305f",
|
||||
|
|
@ -48,6 +49,7 @@ const HEALTH = (() => {
|
|||
if (/\u5de8\u5927\u85ac|giant/.test(text)) return CAUSES.giantDrug;
|
||||
if (/\u77ee\u5c0f\u85ac|dwarf/.test(text)) return CAUSES.dwarfDrug;
|
||||
if (/\u7206\u7af9|firecracker|explosion/.test(text)) return CAUSES.firecracker;
|
||||
if (/\u708e\u4e0a|\u706b|\u708e|\u71c3|burn|fire/.test(text)) return CAUSES.fire;
|
||||
if (/\u4ed5\u69d8\u306b\u3088\u308a\u753b\u9762\u5916\u3067\u524a\u9664|\u753b\u9762\u5916\u3067\u524a\u9664|out.*bounds|offscreen/.test(text)) return CAUSES.outOfBounds;
|
||||
if (/\u885d\u7a81|collision|\u6025\u6fc0\u306a\u901f\u5ea6\u5909\u5316/.test(text)) return CAUSES.collision;
|
||||
if (/\u3064\u3064\u304b\u308c|\u3064\u3064\u304d|poke/.test(text)) return CAUSES.poke;
|
||||
|
|
@ -117,6 +119,7 @@ const HEALTH = (() => {
|
|||
return CAUSES.accident;
|
||||
}
|
||||
|
||||
if (direct === CAUSES.fire || recent === CAUSES.fire || t.lastMajorDamageCause === CAUSES.fire) return CAUSES.fire;
|
||||
if (recentMajor && t.lastMajorDamageCause === CAUSES.collision && !(t.energy <= 20 || t.mood <= 16 || t.stress >= 124 || t.hunger >= 112)) return CAUSES.collision;
|
||||
if (recentMajor && (!direct || direct === t.lastMajorDamageCause || t.energy <= 18 || t.mood <= 16 || t.stress >= 124 || t.hunger >= 112)) {
|
||||
return weakenedDeathReasonFor(t.lastMajorDamageCause);
|
||||
|
|
@ -151,7 +154,7 @@ const HEALTH = (() => {
|
|||
const cause = dominantDeathCause(t, text, opts) || CAUSES.accident;
|
||||
if (cause.includes(WEAKENED_SUFFIX)) return cause;
|
||||
if (opts.fromDamage) return cause;
|
||||
const canWeaken = cause && !cause.endsWith("\u75c5") && cause !== CAUSES.hunger && cause !== CAUSES.stress && cause !== CAUSES.lifespan && cause !== CAUSES.outOfBounds && cause !== CAUSES.collision && cause !== CAUSES.genkotsu && cause !== CAUSES.weakness && cause !== CAUSES.freeze && cause !== CAUSES.heatstroke;
|
||||
const canWeaken = cause && !cause.endsWith("\u75c5") && cause !== CAUSES.hunger && cause !== CAUSES.stress && cause !== CAUSES.lifespan && cause !== CAUSES.outOfBounds && cause !== CAUSES.collision && cause !== CAUSES.genkotsu && cause !== CAUSES.fire && cause !== CAUSES.weakness && cause !== CAUSES.freeze && cause !== CAUSES.heatstroke;
|
||||
if (canWeaken && (opts.weakened || (hasRecentMajorDamage(t) && cause === t.lastMajorDamageCause))) return weakenedDeathReasonFor(cause);
|
||||
return cause;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,12 @@
|
|||
changed = true;
|
||||
item.foodServingsRemaining = Math.max(0, before - lost);
|
||||
item.amount = item.foodServingsRemaining;
|
||||
const resized = (typeof updateServingFoodVisualSize === "function" ? updateServingFoodVisualSize(item) : false);
|
||||
if (resized) {
|
||||
worldRef?.markItemBucketsDirty?.("food-passive-resize");
|
||||
worldRef?.markSpatialDirty?.("food-passive-resize");
|
||||
if (worldRef) worldRef.drawListDirty = true;
|
||||
}
|
||||
worldRef?.markTerrainDirty?.("food-passive-decay");
|
||||
worldRef?.emit?.("item:decayed", { item, type: item.type, amount: lost, passive: true });
|
||||
if (item.foodServingsRemaining <= 0.015) item.amount = 0;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ draw(ctx, t, lighting = null) {
|
|||
}
|
||||
const servingRatio = isServingFoodType(this.type) ? ((this.foodServingsRemaining ?? this.amount) / Math.max(1, this.foodServingsMax || foodServingsForSize(this.toolSize || "medium"))) : null;
|
||||
const forceOpaque = this.type === "slave_chain" || this.type === "golden_crown";
|
||||
const visibleAlpha = forceOpaque ? 1 : (isServingFoodType(this.type) ? clamp(servingRatio ?? 1, 0.24, 1) : clamp(this.amount / 40, 0.24, 1));
|
||||
const visibleAlpha = forceOpaque ? 1 : (isServingFoodType(this.type) ? clamp(0.72 + (servingRatio ?? 1) * 0.28, 0.72, 1) : clamp(this.amount / 40, 0.24, 1));
|
||||
const dropT = this.dropMax > 0 ? clamp(this.dropTimer / this.dropMax, 0, 1) : 0;
|
||||
const dropFallHeight = this.type === "genkotsu" ? Math.max(360, this.r * 5.8) : 170;
|
||||
const dropY = dropT > 0 ? -dropFallHeight * dropT : 0;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const TOOL_DEFINITIONS = Object.freeze({
|
|||
poke: { id: "poke", label: "\u3064\u3064\u304f", placeable: false, scalable: false, iconText: "\u{1F448}", tooltip: "\u305f\u308a\u306a\u3044\u3092\u3064\u3064\u304f\u3002\u30dc\u30fc\u30eb\u3082\u3064\u3064\u3044\u3066\u8ee2\u304c\u305b\u308b\u3002" },
|
||||
pinch: { id: "pinch", label: "\u3064\u307e\u3080", placeable: false, scalable: false, iconText: "\u{1F90F}", tooltip: "\u305f\u308a\u306a\u3044\u3084\u4e00\u90e8\u306e\u7f6e\u304d\u3082\u306e\u3092\u79fb\u52d5\u3055\u305b\u308b\u3002" },
|
||||
new: { id: "new", label: "\u8ffd\u52a0", placeable: false, scalable: false, icon: "assets/ui/tool_new.webp", tooltip: "\u753b\u9762\u5916\u304b\u3089\u65b0\u3057\u3044\u305f\u308a\u306a\u3044\u3092\u547c\u3076\u3002" },
|
||||
water_hose: { id: "water_hose", label: "\u6d17\u6d44", placeable: false, scalable: false, iconText: "\u{1F6BF}", tooltip: "\u30c9\u30e9\u30c3\u30b0\u3067\u6c5a\u308c\u3092\u304d\u308c\u3044\u306b\u3059\u308b\u3002" },
|
||||
water_hose: { id: "water_hose", label: "\u6d17\u6d44", placeable: false, scalable: false, iconText: "\u{1F6BF}", tooltip: "\u30c9\u30e9\u30c3\u30b0\u3067\u6c5a\u308c\u3084\u706b\u3092\u304d\u308c\u3044\u306b\u3059\u308b\u3002" },
|
||||
rope: { id: "rope", label: "\u30d2\u30e2", placeable: false, scalable: false, iconText: "\u2570", tooltip: "2\u3064\u306e\u5bfe\u8c61\u3092\u9806\u306b\u30af\u30ea\u30c3\u30af\u3057\u3066\u3075\u306b\u3083\u3075\u306b\u3083\u3057\u305f\u30d2\u30e2\u3067\u7e4b\u3050\u3002" },
|
||||
rod: { id: "rod", label: "\u68d2", placeable: false, scalable: false, iconText: "\u2501", tooltip: "2\u3064\u306e\u5bfe\u8c61\u3092\u9806\u306b\u30af\u30ea\u30c3\u30af\u3057\u3066\u3001\u66f2\u304c\u3089\u306a\u3044\u68d2\u3067\u63a5\u7d9a\u3059\u308b\u3002" },
|
||||
shoot: { id: "shoot", label: "\u5c04\u6483", placeable: false, scalable: false, icon: "assets/ui/tool_shoot.png", tooltip: "\u9283\u3067\u653b\u6483\u3059\u308b\u3002" },
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@ function initializeItemTypeState(item, type, x, y) {
|
|||
item.foodServingsRemaining = item.foodServingsMax;
|
||||
item.passiveFoodDecayTimer = rand(0, passiveFoodDecayInterval());
|
||||
item.amount = item.foodServingsRemaining;
|
||||
(typeof updateServingFoodVisualSize === "function" ? updateServingFoodVisualSize(item) : false);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
|
|
|||
26
js/items.js
26
js/items.js
|
|
@ -14,6 +14,29 @@ function foodServingsForSize(size = "medium") {
|
|||
function isServingFoodType(type = "") {
|
||||
return FOOD_SERVING_TYPES.has(type);
|
||||
}
|
||||
function foodServingSizeForRemaining(remaining = FOOD_SERVINGS_BY_SIZE.medium) {
|
||||
const n = Math.max(0, Number(remaining) || 0);
|
||||
if (n <= FOOD_SERVINGS_BY_SIZE.small + 0.05) return "small";
|
||||
if (n <= FOOD_SERVINGS_BY_SIZE.medium + 0.05) return "medium";
|
||||
return "large";
|
||||
}
|
||||
function foodServingVisualScaleFor(item) {
|
||||
if (!item || !isServingFoodType(item.type)) return 1;
|
||||
const remaining = Math.max(0, Number(item.foodServingsRemaining ?? item.amount ?? FOOD_SERVINGS_BY_SIZE.medium) || 0);
|
||||
const ratio = remaining / FOOD_SERVINGS_BY_SIZE.medium;
|
||||
return clamp(Math.sqrt(Math.max(0.04, ratio)), 0.72, 1.35);
|
||||
}
|
||||
function updateServingFoodVisualSize(item) {
|
||||
if (!item || !isServingFoodType(item.type)) return false;
|
||||
const baseRadius = itemRadiusFor(item.type, 12);
|
||||
const scale = foodServingVisualScaleFor(item);
|
||||
const nextR = Math.max(5, baseRadius * scale);
|
||||
const changed = Math.abs((Number(item.r) || 0) - nextR) > 0.05;
|
||||
item.r = nextR;
|
||||
item.foodServingScale = scale;
|
||||
item.foodServingVisualSize = foodServingSizeForRemaining(item.foodServingsRemaining ?? item.amount);
|
||||
return changed;
|
||||
}
|
||||
const PASSIVE_FOOD_DECAY_PER_SECOND = 0.004;
|
||||
|
||||
const GRASS_STAGE_COUNT = 5;
|
||||
|
|
@ -600,6 +623,9 @@ function drawToolItemPreview(ctx, type = "", opts = {}) {
|
|||
|
||||
if (typeof window !== "undefined") {
|
||||
window.isConsumableSpriteType = isConsumableSpriteType;
|
||||
window.foodServingSizeForRemaining = foodServingSizeForRemaining;
|
||||
window.foodServingVisualScaleFor = foodServingVisualScaleFor;
|
||||
window.updateServingFoodVisualSize = updateServingFoodVisualSize;
|
||||
window.drawConsumableFieldSprite = drawConsumableFieldSprite;
|
||||
window.drawStoredConsumableSprite = drawStoredConsumableSprite;
|
||||
window.drawToolItemPreview = drawToolItemPreview;
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@
|
|||
let lastQualityCheckAt = 0;
|
||||
const storageKey = "tarinai_visual_settings_v1";
|
||||
const allowed = {
|
||||
effects: new Set(["off", "on"]),
|
||||
shadows: new Set(["off", "on"]),
|
||||
details: new Set(["low", "high"]),
|
||||
effects: new Set(["off", "low", "mid", "high"]),
|
||||
shadows: new Set(["off", "low", "mid", "high"]),
|
||||
details: new Set(["low", "mid", "high"]),
|
||||
};
|
||||
const defaults = Object.freeze({ effects: "on", shadows: "on", details: "high" });
|
||||
const defaults = Object.freeze({ effects: "high", shadows: "high", details: "high" });
|
||||
let visualSettings = loadVisualSettings();
|
||||
let resizeRequested = false;
|
||||
|
||||
|
|
@ -83,11 +83,12 @@
|
|||
const raw = String(value || "");
|
||||
if (key === "effects" || key === "shadows") {
|
||||
if (raw === "off") return "off";
|
||||
if (raw === "on" || raw === "low" || raw === "mid" || raw === "high") return "on";
|
||||
if (raw === "on") return "high";
|
||||
if (raw === "low" || raw === "mid" || raw === "high") return raw;
|
||||
}
|
||||
if (key === "details") {
|
||||
if (raw === "low") return "low";
|
||||
if (raw === "high" || raw === "mid") return "high";
|
||||
if (raw === "mid" || raw === "high") return raw;
|
||||
}
|
||||
return defaults[key];
|
||||
}
|
||||
|
|
@ -119,6 +120,7 @@
|
|||
|
||||
function dprScaleForDetails(details) {
|
||||
if (details === "low") return 0.72;
|
||||
if (details === "mid") return 0.86;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -182,7 +184,7 @@
|
|||
function renderQualityTier() { return visualSettings.details; }
|
||||
function shadowQualityTier() { return visualSettings.shadows; }
|
||||
function effectsQualityTier() { return visualSettings.effects; }
|
||||
function simulationQualityTier() { return visualSettings.details; }
|
||||
function simulationQualityTier() { return "high"; }
|
||||
function dprScaleValue() { return dprScaleForDetails(visualSettings.details); }
|
||||
function consumeResizeRequest() {
|
||||
const v = resizeRequested;
|
||||
|
|
|
|||
|
|
@ -80,6 +80,12 @@
|
|||
const sizeName = opts.toolSize ?? toolSizeName(worldRef, type);
|
||||
const r = (opts.r ?? itemRadius(type, 12)) * scale;
|
||||
const item = { type, x: num(x), y: num(y), r, toolSize: sizeName, foodServingScale: scale, dead: false, isPlacementPreview: true };
|
||||
if (isServingFoodType(type)) {
|
||||
item.foodServingsMax = foodServingsForSize(sizeName);
|
||||
item.foodServingsRemaining = item.foodServingsMax;
|
||||
item.amount = item.foodServingsRemaining;
|
||||
(typeof updateServingFoodVisualSize === "function" ? updateServingFoodVisualSize(item) : false);
|
||||
}
|
||||
if (isRotatable(type)) item.angle = opts.angle ?? (worldRef?.toolAngleFor ? worldRef.toolAngleFor(type) : defaultAngle(type));
|
||||
if (type === "reciprocator") {
|
||||
const axis = item.angle ?? defaultAngle(type);
|
||||
|
|
|
|||
122
js/save_codec.js
122
js/save_codec.js
|
|
@ -426,6 +426,16 @@
|
|||
if (SERVING_FOOD_TYPE_IDS.has(typeId)) return false;
|
||||
return true;
|
||||
}
|
||||
function defaultStructureHp10(typeId) {
|
||||
if (ITEM_TYPE_IDS[typeId] === "grass_bed") return 1000;
|
||||
if (ITEM_TYPE_IDS[typeId] === "plushie") return 10;
|
||||
return 10;
|
||||
}
|
||||
function defaultNeedsFromVitals(hunger = 0, energy = 0, sleepPressure = 0) {
|
||||
const food = Math.max(0, Math.min(100, Math.round(Number(hunger) || 0)));
|
||||
const sleep = Math.max(0, Math.min(100, Math.round(Math.max(Number(sleepPressure) || 0, 100 - (Number(energy) || 0)))));
|
||||
return [food, sleep, 0, 0, 0, 0];
|
||||
}
|
||||
function writeRelationships(writer, rows = []) {
|
||||
const list = Array.isArray(rows) ? rows : [];
|
||||
writer.u(list.length);
|
||||
|
|
@ -523,19 +533,17 @@
|
|||
const defaultPersonality = seedPersonalityArray(birthSeed, flags);
|
||||
const defaultTimers = [0, 0, 0, 0, 0, 0, 0, 0, 100];
|
||||
let bodyMask = 0;
|
||||
if (arrayHasValues(row[9], [0, 0, 0, 0, 0, 0])) bodyMask |= 1;
|
||||
if (arrayHasValues(row[10], defaultPersonality)) bodyMask |= 2;
|
||||
if ((row[11] || []).length) bodyMask |= 4;
|
||||
if ((row[12] || []).length) bodyMask |= 8;
|
||||
if (arrayHasValues(row[13], defaultTimers)) bodyMask |= 16;
|
||||
if (arrayHasValues(row[14], [0, 0, 0])) bodyMask |= 32;
|
||||
writeBitFields(writer, [bodyMask], [6]);
|
||||
if (bodyMask & 1) writeFixedPacked(writer, row[9], 6, 6, 2);
|
||||
if (bodyMask & 2) writeFixedDiffPacked(writer, defaultPersonality, row[10], 4, 8);
|
||||
if (bodyMask & 4) writeIndexList(writer, row[11]);
|
||||
if (bodyMask & 8) writeRelationships(writer, row[12]);
|
||||
if (bodyMask & 16) writeTimers(writer, row[13]);
|
||||
if (bodyMask & 32) writeModes(writer, row[14]);
|
||||
if (arrayHasValues(row[10], defaultPersonality)) bodyMask |= 1;
|
||||
if ((row[11] || []).length) bodyMask |= 2;
|
||||
if ((row[12] || []).length) bodyMask |= 4;
|
||||
if (arrayHasValues(row[13], defaultTimers)) bodyMask |= 8;
|
||||
if (arrayHasValues(row[14], [0, 0, 0])) bodyMask |= 16;
|
||||
writeBitFields(writer, [bodyMask], [5]);
|
||||
if (bodyMask & 1) writeFixedDiffPacked(writer, defaultPersonality, row[10], 4, 8);
|
||||
if (bodyMask & 2) writeIndexList(writer, row[11]);
|
||||
if (bodyMask & 4) writeRelationships(writer, row[12]);
|
||||
if (bodyMask & 8) writeTimers(writer, row[13]);
|
||||
if (bodyMask & 16) writeModes(writer, row[14]);
|
||||
let tailMask = 0;
|
||||
if (pinIdx >= 0) tailMask |= 1;
|
||||
if (nestIdx >= 0) tailMask |= 2;
|
||||
|
|
@ -558,13 +566,13 @@
|
|||
const generation = (coordState?.generation || 1) + reader.s();
|
||||
if (coordState) { coordState.x = x; coordState.y = y; coordState.age = age; coordState.generation = generation; }
|
||||
const [hunger, energy, sleepPressure] = readBitFields(reader, [8, 8, 8]);
|
||||
const [bodyMask] = readBitFields(reader, [6]);
|
||||
const needs = (bodyMask & 1) ? readFixedPacked(reader, 6, 6, 2) : [0, 0, 0, 0, 0, 0];
|
||||
const current = (bodyMask & 2) ? readFixedDiffPacked(reader, seedPersonalityArray(birthSeed, flags), 4, 8) : seedPersonalityArray(birthSeed, flags);
|
||||
const parents = (bodyMask & 4) ? readIndexList(reader) : [];
|
||||
const relationships = (bodyMask & 8) ? readRelationships(reader) : [];
|
||||
const timers = (bodyMask & 16) ? readTimers(reader) : [0, 0, 0, 0, 0, 0, 0, 0, 100];
|
||||
const modes = (bodyMask & 32) ? readModes(reader) : [0, 0, 0];
|
||||
const [bodyMask] = readBitFields(reader, [5]);
|
||||
const needs = defaultNeedsFromVitals(hunger, energy, sleepPressure);
|
||||
const current = (bodyMask & 1) ? readFixedDiffPacked(reader, seedPersonalityArray(birthSeed, flags), 4, 8) : seedPersonalityArray(birthSeed, flags);
|
||||
const parents = (bodyMask & 2) ? readIndexList(reader) : [];
|
||||
const relationships = (bodyMask & 4) ? readRelationships(reader) : [];
|
||||
const timers = (bodyMask & 8) ? readTimers(reader) : [0, 0, 0, 0, 0, 0, 0, 0, 100];
|
||||
const modes = (bodyMask & 16) ? readModes(reader) : [0, 0, 0];
|
||||
const [tailMask] = readBitFields(reader, [5]);
|
||||
const pinIdx = (tailMask & 1) ? reader.u() : -1;
|
||||
const nestIdx = (tailMask & 2) ? reader.u() : -1;
|
||||
|
|
@ -576,31 +584,41 @@
|
|||
|
||||
function writeItemExtra(writer, typeId, extra = []) {
|
||||
if (STRUCTURE_TYPE_IDS.has(typeId)) {
|
||||
writer.s(extra[0] || 0);
|
||||
writer.s(extra[1] || 0);
|
||||
const defaultHp = defaultStructureHp10(typeId);
|
||||
const hp = toSafeInt(extra[0] || 0);
|
||||
const maxHp = toSafeInt(extra[1] || 0);
|
||||
let mask = 0;
|
||||
if ((extra[2] ?? -1) >= 0) mask |= 1;
|
||||
if ((extra[3] ?? -1) >= 0) mask |= 2;
|
||||
if (extra[4]) mask |= 4;
|
||||
if ((extra[5] ?? -1) >= 0) mask |= 8;
|
||||
if (valuesDiffer(extra[6], 0)) mask |= 16;
|
||||
writeBitFields(writer, [mask], [5]);
|
||||
if (mask & 1) writer.u(extra[2]);
|
||||
if (mask & 2) writer.u(extra[3]);
|
||||
if (mask & 8) writer.u(extra[5]);
|
||||
if (mask & 16) writer.s(extra[6] || 0);
|
||||
if (valuesDiffer(hp, defaultHp)) mask |= 1;
|
||||
if (valuesDiffer(maxHp, defaultHp)) mask |= 2;
|
||||
if ((extra[2] ?? -1) >= 0) mask |= 4;
|
||||
if ((extra[3] ?? -1) >= 0) mask |= 8;
|
||||
if (extra[4]) mask |= 16;
|
||||
if ((extra[5] ?? -1) >= 0) mask |= 32;
|
||||
if (valuesDiffer(extra[6], 0)) mask |= 64;
|
||||
writeBitFields(writer, [mask], [7]);
|
||||
if (mask & 1) writer.s(hp);
|
||||
if (mask & 2) writer.s(maxHp);
|
||||
if (mask & 4) writer.u(extra[2]);
|
||||
if (mask & 8) writer.u(extra[3]);
|
||||
if (mask & 32) writer.u(extra[5]);
|
||||
if (mask & 64) writer.s(extra[6] || 0);
|
||||
} else if (typeId === 0) { // grass
|
||||
writer.s(extra[0] || 0); writer.s(extra[1] || 0);
|
||||
writer.s(extra[0] || 0);
|
||||
let mask = 0;
|
||||
if (valuesDiffer(extra[2], 0)) mask |= 1;
|
||||
if (extra[3]) mask |= 2;
|
||||
writeBitFields(writer, [mask], [2]);
|
||||
if (mask & 1) writer.s(extra[2] || 0);
|
||||
if (valuesDiffer(extra[1], 100)) mask |= 1;
|
||||
if (valuesDiffer(extra[2], 0)) mask |= 2;
|
||||
if (extra[3]) mask |= 4;
|
||||
writeBitFields(writer, [mask], [3]);
|
||||
if (mask & 1) writer.s(extra[1] || 0);
|
||||
if (mask & 2) writer.s(extra[2] || 0);
|
||||
} else if (typeId === 1) { // zunchi
|
||||
writeBitFields(writer, [extra[0] || 0], [2]);
|
||||
writer.s(extra[1] || 0);
|
||||
if (valuesDiffer(extra[2], 0)) { writer.u(1); writer.s(extra[2] || 0); }
|
||||
else writer.u(0);
|
||||
let mask = 0;
|
||||
if (valuesDiffer(extra[1], 100)) mask |= 1;
|
||||
if (valuesDiffer(extra[2], 0)) mask |= 2;
|
||||
writeBitFields(writer, [mask], [2]);
|
||||
if (mask & 1) writer.s(extra[1] || 0);
|
||||
if (mask & 2) writer.s(extra[2] || 0);
|
||||
} else if (typeId === 22) { // signboard
|
||||
writeSaveString(writer, extra[0] || "");
|
||||
} else if (typeId === 6) { // duplicator
|
||||
|
|
@ -643,29 +661,29 @@
|
|||
}
|
||||
function readItemExtra(reader, typeId) {
|
||||
if (STRUCTURE_TYPE_IDS.has(typeId)) {
|
||||
const hp = reader.s();
|
||||
const maxHp = reader.s();
|
||||
const [mask] = readBitFields(reader, [5]);
|
||||
const defaultHp = defaultStructureHp10(typeId);
|
||||
const [mask] = readBitFields(reader, [7]);
|
||||
const hp = (mask & 1) ? reader.s() : defaultHp;
|
||||
const maxHp = (mask & 2) ? reader.s() : defaultHp;
|
||||
return [
|
||||
hp,
|
||||
maxHp,
|
||||
(mask & 1) ? reader.u() : -1,
|
||||
(mask & 2) ? reader.u() : -1,
|
||||
(mask & 4) ? 1 : 0,
|
||||
(mask & 4) ? reader.u() : -1,
|
||||
(mask & 8) ? reader.u() : -1,
|
||||
(mask & 16) ? reader.s() : 0,
|
||||
(mask & 16) ? 1 : 0,
|
||||
(mask & 32) ? reader.u() : -1,
|
||||
(mask & 64) ? reader.s() : 0,
|
||||
];
|
||||
}
|
||||
if (typeId === 0) {
|
||||
const growth = reader.s();
|
||||
const health = reader.s();
|
||||
const [mask] = readBitFields(reader, [2]);
|
||||
return [growth, health, (mask & 1) ? reader.s() : 0, (mask & 2) ? 1 : 0];
|
||||
const [mask] = readBitFields(reader, [3]);
|
||||
return [growth, (mask & 1) ? reader.s() : 100, (mask & 2) ? reader.s() : 0, (mask & 4) ? 1 : 0];
|
||||
}
|
||||
if (typeId === 1) {
|
||||
const [stage] = readBitFields(reader, [2]);
|
||||
const freshness = reader.s();
|
||||
return [stage, freshness, reader.u() ? reader.s() : 0];
|
||||
const [mask] = readBitFields(reader, [2]);
|
||||
return [stage, (mask & 1) ? reader.s() : 100, (mask & 2) ? reader.s() : 0];
|
||||
}
|
||||
if (typeId === 22) return [readSaveString(reader)];
|
||||
if (typeId === 6) return [reader.s()];
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
// Owns stable save identifiers shared by snapshot_system.js and save_codec.js.
|
||||
// Current-format save identifiers only.
|
||||
(function (global) {
|
||||
const SNAPSHOT_VERSION = 17;
|
||||
const BINARY_SCHEMA_VERSION = 22;
|
||||
const SNAPSHOT_VERSION = 18;
|
||||
const BINARY_SCHEMA_VERSION = 23;
|
||||
|
||||
const FIELD_IDS = Object.freeze(["garden", "cage", "park"]);
|
||||
const GROUND_IDS = Object.freeze(["soil", "dirt", "concrete", "blanket", "foot_massage", "ice", "laboratory"]);
|
||||
|
|
|
|||
|
|
@ -369,7 +369,7 @@
|
|||
item.angle = typeof global.normalizedItemAngle === "function" ? global.normalizedItemAngle(u(extra[0], 1000, fallback), fallback) : u(extra[0], 1000, fallback);
|
||||
if (type === "gate_fence") item.gateOpen = !!extra[1];
|
||||
} else if (typeof global.isServingFoodType === "function" && global.isServingFoodType(type)) {
|
||||
item.foodServingsRemaining = u(extra[0], 10, item.amount || 1); item.foodServingsMax = u(extra[1], 10, item.foodServingsRemaining || item.amount || 1); item.amount = item.foodServingsRemaining;
|
||||
item.foodServingsRemaining = u(extra[0], 10, item.amount || 1); item.foodServingsMax = u(extra[1], 10, item.foodServingsRemaining || item.amount || 1); item.amount = item.foodServingsRemaining; (typeof updateServingFoodVisualSize === "function" ? updateServingFoodVisualSize(item) : false);
|
||||
}
|
||||
}
|
||||
function createSnapshot(worldRef = global.world) {
|
||||
|
|
@ -482,6 +482,9 @@
|
|||
t.relationships = expandRelationships(t.__savedRelationships || [], worldRef.tarinai, worldRef);
|
||||
delete t.__savedPinIdx; delete t.__savedNestIdx; delete t.__savedRelationships; delete t.__savedStateInfo;
|
||||
}
|
||||
if (typeof global.updateNeeds === "function") {
|
||||
for (const t of worldRef.tarinai) global.updateNeeds(t, worldRef, 0);
|
||||
}
|
||||
worldRef.liveIdNext = Math.max(1, worldRef.tarinai.length + 1);
|
||||
worldRef.liveIdSerial = Math.max(1, worldRef.tarinai.length + 1);
|
||||
worldRef.birthSerial = Math.max(Number(worldRef.birthSerial) || 0, ...worldRef.tarinai.map(t => Number(t.birthSerial) || 0));
|
||||
|
|
|
|||
|
|
@ -115,6 +115,14 @@
|
|||
target.singleUsePending = true;
|
||||
target.singleUseConsumedById = this.id || target.singleUseConsumedById || "";
|
||||
}
|
||||
if (target && target.type !== "nest_box") {
|
||||
this._sleepExternalMotionUntil = 0;
|
||||
this._sleepExternalWakeSuppressUntil = Math.max(Number(this._sleepExternalWakeSuppressUntil || 0) || 0, now + 2.4);
|
||||
this.vx = 0;
|
||||
this.vy = 0;
|
||||
this.impulseVx = 0;
|
||||
this.impulseVy = 0;
|
||||
}
|
||||
const text = reason || (target?.type === "nest_box" ? "\u5de3\u7bb1\u306e\u4e2d\u3067\u7720\u3063\u3066\u3044\u308b" : "\u7720\u3063\u3066\u3044\u308b");
|
||||
setBehaviorText(this, { need: "sleep", actionId: target ? "sleep_in_bed" : "sleep_anywhere", actionLabel: "\u7720\u3063\u3066\u3044\u308b", reasonText: text, target, phase: "perform", source: "behavior" });
|
||||
return this.setActionState("sleep", { target, reason: text, sleeping: true });
|
||||
|
|
@ -138,9 +146,9 @@
|
|||
const sleptFor = Number.isFinite(started) ? Math.max(0, now - started) : 0;
|
||||
const wasActuallySleeping = Boolean((this.state === "sleep" || this.sleeping || session) && session?.consumesGrassBedOnWake);
|
||||
// Reserve-on-use must not consume the simple bed. It is consumed only by
|
||||
// a real wake after sleep has started; pre-sleep wake flags just release
|
||||
// the reservation and leave the bed intact.
|
||||
if (!wasActuallySleeping || sleptFor < 0.75) {
|
||||
// a real wake after sleep has had time to settle; immediate collision or
|
||||
// movement wakeups just release the reservation and leave the bed intact.
|
||||
if (!wasActuallySleeping || sleptFor < 2) {
|
||||
if (bed.singleUseConsumedById === this.id) bed.singleUseConsumedById = "";
|
||||
if (bed.singleUsePending && (!bed.singleUseConsumedById || bed.singleUseConsumedById === this.id)) bed.singleUsePending = false;
|
||||
this.pendingGrassBedWakeId = "";
|
||||
|
|
|
|||
|
|
@ -518,8 +518,13 @@
|
|||
if (typeof this.enterPanic === "function") this.enterPanic({ threat: source, target: null, reason: text, fear: 2.2, wake: true, cause: "burning", surpriseTimer: 0.65, awakeLockTimer: 0.8 });
|
||||
else this.setActionState?.("panic", { target: null, reason: text, wake: true });
|
||||
this.world?.effects?.push(new Effect("ring", this.x, this.y, { size: Math.max(26, this.radius * 1.35), life: 0.26, color: "rgba(255,110,36,0.58)" }));
|
||||
const now = Number(this.world?.time || 0);
|
||||
if (force || !already || now >= Number(this.nextBurnVoiceAt || -999)) {
|
||||
this.nextBurnVoiceAt = now + 2.6;
|
||||
this.bubble?.("\u3042\u3061\u3085\u3044\uff01", 1.35, "rgba(214,82,38,0.90)", { force: true });
|
||||
}
|
||||
this.world?.emit?.("fire:ignite", { tarinai: this, source });
|
||||
this.recordChangeCause?.("\u708e\u4e0a", "\u708e");
|
||||
this.recordChangeCause?.("\u708e\u4e0a", "\u708e\u4e0a");
|
||||
return true;
|
||||
},
|
||||
|
||||
|
|
@ -548,7 +553,7 @@
|
|||
this.sleeping = false;
|
||||
this.eatTimer = 0;
|
||||
if (typeof applyNeedShock === "function") applyNeedShock(this, { health: dt * 6.2, safety: dt * 3.0 });
|
||||
if (typeof this.damage === "function") this.damage(dt * 3.8, "\u708e");
|
||||
if (typeof this.damage === "function") this.damage(dt * 3.8, "\u708e\u4e0a");
|
||||
else this.energy = clamp((this.energy || 0) - dt * 0.12, 0, typeof tarinaiMaxEnergy === "function" ? tarinaiMaxEnergy(this) : (Number(this.maxEnergy) || 100));
|
||||
this.hurtTimer = Math.max(this.hurtTimer || 0, 0.22);
|
||||
const water = globalThis.TarinaiItemDynamicToolSystem?.contactWaterDrop?.(this.world, this.x, this.y, Math.max(this.radius || 18, 16));
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
}
|
||||
it.foodServingsRemaining = clamp(it.foodServingsRemaining, 0, it.foodServingsMax);
|
||||
it.amount = it.foodServingsRemaining;
|
||||
(typeof updateServingFoodVisualSize === "function" ? updateServingFoodVisualSize(it) : false);
|
||||
},
|
||||
consumeFoodServing(it, nutrition) {
|
||||
if (it?.type === "duplicator") {
|
||||
|
|
@ -44,6 +45,12 @@
|
|||
const value = Number.isFinite(nutrition) ? nutrition : (window.TarinaiItemRegistry.food?.nutrition?.(it.type, 0) ?? 0);
|
||||
it.foodServingsRemaining = Math.max(0, (it.foodServingsRemaining || 0) - 1);
|
||||
it.amount = it.foodServingsRemaining;
|
||||
const resized = (typeof updateServingFoodVisualSize === "function" ? updateServingFoodVisualSize(it) : false);
|
||||
if (resized) {
|
||||
this.world?.markItemBucketsDirty?.("food-serving-resize");
|
||||
this.world?.markSpatialDirty?.("food-serving-resize");
|
||||
this.world && (this.world.drawListDirty = true);
|
||||
}
|
||||
this.world?.emit?.("item:eaten", { tarinai: this, item: it, type: it.type, nutrition: value });
|
||||
return value;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -554,12 +554,17 @@ function shouldWakeFromSleep(tarinai, world, needs) {
|
|||
const session = tarinai.sleepSession || { startedAt: now, minDuration: 8, targetEnergy: 74, maxDuration: 28 };
|
||||
const started = Number(session.startedAt);
|
||||
const elapsed = Math.max(0, now - (Number.isFinite(started) ? started : now));
|
||||
const minSleepDuration = Math.max(2, Number(session.minDuration) || 8);
|
||||
|
||||
// 明確な妨害だけは最低睡眠時間より優先して起床させる。
|
||||
if ((tarinai.hurtTimer || 0) > 0.06 || (tarinai.pokeFlashTimer || 0) > 0.08) return true;
|
||||
if (findNearbyDanger(world, tarinai, 160)) return true;
|
||||
|
||||
// 空腹・睡眠欲の回復・体力回復などの通常要因では、最低2時間は寝続ける。
|
||||
if (elapsed < minSleepDuration) return false;
|
||||
|
||||
const foodNeed = Number(needs?.food ?? tarinai.needRaw?.food ?? tarinai.needs?.food ?? 0) || 0;
|
||||
if ((tarinai.hunger || 0) >= 84 || foodNeed >= needThreshold("food", "start")) return true;
|
||||
// \u81ea\u7136\u306a\u7761\u7720\u306f\u77ed\u3044\u5b89\u5168\u30ed\u30c3\u30af\u5f8c\u3001\u5341\u5206\u306b\u56de\u5fa9\u3057\u305f\u3089\u8d77\u304d\u308b\u3002
|
||||
if (elapsed < (Number(session.minDuration) || 8)) return false;
|
||||
const sleepNeed = Number(needs?.sleep ?? tarinai.needRaw?.sleep ?? tarinai.needs?.sleep ?? 0) || 0;
|
||||
if (sleepNeed <= ((typeof needThreshold === "function" ? needThreshold("sleep", "continue") : 34) + 4)) return true;
|
||||
if ((tarinai.energy || 0) >= (Number(session.targetEnergy) || 74)) return true;
|
||||
|
|
@ -567,6 +572,7 @@ function shouldWakeFromSleep(tarinai, world, needs) {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
function protectSleepSession(tarinai, world, needs) {
|
||||
if (!(tarinai?.state === "sleep" || tarinai?.sleeping)) return false;
|
||||
tarinai.sleeping = true;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
const near = typeof worldRef.nearbyTarinai === "function" ? worldRef.nearbyTarinai(bed.x, bed.y, radius) : (worldRef.tarinai || []);
|
||||
for (const t of near || []) {
|
||||
if (!t || t.dead || isTarinaiHiddenInNestBox(worldRef, t)) continue;
|
||||
if (t.state === "sleep" || t.state === "seek_bed" || t.target === bed) n += 1;
|
||||
if (t.target === bed && (t.state === "sleep" || t.state === "seek_bed" || t.sleeping)) n += 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
|
@ -81,7 +81,7 @@
|
|||
if (sleepTargetBlocked(worldRef, tarinai, item)) return false;
|
||||
const capacity = item.type === "nest_box" ? nestBoxCapacity(worldRef, item) : 1;
|
||||
const occupancy = bedOccupancy(worldRef, item);
|
||||
return occupancy < capacity || (tarinai.insideNestBoxId && tarinai.insideNestBoxId === item.id);
|
||||
return occupancy < capacity || tarinai.target === item || (tarinai.insideNestBoxId && tarinai.insideNestBoxId === item.id);
|
||||
}
|
||||
|
||||
function findStructure(worldRef, tarinai, opts = {}) {
|
||||
|
|
@ -135,12 +135,12 @@
|
|||
return findOwnedStructure(worldRef, tarinai, "grass_bed", maxDist)
|
||||
|| nearestSleepPlaceOfType(worldRef, tarinai, "nest_box", maxDist)
|
||||
|| nearestSleepPlaceOfType(worldRef, tarinai, "bed", maxDist)
|
||||
|| nearestSleepPlaceOfType(worldRef, tarinai, "grass_bed", maxDist)
|
||||
|| null;
|
||||
}
|
||||
|
||||
function findNearestSleepPlace(worldRef, tarinai, maxDist = 520) {
|
||||
return findPrioritySleepPlaceBeforeGrassBedBuild(worldRef, tarinai, maxDist)
|
||||
|| findUnownedStructure(worldRef, tarinai, "grass_bed", maxDist)
|
||||
|| null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -581,11 +581,13 @@
|
|||
|
||||
const hpActive = (this.hpBarTimer || 0) > 0;
|
||||
const stressActive = (this.stressBarTimer || 0) > 0;
|
||||
if ((hpActive || stressActive) && (showDecor || this.world.selected === this || this.hurtTimer > 0.04)) {
|
||||
if (hpActive || stressActive) {
|
||||
const bw = Math.max(28, this.radius * 2.35);
|
||||
const bh = 5;
|
||||
const x = this.x - bw / 2;
|
||||
const baseY = this.y - drawH * 0.55 - 16;
|
||||
const stressY = hpActive ? baseY + 7 : baseY;
|
||||
const hpY = stressActive ? baseY - 7 : baseY;
|
||||
const drawMeter = (y, pct, fill, alpha) => {
|
||||
ctx.save();
|
||||
ctx.globalAlpha = alpha;
|
||||
|
|
@ -607,11 +609,11 @@
|
|||
};
|
||||
if (stressActive) {
|
||||
const pct = clamp((this.stress || 0) / 130, 0, 1);
|
||||
drawMeter(baseY, pct, pct > 0.72 ? "rgba(178,74,110,0.92)" : "rgba(212,126,84,0.90)", clamp((this.stressBarTimer || 0) / 0.7, 0.35, 1));
|
||||
drawMeter(stressY, pct, pct > 0.72 ? "rgba(178,74,110,0.92)" : "rgba(212,126,84,0.90)", clamp((this.stressBarTimer || 0) / 0.7, 0.35, 1));
|
||||
}
|
||||
if (hpActive) {
|
||||
const pct = typeof tarinaiEnergyRatio === "function" ? tarinaiEnergyRatio(this) : clamp(this.energy / Math.max(1, this.maxEnergy || 100), 0, 1);
|
||||
drawMeter(baseY - (stressActive ? 11 : 0), pct, pct > 0.45 ? "rgba(118,190,80,0.88)" : (pct > 0.20 ? "rgba(232,172,68,0.90)" : "rgba(218,82,70,0.92)"), clamp((this.hpBarTimer || 0) / 0.7, 0.35, 1));
|
||||
drawMeter(hpY, pct, pct > 0.45 ? "rgba(118,190,80,0.88)" : (pct > 0.20 ? "rgba(232,172,68,0.90)" : "rgba(218,82,70,0.92)"), clamp((this.hpBarTimer || 0) / 0.7, 0.35, 1));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ TEXT_CATALOG.targetLabel = function targetLabelFromCatalog(target) {
|
|||
|
||||
TEXT_CATALOG.reasonLabel = function reasonLabelFromCatalog(t) {
|
||||
if (!t) return "\u306a\u3057";
|
||||
if ((t.pinchThoughtTimer || 0) > 0) return "\u3064\u307e\u307e\u308c\u3066\u3044\u308b";
|
||||
if ((t.pinchThoughtTimer || 0) > 0 && (t.playerHeld || t._heldByPlayer)) return "\u3064\u307e\u307e\u308c\u3066\u3044\u308b";
|
||||
if (t.lastPanicCause === "tarinai_champion" && (t.state === "panic" || (t.fearTimer || 0) > 0.04)) return "\u305F\u308A\u306A\u3044\u738B\u304C\u6016\u304F\u3066\u9003\u3052\u3066\u3044\u308B\u3002";
|
||||
const behaviorState = currentTarinaiBehavior(t);
|
||||
const behaviorLabel = behaviorText(t);
|
||||
|
|
|
|||
|
|
@ -102,6 +102,24 @@ function lineagePartnerPathFromPositions(a, b, nodeW, nodeH, unionY, attachOffse
|
|||
if (a.rowIndex !== b.rowIndex) return lineageCrossGenerationPartnerPath(a, b, nodeW, nodeH, unionY, context);
|
||||
return lineageSameGenerationPartnerPath(a, b, nodeW, nodeH, unionY, attachOffsetA, attachOffsetB);
|
||||
}
|
||||
function lineageChildAnchorOnMarriageLine(partner, fallbackX, fallbackY) {
|
||||
const horizontals = (partner?.segments || [])
|
||||
.filter(seg => seg.kind === "h" && Math.abs(seg.x2 - seg.x1) > 10)
|
||||
.sort((a, b) => {
|
||||
const ty = Number.isFinite(partner?.unionY) ? partner.unionY : fallbackY;
|
||||
const dy = Math.abs(a.y1 - ty) - Math.abs(b.y1 - ty);
|
||||
if (Math.abs(dy) > 0.01) return dy;
|
||||
return Math.abs(b.x2 - b.x1) - Math.abs(a.x2 - a.x1);
|
||||
});
|
||||
const seg = horizontals[0] || null;
|
||||
if (!seg) return { x: fallbackX, y: fallbackY };
|
||||
const minX = Math.min(seg.x1, seg.x2) + 8;
|
||||
const maxX = Math.max(seg.x1, seg.x2) - 8;
|
||||
const midX = (seg.x1 + seg.x2) / 2;
|
||||
const x = minX <= maxX ? clamp(Number.isFinite(fallbackX) ? fallbackX : midX, minX, maxX) : midX;
|
||||
return { x, y: seg.y1 };
|
||||
}
|
||||
|
||||
|
||||
function lineageVerticalBridgePath(x, y1, y2, bridges = [], radius = 8.5) {
|
||||
if (!Number.isFinite(x) || !Number.isFinite(y1) || !Number.isFinite(y2)) return '';
|
||||
|
|
@ -170,6 +188,44 @@ function lineageBuildChildPathGroup(group, nodeW, nodeH, bridgeHorizontals) {
|
|||
return { d };
|
||||
}
|
||||
|
||||
function lineageChildBusSpan(group, nodeW) {
|
||||
const childCenters = (group.children || []).map(c => c.x + nodeW / 2);
|
||||
if (!childCenters.length) return null;
|
||||
return {
|
||||
left: Math.min(group.joinX, ...childCenters),
|
||||
right: Math.max(group.joinX, ...childCenters),
|
||||
};
|
||||
}
|
||||
|
||||
function lineageSpansOverlap(a, b, pad = 12) {
|
||||
if (!a || !b) return false;
|
||||
return Math.max(a.left, b.left) <= Math.min(a.right, b.right) + pad;
|
||||
}
|
||||
|
||||
function lineageSeparateChildBusLanes(groupsInGap, nodeW) {
|
||||
const lanes = [];
|
||||
const ordered = (groupsInGap || [])
|
||||
.filter(group => group && Number.isFinite(group.busY))
|
||||
.map(group => ({ group, span: lineageChildBusSpan(group, nodeW), baseY: group.busY }))
|
||||
.filter(entry => entry.span)
|
||||
.sort((a, b) => a.baseY - b.baseY || a.span.left - b.span.left || a.span.right - b.span.right);
|
||||
|
||||
for (const entry of ordered) {
|
||||
let laneIndex = 0;
|
||||
while (lanes[laneIndex]?.some(prev => Math.abs(prev.group.busY - entry.baseY) < 12 && lineageSpansOverlap(prev.span, entry.span))) {
|
||||
laneIndex += 1;
|
||||
}
|
||||
if (!lanes[laneIndex]) lanes[laneIndex] = [];
|
||||
const fromY = Number(entry.group.fromY);
|
||||
const childTop = Number(entry.group.childTop);
|
||||
const minY = Number.isFinite(fromY) ? fromY + 26 : -Infinity;
|
||||
const maxY = Number.isFinite(childTop) ? childTop - 18 : Infinity;
|
||||
const offset = laneIndex * 9;
|
||||
entry.group.busY = clamp(entry.baseY - offset, minY, maxY);
|
||||
lanes[laneIndex].push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
function lineageComponentSignature(component, family) {
|
||||
family = lineageEdgeFamily(family);
|
||||
const idSet = new Set(component.map(x => x?.id).filter(Boolean));
|
||||
|
|
@ -281,7 +337,7 @@ function lineageAssignPartnerAttachOffsets(linkGroups) {
|
|||
for (const [key, groups] of buckets.entries()) {
|
||||
groups.sort((a, b) => a.joinX - b.joinX || a.childTop - b.childTop);
|
||||
const n = groups.length;
|
||||
const step = 9;
|
||||
const step = 14;
|
||||
groups.forEach((group, i) => {
|
||||
const offset = n <= 1 ? 0 : (i - (n - 1) / 2) * step;
|
||||
const [nodeId] = key.split(':');
|
||||
|
|
@ -328,8 +384,8 @@ function lineageBuildLinks(nodes, idSet, layout) {
|
|||
|
||||
for (const groupsInGap of byGap.values()) {
|
||||
groupsInGap.sort((a, b) => a.joinX - b.joinX || a.children.length - b.children.length);
|
||||
const top = Math.min(...groupsInGap.map(g => g.parentBottom)) + 44;
|
||||
const bottom = Math.max(...groupsInGap.map(g => g.childTop)) - 42;
|
||||
const top = Math.min(...groupsInGap.map(g => g.parentBottom)) + 58;
|
||||
const bottom = Math.max(...groupsInGap.map(g => g.childTop)) - 54;
|
||||
const span = Math.max(16, bottom - top);
|
||||
groupsInGap.forEach((group, i) => {
|
||||
const laneY = top + span * ((i + 1) / (groupsInGap.length + 1));
|
||||
|
|
@ -343,22 +399,21 @@ function lineageBuildLinks(nodes, idSet, layout) {
|
|||
const attachB = 0;
|
||||
const partnerY = ((left.y + nodeH / 2) + (right.y + nodeH / 2)) / 2;
|
||||
const partner = lineagePartnerPathFromPositions(left, right, nodeW, nodeH, partnerY, attachA, attachB, layout);
|
||||
if (Number.isFinite(partner.unionX)) group.joinX = partner.unionX;
|
||||
const unionY = Number.isFinite(partner.unionY) ? partner.unionY : partnerY;
|
||||
const clearY = clamp(group.parentBottom + 22 + i * 2, unionY + 10, group.childTop - 36);
|
||||
group.fromY = clearY;
|
||||
group.busY = group.children.length === 1 ? Math.min(group.childTop - 18, clearY + 28) : Math.min(group.childTop - 24, Math.max(clearY + 24, laneY + 18 + i * 3));
|
||||
const anchor = lineageChildAnchorOnMarriageLine(partner, Number.isFinite(partner.unionX) ? partner.unionX : group.joinX, unionY);
|
||||
group.joinX = anchor.x;
|
||||
group.fromY = anchor.y;
|
||||
group.busY = group.children.length === 1 ? Math.min(group.childTop - 22, anchor.y + 38) : Math.min(group.childTop - 28, Math.max(anchor.y + 34, laneY + 24 + i * 5));
|
||||
if (partner.d) {
|
||||
partnerLinks.push(partner.d);
|
||||
horizontalSegments.push(...partner.segments.filter(seg => seg.kind === 'h'));
|
||||
}
|
||||
const trunk = lineageVerticalBridgePath(group.joinX, unionY, group.fromY, []);
|
||||
if (trunk) childLinks.push(trunk);
|
||||
} else {
|
||||
group.fromY = group.parentBottom;
|
||||
group.busY = group.children.length === 1 ? Math.min(group.childTop - 18, group.fromY + 30) : Math.min(group.childTop - 24, Math.max(group.fromY + 28, laneY + 18 + i * 3));
|
||||
group.busY = group.children.length === 1 ? Math.min(group.childTop - 22, group.fromY + 38) : Math.min(group.childTop - 28, Math.max(group.fromY + 34, laneY + 24 + i * 5));
|
||||
}
|
||||
});
|
||||
lineageSeparateChildBusLanes(groupsInGap, nodeW);
|
||||
}
|
||||
|
||||
for (const groupsInGap of byGap.values()) {
|
||||
|
|
|
|||
|
|
@ -91,19 +91,25 @@ function lineageBuildLinksFast(nodes, idSet, layout) {
|
|||
childLinkEdges += parents.length;
|
||||
const childX = childPos.x + nodeW / 2;
|
||||
const childY = childPos.y;
|
||||
const fromY = Math.max(...parents.map(p => p.y + nodeH));
|
||||
const joinX = parents.reduce((sum, p) => sum + p.x + nodeW / 2, 0) / parents.length;
|
||||
let fromY = Math.max(...parents.map(p => p.y + nodeH));
|
||||
let joinX = parents.reduce((sum, p) => sum + p.x + nodeW / 2, 0) / parents.length;
|
||||
if (parents.length >= 2) {
|
||||
const left = parents[0];
|
||||
const right = parents[parents.length - 1];
|
||||
const partnerKey = parents.map(p => p.node?.id).filter(Boolean).sort().join("+");
|
||||
if (partnerKey && !seenPartners.has(partnerKey)) {
|
||||
seenPartners.add(partnerKey);
|
||||
const y = ((left.y + nodeH / 2) + (right.y + nodeH / 2)) / 2;
|
||||
partnerLinks.push(`M ${(left.x + nodeW).toFixed(1)} ${y.toFixed(1)} L ${right.x.toFixed(1)} ${y.toFixed(1)}`);
|
||||
const y = ((left.y + nodeH / 2) + (right.y + nodeH / 2)) / 2;
|
||||
const x1 = left.x + nodeW;
|
||||
const x2 = right.x;
|
||||
if (x2 > x1) {
|
||||
joinX = clamp(joinX, x1 + 8, x2 - 8);
|
||||
fromY = y;
|
||||
if (partnerKey && !seenPartners.has(partnerKey)) {
|
||||
seenPartners.add(partnerKey);
|
||||
partnerLinks.push(`M ${x1.toFixed(1)} ${y.toFixed(1)} L ${x2.toFixed(1)} ${y.toFixed(1)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
const laneY = Math.min(childY - 18, fromY + Math.max(28, (childY - fromY) * 0.48));
|
||||
const laneY = Math.min(childY - 22, fromY + Math.max(36, (childY - fromY) * 0.54));
|
||||
childLinks.push(`M ${joinX.toFixed(1)} ${fromY.toFixed(1)} L ${joinX.toFixed(1)} ${laneY.toFixed(1)} L ${childX.toFixed(1)} ${laneY.toFixed(1)} L ${childX.toFixed(1)} ${childY.toFixed(1)}`);
|
||||
}
|
||||
return { partnerLinks, childLinks, childLinkGroups, childLinkEdges };
|
||||
|
|
|
|||
|
|
@ -302,6 +302,13 @@
|
|||
if (uiCache.grabKind === "item") syncGrabbedPhysicsItem(target, "pinch-release");
|
||||
target.playerHeld = false;
|
||||
target._heldByPlayer = false;
|
||||
if (uiCache.grabKind === "tarinai") {
|
||||
target.pinchThoughtTimer = 0;
|
||||
if (target.thought === "つままれている") target.thought = "";
|
||||
const behavior = typeof currentTarinaiBehavior === "function" ? currentTarinaiBehavior(target) : null;
|
||||
const reason = String(behavior?.reason || behavior?.text || behavior?.label || "");
|
||||
if (reason === "つままれている" && typeof clearBehavior === "function") clearBehavior(target);
|
||||
}
|
||||
if (target.type === "duplicator") {
|
||||
target.duplicatorLoadSuppressedUntil = Math.max(target.duplicatorLoadSuppressedUntil || 0, (world.time || 0) + 1.2);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
"use strict";
|
||||
|
||||
(function () {
|
||||
const APP_VERSION = "39.15.101";
|
||||
const APP_BUILD = "tarinai-fire-effects-v39-15-101";
|
||||
const APP_VERSION = "39.15.107";
|
||||
const APP_BUILD = "tarinai-family-line-lanes-v39-15-107";
|
||||
const APP_CACHE_NAME = `tarinai-colony-${APP_VERSION}`;
|
||||
const STATIC_VERSION_PARAM = `v=${APP_VERSION}`;
|
||||
|
||||
|
|
@ -21,4 +21,3 @@
|
|||
window.TARINAI_VERSION = APP_VERSION;
|
||||
window.TARINAI_APP = config;
|
||||
})();
|
||||
|
||||
|
|
|
|||
|
|
@ -295,6 +295,15 @@
|
|||
it.grassStage = -1;
|
||||
}
|
||||
cleaned += p * 0.72;
|
||||
} else if (it.type === "fire") {
|
||||
it.amount = Math.max(0, (Number(it.amount) || 0) - dt * 540 * (0.35 + p));
|
||||
it.flameLife = Math.max(0, (Number(it.flameLife) || 0) - dt * 5.2 * (0.35 + p));
|
||||
it.fireShrinkPulse = Math.max(Number(it.fireShrinkPulse || 0), 0.18);
|
||||
if (it.amount <= 4 || it.flameLife <= 0.05) {
|
||||
it.amount = 0;
|
||||
it.flameLife = 0;
|
||||
}
|
||||
cleaned += p * 1.15;
|
||||
} else if (it.type === "grass_bed") {
|
||||
it.amount = 0;
|
||||
it.hp = 0;
|
||||
|
|
@ -320,6 +329,20 @@
|
|||
t.vy += ny * (95 + 165 * p) + deterministicRange(this, "water-hose-jitter-y", -10, 10, t, x, y);
|
||||
t.surpriseTimer = Math.max(t.surpriseTimer || 0, 0.24);
|
||||
t.applyWaterEffect?.(dt * (1.0 + p * 1.8), "hose");
|
||||
if (t.burning || (t.burnTimer || 0) > 0) {
|
||||
t.extinguishFire?.("\u6d17\u6d44\u3067\u6d88\u706b\u3055\u308c\u305f");
|
||||
cleaned += p * 1.10;
|
||||
}
|
||||
}
|
||||
for (const ant of this.nearbyAnts?.(x, y, radius + 45) || []) {
|
||||
if (!ant || ant.dead) continue;
|
||||
const d = distXY(ant.x, ant.y, x, y);
|
||||
if (d > radius + (ant.r || 4)) continue;
|
||||
const p = clamp(1 - d / (radius + (ant.r || 4)), 0, 1);
|
||||
if (ant.burning || (ant.burnTimer || 0) > 0) {
|
||||
ant.extinguishFire?.("water-hose");
|
||||
cleaned += p * 0.75;
|
||||
}
|
||||
}
|
||||
if (deterministicChance(this, "water-hose-ring", 0.72, x, y)) {
|
||||
this.effects.push(new Effect("ring", x + deterministicRange(this, "water-hose-ring-x", -18, 18, x, y), y + deterministicRange(this, "water-hose-ring-y", -12, 12, x, y), { size: deterministicRange(this, "water-hose-ring-size", 8, 22, x, y), life: deterministicRange(this, "water-hose-ring-life", 0.18, 0.32, x, y), color: "rgba(128,204,238,0.60)" }));
|
||||
|
|
|
|||
|
|
@ -660,10 +660,10 @@
|
|||
}
|
||||
if (isServingFoodType(item.type)) {
|
||||
item.toolSize = data.toolSize || item.toolSize || "medium";
|
||||
item.foodServingScale = Number(data.foodServingScale || item.foodServingScale || 1) || 1;
|
||||
item.foodServingsMax = Math.max(1, Number(data.foodServingsMax || item.foodServingsMax || item.amount || 1));
|
||||
item.foodServingsRemaining = item.foodServingsMax;
|
||||
item.foodServingsRemaining = clamp(Number(data.foodServingsRemaining ?? item.foodServingsMax) || item.foodServingsMax, 0, item.foodServingsMax);
|
||||
item.amount = item.foodServingsRemaining;
|
||||
(typeof updateServingFoodVisualSize === "function" ? updateServingFoodVisualSize(item) : false);
|
||||
}
|
||||
if (Number.isFinite(Number(item.x))) item.prevX = item.x;
|
||||
if (Number.isFinite(Number(item.y))) item.prevY = item.y;
|
||||
|
|
|
|||
|
|
@ -406,10 +406,10 @@
|
|||
item.r *= scale;
|
||||
if (isServingFoodType(item.type)) {
|
||||
const servings = foodServingsForSize(size);
|
||||
item.foodServingScale = scale;
|
||||
item.foodServingsMax = servings;
|
||||
item.foodServingsRemaining = servings;
|
||||
item.amount = servings;
|
||||
(typeof updateServingFoodVisualSize === "function" ? updateServingFoodVisualSize(item) : false);
|
||||
} else if (["grass", "zunchi"].includes(item.type)) {
|
||||
item.amount *= scale * scale;
|
||||
item.foodServingScale = scale;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
const APP_VERSION = "39.15.101";
|
||||
const APP_VERSION = "39.15.107";
|
||||
const CACHE_NAME = `tarinai-colony-${APP_VERSION}`;
|
||||
const v = `v=${APP_VERSION}`;
|
||||
// Generated from app_manifest.json. Run scripts/generate_app_files.py after changing static files.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue