v1.2
This commit is contained in:
parent
f500279abd
commit
cefa2ace5a
27 changed files with 1275 additions and 314 deletions
58
AI_MAP.md
Normal file
58
AI_MAP.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# tarinai_ AI map
|
||||
|
||||
Purpose: static Japanese browser simulation/game. Optimize context by reading this file only after `README.md`, then open the relevant subsystem files. For exact file expansion, use `scripts/ai_inventory.ps1` or `scripts/ai_inventory.py`.
|
||||
|
||||
## Architecture
|
||||
|
||||
- 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.47`.
|
||||
- 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/world_update_phases.js` / `js/simulation_systems.js`.
|
||||
- Command path: UI/input emits command objects; `js/command_dispatcher.js` routes tool, pointer, selection, save, reset, and UI intents.
|
||||
- Persistence: save files are schema/codec/storage/coordinator modules; snapshot/history modules keep world restoration and stats consistent.
|
||||
|
||||
## Subsystem Map
|
||||
|
||||
- Core/bootstrap: `js/version.js`, `js/event_bus.js`, registries, `js/data.js`, `js/ground_types.js`, `js/health.js`, `js/main.js`, `js/debug_tools.js`, `js/perf_profiler.js`.
|
||||
- Rendering/assets/audio: `js/assets.js`, `js/audio.js`, `js/render.js`, `js/tarinai_render.js`, `js/item_render_runtime.js`, `js/sound_pack.js`.
|
||||
- Simulation phases: `js/sim_core.js`, `js/simulation*.js`, `js/world_update*.js`, `js/system_order.js`.
|
||||
- World domain: `js/world*.js`, `js/weather_system.js`; covers state, camera/view, environment, combat/effects, placement/history, tools, family/social, ants, spatial budgets.
|
||||
- Creature domain: `js/tarinai*.js`; covers state, identity, needs, actions, forced behavior, social life, item effects, disease/nests, movement/update pipeline, rendering.
|
||||
- Item/structure domain: `js/item*.js`, `js/items.js`, `js/structures.js`, `js/structure_lifecycle.js`; covers definitions, spawning defaults, lifecycle, dynamic behavior, rendering.
|
||||
- Physics/mechanics: `js/physics*.js`, `js/mechanical_system.js`, `js/constraint_system.js`, `js/collision_footprint_system.js`.
|
||||
- UI/input: `js/ui*.js`, `js/text_catalog.js`, `js/command_dispatcher.js`; covers panels, tools, selected creature, logs, charts, family tree, mouse/touch, dialogs, labels.
|
||||
- Family tree: `js/family_graph.js`, `js/ui_family_*.js`; data transform, layout, paths, async rendering, validation.
|
||||
- Save/history: `js/save*.js`, `js/snapshot_system.js`, `js/restore_coordinator.js`, `js/history_system.js`.
|
||||
- Scripts: `scripts/generate_app_files.py`, `scripts/generate_item_icons.py`, `scripts/regression_check.py`, `scripts/ai_inventory.py`, `scripts/ai_inventory.ps1`.
|
||||
|
||||
## File Pattern Semantics
|
||||
|
||||
- `*_pipeline.js`: ordered runner/facade for update or lifecycle work.
|
||||
- `*_step_*.js`: narrow pipeline step wrapper.
|
||||
- `*_system.js`: domain service, phase service, or compatibility facade.
|
||||
- `*_runtime.js`: stable runtime-facing facade around split modules.
|
||||
- `*_registry.js`: definition registry and lookup/normalization helpers.
|
||||
- `world_*`: world-owned behavior; `tarinai_*`: creature-owned behavior; `item_*`: item-owned behavior; `ui_*`: DOM/input/panel behavior.
|
||||
- `assets/sprites/tarinai_*.webp`: creature visual state sprites; numeric prefix is stable asset ID/order.
|
||||
- `assets/ui/tool_*`: tool palette icons; `assets/ui/favicon*`, `apple-touch-icon`, `ecology_*` are app/help icons.
|
||||
- `assets/objects/*`: field object sprites for ants, zunchi, pushpin, oshibyo, genkotsu, plushie.
|
||||
- `assets/sounds/voice_*`: creature voice samples; `shoot_*`, `major_damage_*`, `firecracker_*` are SFX.
|
||||
|
||||
## Task-Oriented Read Sets
|
||||
|
||||
- UI/layout bug: `index.html`, relevant `css/*.css`, `js/ui_bind.js`, `js/ui_layout_dialogs.js`, matching `js/ui_*.js`.
|
||||
- Tool behavior: `js/command_dispatcher.js`, `js/world_tool_actions.js`, `js/item_registry.js`, relevant `js/item_*` or `js/tarinai_item_*`.
|
||||
- Creature behavior: `js/tarinai.js`, `js/tarinai_update_pipeline.js`, relevant `js/tarinai_*`, `js/system_order.js`.
|
||||
- World/environment/combat: matching `js/world_*`, `js/simulation_*`, `js/system_order.js`.
|
||||
- Save/load: `js/save_schema.js`, `js/save_codec.js`, `js/save_system.js`, `js/snapshot_system.js`, `js/restore_coordinator.js`.
|
||||
- Family tree: `js/world_family_social.js`, `js/family_graph.js`, `js/ui_family_*.js`.
|
||||
- Static app/cache/version: `app_manifest.json`, `scripts/generate_app_files.py`, `service-worker.js`, `js/version.js`, `index.html`.
|
||||
|
||||
## Invariants
|
||||
|
||||
- Preserve `index.html` script order unless all dependent manifests/generated files are updated together.
|
||||
- Keep `app_manifest.json`, `js/version.js`, `service-worker.js`, and cache query versions synchronized.
|
||||
- Keep global exports for compatibility with regression checks and browser runtime.
|
||||
- Prefer focused edits in the owning subsystem; split modules are intentional compatibility boundaries.
|
||||
- Run `node --check js/*.js` and `python scripts/regression_check.py` for behavior changes.
|
||||
91
FILES.json
Normal file
91
FILES.json
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
{
|
||||
"purpose": "Token-efficient machine-readable routing for tarinai_. Use scripts/ai_inventory.py for exact current file expansion.",
|
||||
"version": "39.15.47",
|
||||
"entrypoints": {
|
||||
"app": "index.html",
|
||||
"runtime": "js/main.js",
|
||||
"update_order": "js/system_order.js",
|
||||
"manifest": "app_manifest.json",
|
||||
"service_worker": "service-worker.js"
|
||||
},
|
||||
"read_strategy": {
|
||||
"default": ["README.md"],
|
||||
"architecture": ["README.md", "AI_MAP.md"],
|
||||
"exact_inventory": ["powershell -NoProfile -ExecutionPolicy Bypass -File scripts/ai_inventory.ps1 -All", "python scripts/ai_inventory.py --all"],
|
||||
"machine_routing": ["FILES.json"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"id": "root",
|
||||
"patterns": [".gitignore", ".htaccess", "app_manifest.json", "favicon.ico", "index.html", "README.md", "AI_MAP.md", "FILES.json", "service-worker.js"],
|
||||
"meaning": "Repo/app shell metadata, server/static cache config, docs, manifest, entrypoint."
|
||||
},
|
||||
{
|
||||
"id": "css",
|
||||
"patterns": ["css/*.css"],
|
||||
"meaning": "Base, layout, panel, component, and mobile responsive styling."
|
||||
},
|
||||
{
|
||||
"id": "scripts",
|
||||
"patterns": ["scripts/*.py", "scripts/*.ps1"],
|
||||
"meaning": "Generators, regression checks, and token-aware AI inventory tooling."
|
||||
},
|
||||
{
|
||||
"id": "core",
|
||||
"patterns": ["js/version.js", "js/event_bus.js", "js/registry_base.js", "js/disease_registry.js", "js/sound_pack.js", "js/data.js", "js/ground_types.js", "js/health.js", "js/input_mode_manager.js", "js/main.js", "js/math.js", "js/patch_helpers.js", "js/perf_profiler.js", "js/debug_tools.js"],
|
||||
"meaning": "Bootstrap, shared constants/helpers, registries, config, profiling/debug."
|
||||
},
|
||||
{
|
||||
"id": "assets_runtime",
|
||||
"patterns": ["js/assets.js", "js/audio.js", "js/render.js", "js/tarinai_render.js", "js/item_render_runtime.js"],
|
||||
"meaning": "Image/audio loading and canvas rendering."
|
||||
},
|
||||
{
|
||||
"id": "simulation",
|
||||
"patterns": ["js/sim_core.js", "js/simulation*.js", "js/system_order.js"],
|
||||
"meaning": "Simulation primitives, concrete systems, phase ordering."
|
||||
},
|
||||
{
|
||||
"id": "world",
|
||||
"patterns": ["js/world*.js", "js/weather_system.js"],
|
||||
"meaning": "World state/view/update/tools/environment/combat/family/social/ants."
|
||||
},
|
||||
{
|
||||
"id": "physics",
|
||||
"patterns": ["js/physics*.js", "js/mechanical_system.js", "js/constraint_system.js", "js/collision_footprint_system.js"],
|
||||
"meaning": "Geometry, collisions, constraints, mechanical item behavior."
|
||||
},
|
||||
{
|
||||
"id": "items",
|
||||
"patterns": ["js/item*.js", "js/items.js", "js/structures.js", "js/structure_lifecycle.js"],
|
||||
"meaning": "Item/structure registry, lifecycle, dynamic behavior, rendering."
|
||||
},
|
||||
{
|
||||
"id": "tarinai",
|
||||
"patterns": ["js/tarinai*.js", "js/ants.js"],
|
||||
"meaning": "Creature state/actions/needs/social life/item effects/disease/rendering plus ant actor model."
|
||||
},
|
||||
{
|
||||
"id": "save",
|
||||
"patterns": ["js/save*.js", "js/snapshot_system.js", "js/restore_coordinator.js", "js/history_system.js"],
|
||||
"meaning": "Snapshot schema/codec/storage/restore/history."
|
||||
},
|
||||
{
|
||||
"id": "ui",
|
||||
"patterns": ["js/ui*.js", "js/text_catalog.js", "js/command_dispatcher.js", "js/family_graph.js"],
|
||||
"meaning": "DOM UI, input, tools, selected panel, logs, charts, family tree, text labels, command routing."
|
||||
},
|
||||
{
|
||||
"id": "assets",
|
||||
"patterns": ["assets/sprites/tarinai_*.webp", "assets/ui/tool_*", "assets/ui/favicon*", "assets/ui/apple-touch-icon.png", "assets/ui/ecology_*.webp", "assets/objects/*", "assets/sounds/*"],
|
||||
"meaning": "Creature sprites, UI/tool icons, field object images, voice/SFX samples."
|
||||
}
|
||||
],
|
||||
"conventions": {
|
||||
"*_pipeline.js": "ordered runner/facade",
|
||||
"*_step_*.js": "pipeline stage wrapper",
|
||||
"*_system.js": "domain service or compatibility facade",
|
||||
"*_runtime.js": "stable runtime-facing facade",
|
||||
"*_registry.js": "definition lookup/normalization"
|
||||
}
|
||||
}
|
||||
92
README.md
92
README.md
|
|
@ -1,18 +1,82 @@
|
|||
# tarinai v39.15.39
|
||||
# tarinai_
|
||||
|
||||
## 変更点
|
||||
AI-first entrypoint for a static browser simulation/game. Keep this file small; read deeper docs only when needed.
|
||||
|
||||
- PC表示を再度ロールバックし、PC側の変更はクイックスクロールの「本体」「道具」ボタン非表示に限定。道具UI本体は維持。
|
||||
- PC上部ボタンを右詰め・やや大きめに調整し、ゲームタイトルがボタンに押されて省略されないように修正。
|
||||
- スマホ用のスクロールスナップ系指定を無効化し、通常スクロールを妨げないように修正。
|
||||
- スマホで「家系図」へ移動したとき、更新OFFでも手動ジャンプ扱いで現在の家系図を描画してからスクロールするように修正。
|
||||
- スマホの自動スクロールボタンは、初期折り畳み部分を開いてから目的地へ移動。ユーザーが手動で閉じた部分は開かない挙動を維持。
|
||||
- たりない/道具系ツールチップの最大幅・折り返し・画面端補正を維持。
|
||||
- 「333の部屋に移動」のリンク先は `https://2012r2.nishi.boats/~333/`。
|
||||
- 「観察」は「たりないデータ」の表記を維持。
|
||||
## Read Strategy
|
||||
|
||||
## 確認
|
||||
- Start here for architecture, script order constraints, and where to look next.
|
||||
- For subsystem work, read `AI_MAP.md` plus only the matching files.
|
||||
- For exact file inventory, run `powershell -NoProfile -ExecutionPolicy Bypass -File scripts/ai_inventory.ps1 -All` or narrow it: `powershell -NoProfile -ExecutionPolicy Bypass -File scripts/ai_inventory.ps1 ui`. Python equivalent: `python scripts/ai_inventory.py --all`.
|
||||
- For machine-readable routing, read `FILES.json`.
|
||||
- Avoid loading all assets into context; asset filenames are mostly self-describing and grouped by glob.
|
||||
|
||||
- `node --check js/*.js` passed
|
||||
- `python3 scripts/regression_check.py` passed
|
||||
- version synchronized: `39.15.39`
|
||||
## Runtime Shape
|
||||
|
||||
- No bundler; `index.html` loads ordered global-IIFE scripts with `?v=39.15.47`.
|
||||
- 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`.
|
||||
- Generated/static app shell: `app_manifest.json`, `service-worker.js`, and `js/version.js` must stay version-aligned.
|
||||
|
||||
## File Routing
|
||||
|
||||
- `index.html`: DOM shell, CSS/JS load order, panels/dialogs/canvas.
|
||||
- `css/*.css`: base/layout/panel/components/mobile styling.
|
||||
- `js/world*.js`: world state, view, update phases, tools, placement, combat, environment, family/social, ants.
|
||||
- `js/tarinai*.js`: creature state, needs, actions, behavior, social life, item effects, update pipeline, rendering.
|
||||
- `js/item*.js`, `js/structures*.js`: item registry/runtime/lifecycle/dynamic behavior/rendering plus structures.
|
||||
- `js/simulation*.js`, `js/system_order.js`, `js/sim_core.js`: concrete update systems and phase ordering.
|
||||
- `js/ui*.js`, `js/text_catalog.js`, `js/command_dispatcher.js`: DOM UI, input, selected panel, logs, charts, family tree, tools, labels.
|
||||
- `js/save*.js`, `js/snapshot_system.js`, `js/restore_coordinator.js`, `js/history_system.js`: persistence and history.
|
||||
- `js/physics*.js`, `js/mechanical_system.js`, `js/constraint_system.js`, `js/collision_footprint_system.js`: geometry, mechanics, constraints, collisions.
|
||||
- `assets/sprites/tarinai_*.webp`: creature state sprites.
|
||||
- `assets/ui/tool_*`: tool palette icons; other `assets/ui/*` are app/help icons.
|
||||
- `assets/objects/*`: field object images.
|
||||
- `assets/sounds/*`: voice and SFX samples.
|
||||
- `scripts/*`: generators, regression checks, and AI inventory.
|
||||
|
||||
## Conventions
|
||||
|
||||
- `*_pipeline` = ordered runner/facade.
|
||||
- `*_step_*` = pipeline stage wrapper.
|
||||
- `*_system` = domain service or compatibility facade.
|
||||
- `*_runtime` = stable runtime-facing facade.
|
||||
- `*_registry` = definition lookup/normalization.
|
||||
- Many split files preserve regression contracts; do not collapse them casually.
|
||||
|
||||
## Checks
|
||||
|
||||
- Syntax: `node --check js/*.js`
|
||||
- Regression: `python scripts/regression_check.py`
|
||||
- Inventory sanity: `powershell -NoProfile -ExecutionPolicy Bypass -File scripts/ai_inventory.ps1 -All`
|
||||
|
||||
|
||||
## v39.15.47 UI media isolation / scroll repair
|
||||
|
||||
- PC / smartphone UI override rules are separated into `@media (min-width: 981px)` and `@media (max-width: 980px)`.
|
||||
- PC family tree card is restored below the main game layout, not inside the right menu.
|
||||
- Smartphone manual scroll no longer uses internal vertical scroll traps for event log / family tree content.
|
||||
- Auto-scroll does not open collapsed cards.
|
||||
|
||||
|
||||
## v39.15.47 event/family/oshibyo UI fixes
|
||||
|
||||
- Event UI hides direct player item placement records while keeping those records in the internal log.
|
||||
- Collapsed Event UI no longer leaves a large blank area from the log-card min-height.
|
||||
- Family tree nodes show inherited stats in a 2x2 grid and move the tarinai portrait to the upper-right.
|
||||
- Oshibyo lodged position is anchored to the left side of the tarinai sprite.
|
||||
|
||||
|
||||
## v39.15.47 family / sprinkler polish
|
||||
|
||||
- Family tree generation row spacing is tightened.
|
||||
- Family tree personality tags also use a 2x2 grid (up to 4 visible tags).
|
||||
- Sprinkler effective cleaning radius is increased to 3x the previous value and still filters targets by actual distance.
|
||||
- Sprinkler rendering is redesigned to a more realistic yard-sprinkler look and now emits water-like spray effects when active.
|
||||
|
||||
|
||||
## v39.15.47 owned object panic / shoot effect
|
||||
|
||||
- Tarinai panic when their own plushie or simple grass bed is destroyed.
|
||||
- The selected Tarinai data behavior text explicitly shows the owned-object panic reason.
|
||||
- Shooting impact effects are larger and remain visible for slightly longer.
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
# センサー系アイデア
|
||||
|
||||
実験観察ゲームとして、直接介入ではなく「測る」ための道具案。
|
||||
|
||||
## 低コストで入れやすいもの
|
||||
|
||||
| センサー | 役割 | 記録する値 | 観察上の使い道 |
|
||||
|---|---|---|---|
|
||||
| ストレスセンサー | 範囲内の緊張度を測る | 平均ストレス / 最大ストレス / 急上昇回数 | 地形・混雑・病気・王者個体の影響を見る |
|
||||
| 通過カウンター | 置いた地点を通った回数を数える | 通過数 / 個体別通過数 | 道・縄張り・回避行動の発見 |
|
||||
| 滞在センサー | 範囲内にいた時間を測る | 滞在秒数 / 個体ID / 世代 | 好まれる場所・避けられる場所の比較 |
|
||||
| けんかセンサー | 範囲内の争いを記録 | 勝者 / 敗者 / 勝率変化 | 王者候補・危険地帯の検出 |
|
||||
| 汚染センサー | ずんち・病気・汚れを見る | 汚染量 / 増加量 / 感染者数 | スプリンクラーや隔離の効果検証 |
|
||||
| 出生センサー | 近くで生まれた個体を記録 | 親 / 子 / 遺伝値 | 繁殖条件・血統観察 |
|
||||
|
||||
## 中コストのセンサー
|
||||
|
||||
| センサー | 仕様 |
|
||||
|---|---|
|
||||
| 個体追跡ビーコン | 特定個体に付ける。移動経路、接触相手、食事、喧嘩、睡眠を時系列で記録する。 |
|
||||
| 関係センサー | 範囲内で友好・敵対が増減した時だけログを残す。群れの分裂や王者の圧力を見やすくする。 |
|
||||
| 遺伝スキャナー | 選択個体の寿命・サイズ・攻撃・速度の遺伝倍率を読み取る。家系図の補助UIにも使える。 |
|
||||
| 行動理由センサー | 近くの個体が「なぜその行動を選んだか」を短く記録する。空腹、恐怖、王者回避、繁殖欲求など。 |
|
||||
| 死因センサー | 範囲内の死亡時に、直前の状態・接触相手・地面・病気・道具をまとめる。事故検証用。 |
|
||||
|
||||
## 高コストだが強いもの
|
||||
|
||||
| センサー | 仕様 |
|
||||
|---|---|
|
||||
| ヒートマップ装置 | ストレス、滞在、喧嘩、死亡、汚染、出生を色で重ねて表示する。 |
|
||||
| A/B実験レコーダー | 2つの区画に別条件を設定し、生存数・出生数・平均寿命・王者発生率を比較する。 |
|
||||
| 血統観察端末 | 家系ごとの平均寿命、平均サイズ、勝率、移動速度、病気耐性をランキング表示する。 |
|
||||
| 社会構造センサー | 群れの中心個体、孤立個体、王者、敵対ネットワークをグラフ化する。 |
|
||||
|
||||
## 特に相性が良い実装順
|
||||
|
||||
1. 通過カウンター
|
||||
2. ストレスセンサー
|
||||
3. けんかセンサー
|
||||
4. 遺伝スキャナー
|
||||
5. ヒートマップ装置
|
||||
|
||||
最初は「画面内に結果を出す道具」ではなく、ログとグラフに測定値を流すだけでも十分に実験感が出る。
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "39.15.39",
|
||||
"version": "39.15.47",
|
||||
"css": [
|
||||
"css/base.css",
|
||||
"css/layout.css",
|
||||
|
|
|
|||
|
|
@ -801,3 +801,9 @@
|
|||
max-width: min(280px, calc(100vw - 24px));
|
||||
}
|
||||
}
|
||||
|
||||
/* v39.15.47: collapsed event card must shrink to its header.
|
||||
layout.css gives .log-card a min-height, which left a large blank panel when folded. */
|
||||
.collapsible-card.collapsed { min-height: 0 !important; }
|
||||
.log-card.collapsed { min-height: 0 !important; height: auto !important; flex: 0 0 auto !important; flex-basis: auto !important; }
|
||||
.log-card.collapsed .panel-card-body { display: none !important; }
|
||||
|
|
|
|||
278
css/mobile.css
278
css/mobile.css
|
|
@ -59,7 +59,7 @@
|
|||
.sound-panel input:disabled + span { opacity: .55; }
|
||||
.sound-master { font-weight: 700; }
|
||||
.sound-panel-divider { height: 1px; background: rgba(84,68,48,.12); margin: 2px 0; }
|
||||
@media (max-width: 760px) { .sound-panel { right: auto; left: 0; } }
|
||||
@media (max-width: 980px) { .sound-panel { right: auto; left: 0; } }
|
||||
|
||||
|
||||
/* Collapsed card sizing is shared by the base panel rules; no event-only shrink override. */
|
||||
|
|
@ -363,7 +363,7 @@ body.touch-mobile-ui .tool-tip-popover {
|
|||
}
|
||||
|
||||
|
||||
/* v39.15.39: desktop rollback guard. Keep the normal PC layout; only remove duplicated quick-scroll buttons. */
|
||||
/* v39.15.47: desktop rollback guard. Keep the normal PC layout; only remove duplicated quick-scroll buttons. */
|
||||
@media (min-width: 981px) {
|
||||
.topbar {
|
||||
display: flex;
|
||||
|
|
@ -410,19 +410,281 @@ body.touch-mobile-ui .tool-tip-popover {
|
|||
}
|
||||
}
|
||||
|
||||
/* v39.15.39: mobile scroll must remain manual; disable any CSS scroll snapping. */
|
||||
/* v39.15.47: PC / smartphone UI isolation. All override UI placement is scoped by width. */
|
||||
@media (min-width: 981px) {
|
||||
html,
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
#app {
|
||||
width: min(1480px, 100vw);
|
||||
padding: 18px;
|
||||
overflow: visible;
|
||||
}
|
||||
.topbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 14px 18px;
|
||||
}
|
||||
.top-left {
|
||||
flex: 1 0 auto;
|
||||
min-width: max-content;
|
||||
max-width: 100%;
|
||||
}
|
||||
.brand,
|
||||
.brand h1,
|
||||
h1 {
|
||||
flex: 0 0 auto;
|
||||
min-width: max-content;
|
||||
max-width: none !important;
|
||||
overflow: visible !important;
|
||||
text-overflow: clip !important;
|
||||
}
|
||||
.top-actions {
|
||||
flex: 1 1 560px;
|
||||
margin-left: auto;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.top-actions .btn,
|
||||
.top-actions .sound-menu-btn,
|
||||
.room-link {
|
||||
min-height: 42px;
|
||||
padding: 10px 16px;
|
||||
font-size: 13px;
|
||||
line-height: 1.15;
|
||||
}
|
||||
.layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(680px, 1fr) minmax(320px, 390px);
|
||||
align-items: start;
|
||||
}
|
||||
.panel {
|
||||
position: sticky;
|
||||
top: 12px;
|
||||
align-self: start;
|
||||
max-height: calc(100vh - 24px);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.panel-quick-tabs {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
.panel-quick-tabs [data-panel-tab="game"],
|
||||
.panel-quick-tabs [data-panel-tab="tools"] {
|
||||
display: none !important;
|
||||
}
|
||||
.lineage-main-card {
|
||||
display: block;
|
||||
visibility: visible;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: auto;
|
||||
margin: 16px 0 0;
|
||||
overflow: hidden;
|
||||
contain: layout paint;
|
||||
}
|
||||
.lineage-main-content {
|
||||
height: min(78vh, 980px);
|
||||
min-height: 520px;
|
||||
max-height: calc(100vh - 120px);
|
||||
overflow: auto;
|
||||
overscroll-behavior: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
html, body, #app, .layout, .stage-wrap, .panel, .card, .lineage-main-card {
|
||||
scroll-snap-type: none !important;
|
||||
scroll-snap-align: none !important;
|
||||
scroll-snap-stop: normal !important;
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
min-height: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior-y: auto;
|
||||
}
|
||||
#app {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 10px max(10px, env(safe-area-inset-right, 0px)) calc(env(safe-area-inset-bottom, 0px) + 104px) max(10px, env(safe-area-inset-left, 0px));
|
||||
overflow-x: clip;
|
||||
overflow-y: visible;
|
||||
}
|
||||
.topbar,
|
||||
.top-left,
|
||||
.brand,
|
||||
.layout,
|
||||
.stage-wrap,
|
||||
.panel,
|
||||
.card,
|
||||
.lineage-main-card {
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.topbar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 8px;
|
||||
}
|
||||
.top-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.brand h1,
|
||||
h1 {
|
||||
max-width: 100%;
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
white-space: nowrap;
|
||||
font-size: clamp(20px, 7vw, 30px);
|
||||
}
|
||||
.top-actions {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-start;
|
||||
gap: 5px;
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
padding: 1px 0 5px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
.top-actions > .btn,
|
||||
.top-actions > .sound-menu,
|
||||
.top-actions .sound-menu-btn {
|
||||
flex: 0 0 auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.top-actions .btn,
|
||||
.top-actions .sound-menu-btn {
|
||||
min-height: 34px;
|
||||
padding: 7px 9px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
overflow: visible;
|
||||
}
|
||||
.stage-wrap {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.panel {
|
||||
position: relative !important;
|
||||
top: auto !important;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
max-height: none !important;
|
||||
height: auto !important;
|
||||
overflow: visible !important;
|
||||
isolation: auto;
|
||||
}
|
||||
body.touch-mobile-ui .panel-quick-tabs,
|
||||
.panel-quick-tabs {
|
||||
position: fixed;
|
||||
left: max(8px, env(safe-area-inset-left, 0px));
|
||||
right: max(8px, env(safe-area-inset-right, 0px));
|
||||
bottom: calc(env(safe-area-inset-bottom, 0px) + 8px);
|
||||
top: auto;
|
||||
z-index: 12002;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
gap: 4px;
|
||||
width: auto;
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
padding: 6px;
|
||||
border: 1px solid rgba(102, 169, 215, 0.34);
|
||||
border-radius: 16px;
|
||||
background: rgba(244, 250, 247, 0.95);
|
||||
box-shadow: 0 12px 28px rgba(42, 54, 64, 0.18);
|
||||
}
|
||||
.panel-quick-tabs button {
|
||||
min-height: 34px;
|
||||
padding: 5px 2px;
|
||||
font-size: 10px;
|
||||
}
|
||||
body.touch-mobile-ui .mobile-mode-controls:not([hidden]) {
|
||||
display: flex !important;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
right: auto;
|
||||
bottom: 10px;
|
||||
transform: translateX(-50%);
|
||||
z-index: 24;
|
||||
max-width: calc(100% - 18px);
|
||||
}
|
||||
body.touch-mobile-ui.mobile-mode-auto #gameCanvas {
|
||||
touch-action: pan-y pinch-zoom !important;
|
||||
}
|
||||
body.touch-mobile-ui.mobile-mode-camera #gameCanvas,
|
||||
body.touch-mobile-ui.mobile-mode-tool #gameCanvas {
|
||||
touch-action: none !important;
|
||||
}
|
||||
.log {
|
||||
max-height: none !important;
|
||||
height: auto !important;
|
||||
overflow: visible !important;
|
||||
overscroll-behavior-y: auto !important;
|
||||
}
|
||||
.lineage-main-card {
|
||||
display: block !important;
|
||||
visibility: visible !important;
|
||||
margin-bottom: calc(env(safe-area-inset-bottom, 0px) + 132px);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
margin: 10px 0 calc(env(safe-area-inset-bottom, 0px) + 132px);
|
||||
overflow: visible !important;
|
||||
contain: none !important;
|
||||
clear: both;
|
||||
}
|
||||
.lineage-main-content {
|
||||
display: block !important;
|
||||
height: auto !important;
|
||||
min-height: 360px;
|
||||
max-height: none !important;
|
||||
overflow-x: hidden !important;
|
||||
overflow-y: visible !important;
|
||||
overscroll-behavior-y: auto !important;
|
||||
-webkit-overflow-scrolling: auto;
|
||||
touch-action: pan-y pinch-zoom;
|
||||
}
|
||||
.lineage-family-tree {
|
||||
max-width: 100%;
|
||||
overflow-x: auto !important;
|
||||
overflow-y: visible !important;
|
||||
overscroll-behavior-x: contain;
|
||||
overscroll-behavior-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
touch-action: pan-x pan-y pinch-zoom;
|
||||
}
|
||||
.lineage-family-tree .lineage-tree-panel {
|
||||
max-width: none;
|
||||
}
|
||||
.selected-card,
|
||||
.tool-card,
|
||||
.colony-card,
|
||||
.log-card,
|
||||
.lineage-main-card,
|
||||
.stage-wrap {
|
||||
scroll-margin-top: 0 !important;
|
||||
scroll-margin-bottom: calc(env(safe-area-inset-bottom, 0px) + 104px);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,19 +121,39 @@ body::before {
|
|||
}
|
||||
|
||||
.family-genetic-stats {
|
||||
display: block;
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 1px 5px;
|
||||
margin-top: 2px;
|
||||
font-size: 9px;
|
||||
line-height: 1.16;
|
||||
line-height: 1.12;
|
||||
color: rgba(88, 70, 48, 0.86);
|
||||
letter-spacing: -0.02em;
|
||||
white-space: normal;
|
||||
max-width: 104px;
|
||||
max-width: 82px;
|
||||
}
|
||||
.family-genetic-stats b {
|
||||
.family-genetic-stats span {
|
||||
display: inline-flex;
|
||||
gap: 1px;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.family-genetic-stats b,
|
||||
.family-fight-stat b {
|
||||
font-weight: 800;
|
||||
color: rgba(120, 86, 30, 0.92);
|
||||
}
|
||||
.family-fight-stat {
|
||||
display: block;
|
||||
margin-top: 1px;
|
||||
max-width: 82px;
|
||||
font-size: 9px;
|
||||
line-height: 1.12;
|
||||
color: rgba(88, 70, 48, 0.82);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.lineage-family-tree .tree-node.champion {
|
||||
border-color: rgba(202, 146, 33, 0.58);
|
||||
box-shadow: inset 0 0 0 1px rgba(247, 200, 64, 0.30), 0 3px 8px rgba(120, 78, 12, 0.09);
|
||||
|
|
@ -173,18 +193,19 @@ body::before {
|
|||
color: rgba(126, 80, 64, 0.92);
|
||||
}
|
||||
.family-personality-tags {
|
||||
display: flex !important;
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
overflow: visible !important;
|
||||
text-overflow: clip !important;
|
||||
flex-wrap: wrap;
|
||||
gap: 2px;
|
||||
gap: 2px 4px;
|
||||
margin-top: 2px;
|
||||
white-space: normal !important;
|
||||
overflow: visible;
|
||||
max-height: none;
|
||||
max-width: 84px;
|
||||
}
|
||||
.family-personality-tags em {
|
||||
display: inline-block;
|
||||
display: block;
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
padding: 1px 4px;
|
||||
border-radius: 999px;
|
||||
|
|
@ -194,8 +215,8 @@ body::before {
|
|||
font-size: 7.5px;
|
||||
font-style: normal;
|
||||
line-height: 1.25;
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.family-copy strong { font-size: 11px; }
|
||||
|
|
@ -407,10 +428,11 @@ body::before {
|
|||
transform: none;
|
||||
}
|
||||
.lineage-family-tree .tree-node .family-portrait {
|
||||
right: 0;
|
||||
bottom: -6px;
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
bottom: auto;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
.lineage-family-tree .tree-generation-label {
|
||||
position: absolute;
|
||||
|
|
@ -450,3 +472,11 @@ body::before {
|
|||
|
||||
|
||||
.tool[data-tip] { position: relative; }
|
||||
|
||||
.lineage-family-tree .tree-node .family-copy {
|
||||
max-width: calc(100% - 46px);
|
||||
}
|
||||
.lineage-family-tree .tree-node .family-copy strong,
|
||||
.lineage-family-tree .tree-node .family-copy > span:not(.family-genetic-stats):not(.family-fight-stat):not(.family-personality-tags) {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
|
|
|||
289
index.html
289
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.39" />
|
||||
<link rel="stylesheet" href="css/layout.css?v=39.15.39" />
|
||||
<link rel="stylesheet" href="css/panel.css?v=39.15.39" />
|
||||
<link rel="stylesheet" href="css/components.css?v=39.15.39" />
|
||||
<link rel="stylesheet" href="css/mobile.css?v=39.15.39" />
|
||||
<link rel="stylesheet" href="css/base.css?v=39.15.47" />
|
||||
<link rel="stylesheet" href="css/layout.css?v=39.15.47" />
|
||||
<link rel="stylesheet" href="css/panel.css?v=39.15.47" />
|
||||
<link rel="stylesheet" href="css/components.css?v=39.15.47" />
|
||||
<link rel="stylesheet" href="css/mobile.css?v=39.15.47" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="loadingScreen" class="loading-screen" aria-live="polite">
|
||||
|
|
@ -123,6 +123,10 @@
|
|||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</aside>
|
||||
</main>
|
||||
|
||||
|
||||
|
||||
<section class="card lineage-main-card">
|
||||
<div class="lineage-head">
|
||||
|
|
@ -134,8 +138,7 @@
|
|||
</div>
|
||||
<div id="archiveContent" class="archive-content lineage-main-content"></div>
|
||||
</section>
|
||||
</aside>
|
||||
</main>
|
||||
|
||||
|
||||
<button id="backToGameBtn" class="back-to-game-btn" type="button" hidden aria-label="ゲーム本体へ戻る">▲ 本体</button>
|
||||
|
||||
|
|
@ -193,141 +196,141 @@
|
|||
</div>
|
||||
|
||||
<div id="logPushOverlay" class="log-push-overlay" aria-live="polite" aria-atomic="false"></div>
|
||||
<script src="js/version.js?v=39.15.39" defer></script>
|
||||
<script src="js/event_bus.js?v=39.15.39" defer></script>
|
||||
<script src="js/registry_base.js?v=39.15.39" defer></script>
|
||||
<script src="js/disease_registry.js?v=39.15.39" defer></script>
|
||||
<script src="js/sound_pack.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_registry.js?v=39.15.39" defer></script>
|
||||
<script src="js/data.js?v=39.15.39" defer></script>
|
||||
<script src="js/ground_types.js?v=39.15.39" defer></script>
|
||||
<script src="js/input_mode_manager.js?v=39.15.39" defer></script>
|
||||
<script src="js/math.js?v=39.15.39" defer></script>
|
||||
<script src="js/physics_helpers.js?v=39.15.39" defer></script>
|
||||
<script src="js/collision_footprint_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/mechanical_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/constraint_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/physics_world_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/assets.js?v=39.15.39" defer></script>
|
||||
<script src="js/audio.js?v=39.15.39" defer></script>
|
||||
<script src="js/perf_profiler.js?v=39.15.39" defer></script>
|
||||
<script src="js/render.js?v=39.15.39" defer></script>
|
||||
<script src="js/sim_core.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_seed_factory.js?v=39.15.39" defer></script>
|
||||
<script src="js/structures.js?v=39.15.39" defer></script>
|
||||
<script src="js/items.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_type_initializers.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_lifecycle_support.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_lifecycle_runtime.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_update_policy.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_dynamic_tool_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_dynamic_ball_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_dynamic_duplicator_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_dynamic_pin_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_dynamic_zunchi_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_dynamic_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_lifecycle_decay_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_lifecycle_growth_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_lifecycle_step_frame.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_lifecycle_step_decay.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_lifecycle_step_dynamic.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_lifecycle_step_growth.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_lifecycle_pipeline.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_runtime.js?v=39.15.39" defer></script>
|
||||
<script src="js/structure_lifecycle.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_render_runtime.js?v=39.15.39" defer></script>
|
||||
<script src="js/ants.js?v=39.15.39" defer></script>
|
||||
<script src="js/health.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_action_spec.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_behavior_state.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_identity_social.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_action_state.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_disease_nest.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_item_effects.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_needs_core.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_behavior_text.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_forced_behavior.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_item_targeting.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_consumable_behavior.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_social_action_runtime.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_building_behavior.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_action_definitions.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_needs_items.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_forced_action_planner_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_action_planner_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_need_planner_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_item_interaction_context.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_food_interaction_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_contact_item_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_item_interaction_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_sunbath_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_cursor_care_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_local_environment_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_social_move_life.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_update_step_frame.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_update_step_ai.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_update_step_environment.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_update_step_movement.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_update_step_health.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_update_pipeline.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_runtime.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_render.js?v=39.15.39" defer></script>
|
||||
<script src="js/world.js?v=39.15.39" defer></script>
|
||||
<script src="js/world_view.js?v=39.15.39" defer></script>
|
||||
<script src="js/family_graph.js?v=39.15.39" defer></script>
|
||||
<script src="js/world_family_social.js?v=39.15.39" defer></script>
|
||||
<script src="js/world_combat_effects.js?v=39.15.39" defer></script>
|
||||
<script src="js/world_environment.js?v=39.15.39" defer></script>
|
||||
<script src="js/world_spatial_budget.js?v=39.15.39" defer></script>
|
||||
<script src="js/world_ants_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/weather_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/item_update_scheduler.js?v=39.15.39" defer></script>
|
||||
<script src="js/simulation_runtime_helpers.js?v=39.15.39" defer></script>
|
||||
<script src="js/tarinai_update_policy.js?v=39.15.39" defer></script>
|
||||
<script src="js/simulation_environment_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/simulation_item_ant_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/simulation_effects_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/simulation_creature_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/simulation_maintenance_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/simulation_ambient_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/simulation_systems.js?v=39.15.39" defer></script>
|
||||
<script src="js/world_update_phases.js?v=39.15.39" defer></script>
|
||||
<script src="js/system_order.js?v=39.15.39" defer></script>
|
||||
<script src="js/simulation.js?v=39.15.39" defer></script>
|
||||
<script src="js/world_update.js?v=39.15.39" defer></script>
|
||||
<script src="js/world_tool_actions.js?v=39.15.39" defer></script>
|
||||
<script src="js/world_placement_log.js?v=39.15.39" defer></script>
|
||||
<script src="js/world_event_effects.js?v=39.15.39" defer></script>
|
||||
<script src="js/command_dispatcher.js?v=39.15.39" defer></script>
|
||||
<script src="js/text_catalog.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_log.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_helpers.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_selected.js?v=39.15.39" defer></script>
|
||||
<script src="js/save_schema.js?v=39.15.39" defer></script>
|
||||
<script src="js/snapshot_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/restore_coordinator.js?v=39.15.39" defer></script>
|
||||
<script src="js/history_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/save_codec.js?v=39.15.39" defer></script>
|
||||
<script src="js/save_storage.js?v=39.15.39" defer></script>
|
||||
<script src="js/save_system.js?v=39.15.39" defer></script>
|
||||
<script src="js/patch_helpers.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_tooltips.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_layout_dialogs.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_ground.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_tools.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_input_shared.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_input_touch.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_input_mouse.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_bind.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_charts.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_family_data.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_family_async.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_family_layout.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_family_paths.js?v=39.15.39" defer></script>
|
||||
<script src="js/ui_family_render.js?v=39.15.39" defer></script>
|
||||
<script src="js/main.js?v=39.15.39" defer></script>
|
||||
<script src="js/debug_tools.js?v=39.15.39" defer></script>
|
||||
<script src="js/version.js?v=39.15.47" defer></script>
|
||||
<script src="js/event_bus.js?v=39.15.47" defer></script>
|
||||
<script src="js/registry_base.js?v=39.15.47" defer></script>
|
||||
<script src="js/disease_registry.js?v=39.15.47" defer></script>
|
||||
<script src="js/sound_pack.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_registry.js?v=39.15.47" defer></script>
|
||||
<script src="js/data.js?v=39.15.47" defer></script>
|
||||
<script src="js/ground_types.js?v=39.15.47" defer></script>
|
||||
<script src="js/input_mode_manager.js?v=39.15.47" defer></script>
|
||||
<script src="js/math.js?v=39.15.47" defer></script>
|
||||
<script src="js/physics_helpers.js?v=39.15.47" defer></script>
|
||||
<script src="js/collision_footprint_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/mechanical_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/constraint_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/physics_world_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/assets.js?v=39.15.47" defer></script>
|
||||
<script src="js/audio.js?v=39.15.47" defer></script>
|
||||
<script src="js/perf_profiler.js?v=39.15.47" defer></script>
|
||||
<script src="js/render.js?v=39.15.47" defer></script>
|
||||
<script src="js/sim_core.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_seed_factory.js?v=39.15.47" defer></script>
|
||||
<script src="js/structures.js?v=39.15.47" defer></script>
|
||||
<script src="js/items.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_type_initializers.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_lifecycle_support.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_lifecycle_runtime.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_update_policy.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_dynamic_tool_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_dynamic_ball_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_dynamic_duplicator_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_dynamic_pin_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_dynamic_zunchi_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_dynamic_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_lifecycle_decay_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_lifecycle_growth_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_lifecycle_step_frame.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_lifecycle_step_decay.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_lifecycle_step_dynamic.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_lifecycle_step_growth.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_lifecycle_pipeline.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_runtime.js?v=39.15.47" defer></script>
|
||||
<script src="js/structure_lifecycle.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_render_runtime.js?v=39.15.47" defer></script>
|
||||
<script src="js/ants.js?v=39.15.47" defer></script>
|
||||
<script src="js/health.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_action_spec.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_behavior_state.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_identity_social.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_action_state.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_disease_nest.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_item_effects.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_needs_core.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_behavior_text.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_forced_behavior.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_item_targeting.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_consumable_behavior.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_social_action_runtime.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_building_behavior.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_action_definitions.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_needs_items.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_forced_action_planner_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_action_planner_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_need_planner_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_item_interaction_context.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_food_interaction_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_contact_item_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_item_interaction_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_sunbath_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_cursor_care_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_local_environment_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_social_move_life.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_update_step_frame.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_update_step_ai.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_update_step_environment.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_update_step_movement.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_update_step_health.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_update_pipeline.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_runtime.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_render.js?v=39.15.47" defer></script>
|
||||
<script src="js/world.js?v=39.15.47" defer></script>
|
||||
<script src="js/world_view.js?v=39.15.47" defer></script>
|
||||
<script src="js/family_graph.js?v=39.15.47" defer></script>
|
||||
<script src="js/world_family_social.js?v=39.15.47" defer></script>
|
||||
<script src="js/world_combat_effects.js?v=39.15.47" defer></script>
|
||||
<script src="js/world_environment.js?v=39.15.47" defer></script>
|
||||
<script src="js/world_spatial_budget.js?v=39.15.47" defer></script>
|
||||
<script src="js/world_ants_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/weather_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/item_update_scheduler.js?v=39.15.47" defer></script>
|
||||
<script src="js/simulation_runtime_helpers.js?v=39.15.47" defer></script>
|
||||
<script src="js/tarinai_update_policy.js?v=39.15.47" defer></script>
|
||||
<script src="js/simulation_environment_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/simulation_item_ant_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/simulation_effects_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/simulation_creature_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/simulation_maintenance_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/simulation_ambient_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/simulation_systems.js?v=39.15.47" defer></script>
|
||||
<script src="js/world_update_phases.js?v=39.15.47" defer></script>
|
||||
<script src="js/system_order.js?v=39.15.47" defer></script>
|
||||
<script src="js/simulation.js?v=39.15.47" defer></script>
|
||||
<script src="js/world_update.js?v=39.15.47" defer></script>
|
||||
<script src="js/world_tool_actions.js?v=39.15.47" defer></script>
|
||||
<script src="js/world_placement_log.js?v=39.15.47" defer></script>
|
||||
<script src="js/world_event_effects.js?v=39.15.47" defer></script>
|
||||
<script src="js/command_dispatcher.js?v=39.15.47" defer></script>
|
||||
<script src="js/text_catalog.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_log.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_helpers.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_selected.js?v=39.15.47" defer></script>
|
||||
<script src="js/save_schema.js?v=39.15.47" defer></script>
|
||||
<script src="js/snapshot_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/restore_coordinator.js?v=39.15.47" defer></script>
|
||||
<script src="js/history_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/save_codec.js?v=39.15.47" defer></script>
|
||||
<script src="js/save_storage.js?v=39.15.47" defer></script>
|
||||
<script src="js/save_system.js?v=39.15.47" defer></script>
|
||||
<script src="js/patch_helpers.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_tooltips.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_layout_dialogs.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_ground.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_tools.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_input_shared.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_input_touch.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_input_mouse.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_bind.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_charts.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_family_data.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_family_async.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_family_layout.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_family_paths.js?v=39.15.47" defer></script>
|
||||
<script src="js/ui_family_render.js?v=39.15.47" defer></script>
|
||||
<script src="js/main.js?v=39.15.47" defer></script>
|
||||
<script src="js/debug_tools.js?v=39.15.47" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -89,10 +89,10 @@
|
|||
item.deletable = false;
|
||||
item.pinTargetId = t.id;
|
||||
const faceDir = t.facingDir ? t.facingDir() : (t.facing || 1);
|
||||
const visualSide = isOshibyo ? -faceDir : faceDir;
|
||||
const visualSide = isOshibyo ? -1 : faceDir;
|
||||
item.pinVisualSide = visualSide;
|
||||
item.pinAttachAngle = isOshibyo ? visualSide * 0.36 : Math.atan2(dy || -1, dx || visualSide);
|
||||
item.pinAttachDistance = isOshibyo ? (t.radius || 16) * 0.72 : clamp(distToTarget || (t.radius || 16) * 0.58, (t.radius || 16) * 0.20, (t.radius || 16) * 0.74);
|
||||
item.pinAttachAngle = isOshibyo ? -0.46 : Math.atan2(dy || -1, dx || visualSide);
|
||||
item.pinAttachDistance = isOshibyo ? (t.radius || 16) * 0.88 : clamp(distToTarget || (t.radius || 16) * 0.58, (t.radius || 16) * 0.20, (t.radius || 16) * 0.74);
|
||||
item.pinOffsetY = isOshibyo ? (t.radius || 16) * 0.40 : clamp(dy, -(t.radius || 16) * 0.74, (t.radius || 16) * 0.38);
|
||||
item.pinDamageTick = 0;
|
||||
item.pinFallCheckTimer = 0;
|
||||
|
|
@ -138,13 +138,13 @@
|
|||
t.stuckPushpinId = item.id;
|
||||
item.deletable = false;
|
||||
const faceDir = t.facingDir ? t.facingDir() : (t.facing || 1);
|
||||
const visualSide = isOshibyo ? -faceDir : faceDir;
|
||||
const visualSide = isOshibyo ? -1 : faceDir;
|
||||
item.pinVisualSide = visualSide;
|
||||
item.x = isOshibyo ? t.x + visualSide * (t.radius || 16) * 0.72 : t.x + visualSide * (t.radius || 16) * 0.36;
|
||||
item.x = isOshibyo ? t.x - (t.radius || 16) * 0.88 : t.x + visualSide * (t.radius || 16) * 0.36;
|
||||
item.y = isOshibyo ? t.y + (t.radius || 16) * 0.42 : t.y - (t.radius || 16) * 0.04;
|
||||
item.prevX = item.x;
|
||||
item.prevY = item.y;
|
||||
item.spin = visualSide * (isOshibyo ? 0.52 : 0.28);
|
||||
item.spin = isOshibyo ? -0.52 : visualSide * 0.28;
|
||||
item.vx = t.vx || 0;
|
||||
item.vy = t.vy || 0;
|
||||
if (isOshibyo) {
|
||||
|
|
|
|||
|
|
@ -56,7 +56,27 @@
|
|||
|
||||
|
||||
function sprinklerRange(item) {
|
||||
return Math.max(96, (Number(item?.r || 28) || 28) * 4.6);
|
||||
// v39.15.47: effective cleaning radius increased to 3x the prior radius.
|
||||
// Previous formula: max(96, r * 4.6). Current: max(288, r * 13.8).
|
||||
return Math.max(288, (Number(item?.r || 28) || 28) * 13.8);
|
||||
}
|
||||
|
||||
function spawnSprinklerWaterEffects(item, worldRef, range, flashStrength = 1) {
|
||||
if (!worldRef?.effects || !item) return;
|
||||
const burst = 8;
|
||||
for (let i = 0; i < burst; i++) {
|
||||
const base = (Math.PI * 2 * i) / burst + (Number(item.sprinklerSpin || 0) || 0) * 0.35;
|
||||
const dist = Math.min(range * 0.55, 46 + i * 6);
|
||||
const px = item.x + Math.cos(base) * dist;
|
||||
const py = item.y + Math.sin(base) * dist * 0.72 - 6;
|
||||
worldRef.effects.push(new Effect("ring", px, py, {
|
||||
size: 6 + (i % 3) * 2,
|
||||
life: 0.16 + (i % 2) * 0.05,
|
||||
color: `rgba(120,204,245,${0.28 + 0.12 * flashStrength})`,
|
||||
vx: Math.cos(base) * 8,
|
||||
vy: Math.sin(base) * 6 - 10,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function updateSprinkler(item, dt, worldRef) {
|
||||
|
|
@ -77,9 +97,10 @@
|
|||
if (distXY(item.x, item.y, it.x, it.y) <= range + r) targets.push(it);
|
||||
}
|
||||
const sprayAngle = global.deterministicAngle ? global.deterministicAngle(worldRef, "sprinkler-spray-angle", item) : Math.random() * Math.PI * 2;
|
||||
const sprayX = item.x + Math.cos(sprayAngle) * Math.min(range * 0.42, 86);
|
||||
const sprayY = item.y + Math.sin(sprayAngle) * Math.min(range * 0.42, 86);
|
||||
const sprayX = item.x + Math.cos(sprayAngle) * Math.min(range * 0.42, 132);
|
||||
const sprayY = item.y + Math.sin(sprayAngle) * Math.min(range * 0.42, 132);
|
||||
worldRef.effects?.push(new Effect("ring", sprayX, sprayY, { size: 14, life: 0.18, color: "rgba(95,166,230,0.22)" }));
|
||||
spawnSprinklerWaterEffects(item, worldRef, range, 0.8);
|
||||
if (!targets.length) return true;
|
||||
const pickIndex = Math.floor((global.deterministicUnit ? global.deterministicUnit(worldRef, "sprinkler-pick", item, targets.length) : Math.random()) * targets.length) % targets.length;
|
||||
const target = targets[pickIndex];
|
||||
|
|
@ -87,8 +108,10 @@
|
|||
target.amount = 0;
|
||||
target.hp = 0;
|
||||
target.removedReason = "sprinkler-clean";
|
||||
item.sprinklerFlash = 0.45;
|
||||
item.sprinklerFlash = 0.55;
|
||||
item.sprinklerJustActivated = 0.22;
|
||||
worldRef.effects?.push(new Effect("ring", target.x, target.y, { size: Math.max(18, (target.r || 12) * 1.6), life: 0.28, color: "rgba(95,166,230,0.34)" }));
|
||||
spawnSprinklerWaterEffects(item, worldRef, range, 1);
|
||||
worldRef.markItemBucketsDirty?.("sprinkler-clean");
|
||||
worldRef.markSpatialDirty?.("sprinkler-clean");
|
||||
worldRef.updateItemCounts?.();
|
||||
|
|
@ -123,6 +146,7 @@
|
|||
updateReciprocator,
|
||||
updatePoisonBlock,
|
||||
updateSprinkler,
|
||||
sprinklerRange,
|
||||
updateFlexibleLink,
|
||||
updateRigidLink,
|
||||
resolveMechanicalInteractions,
|
||||
|
|
|
|||
|
|
@ -297,41 +297,115 @@ draw(ctx, t, lighting = null) {
|
|||
ctx.save();
|
||||
const flash = clamp(Number(this.sprinklerFlash || 0) || 0, 0, 1);
|
||||
const spin = Number(this.sprinklerSpin || 0) || 0;
|
||||
ctx.fillStyle = styleNight ? "#405b6f" : "#8fc7df";
|
||||
ctx.strokeStyle = styleNight ? "#20313f" : "#4e7f96";
|
||||
ctx.lineWidth = Math.max(1.6, this.r * 0.06);
|
||||
const range = globalThis.TarinaiItemDynamicToolSystem?.sprinklerRange ? globalThis.TarinaiItemDynamicToolSystem.sprinklerRange(this) : Math.max(288, (Number(this.r || 28) || 28) * 13.8);
|
||||
const metalA = styleNight ? "#6a7782" : "#d6dde3";
|
||||
const metalB = styleNight ? "#39434d" : "#8a969f";
|
||||
const darkMetal = styleNight ? "#1e252b" : "#53606a";
|
||||
const baseGreen = styleNight ? "#4f6a3a" : "#76a54b";
|
||||
const baseGreenDark = styleNight ? "#304324" : "#547635";
|
||||
const hose = styleNight ? "#2f3a42" : "#4b5963";
|
||||
// base shadow
|
||||
ctx.fillStyle = "rgba(30,40,48,0.14)";
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(0, this.r * 0.22, this.r * 0.74, this.r * 0.34, 0, 0, Math.PI * 2);
|
||||
ctx.ellipse(0, this.r * 0.70, this.r * 0.92, this.r * 0.26, 0, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
// ground stake and foot
|
||||
ctx.fillStyle = baseGreen;
|
||||
ctx.strokeStyle = baseGreenDark;
|
||||
ctx.lineWidth = Math.max(1.6, this.r * 0.05);
|
||||
roundedRect(ctx, -this.r * 0.56, this.r * 0.12, this.r * 1.12, this.r * 0.46, this.r * 0.12);
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
ctx.fillStyle = styleNight ? "#587789" : "#b8e5f2";
|
||||
roundedRect(ctx, -this.r * 0.28, -this.r * 0.46, this.r * 0.56, this.r * 0.78, this.r * 0.12);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-this.r * 0.18, this.r * 0.10);
|
||||
ctx.lineTo(-this.r * 0.38, this.r * 0.88);
|
||||
ctx.lineTo(-this.r * 0.18, this.r * 0.88);
|
||||
ctx.lineTo(-this.r * 0.02, this.r * 0.10);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.r * 0.18, this.r * 0.10);
|
||||
ctx.lineTo(this.r * 0.38, this.r * 0.88);
|
||||
ctx.lineTo(this.r * 0.18, this.r * 0.88);
|
||||
ctx.lineTo(this.r * 0.02, this.r * 0.10);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
// riser pipe
|
||||
const pipeGrad = ctx.createLinearGradient(-this.r * 0.12, -this.r * 0.54, this.r * 0.12, this.r * 0.36);
|
||||
pipeGrad.addColorStop(0, metalA);
|
||||
pipeGrad.addColorStop(0.55, metalB);
|
||||
pipeGrad.addColorStop(1, metalA);
|
||||
ctx.fillStyle = pipeGrad;
|
||||
ctx.strokeStyle = darkMetal;
|
||||
roundedRect(ctx, -this.r * 0.12, -this.r * 0.30, this.r * 0.24, this.r * 0.62, this.r * 0.08);
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
// head and rotor
|
||||
ctx.save();
|
||||
ctx.translate(0, -this.r * 0.40);
|
||||
ctx.rotate(spin);
|
||||
ctx.strokeStyle = flash > 0 ? "rgba(174,226,255,0.95)" : (styleNight ? "rgba(142,190,214,0.72)" : "rgba(67,129,160,0.78)");
|
||||
ctx.lineWidth = Math.max(2.0, this.r * 0.075);
|
||||
const armGrad = ctx.createLinearGradient(-this.r * 0.84, 0, this.r * 0.84, 0);
|
||||
armGrad.addColorStop(0, metalB);
|
||||
armGrad.addColorStop(0.5, metalA);
|
||||
armGrad.addColorStop(1, metalB);
|
||||
ctx.strokeStyle = armGrad;
|
||||
ctx.lineWidth = Math.max(2.4, this.r * 0.09);
|
||||
ctx.lineCap = "round";
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-this.r * 0.58, -this.r * 0.46);
|
||||
ctx.lineTo(this.r * 0.58, -this.r * 0.46);
|
||||
ctx.moveTo(0, -this.r * 0.72);
|
||||
ctx.lineTo(0, -this.r * 0.20);
|
||||
ctx.moveTo(-this.r * 0.72, 0);
|
||||
ctx.lineTo(this.r * 0.72, 0);
|
||||
ctx.stroke();
|
||||
ctx.strokeStyle = darkMetal;
|
||||
ctx.lineWidth = Math.max(1.2, this.r * 0.04);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, -this.r * 0.28);
|
||||
ctx.lineTo(0, this.r * 0.20);
|
||||
ctx.stroke();
|
||||
for (const sx of [-1, 1]) {
|
||||
ctx.fillStyle = hose;
|
||||
ctx.beginPath();
|
||||
ctx.arc(sx * this.r * 0.68, -this.r * 0.46, this.r * 0.10, 0, Math.PI * 2);
|
||||
ctx.fillStyle = "rgba(112,184,224,0.72)";
|
||||
ctx.arc(sx * this.r * 0.74, 0, this.r * 0.10, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.fillStyle = flash > 0 ? "rgba(170,225,255,0.95)" : "rgba(118,188,232,0.80)";
|
||||
ctx.beginPath();
|
||||
ctx.arc(sx * this.r * 0.80, 0, this.r * 0.06, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
if (flash > 0.02) {
|
||||
ctx.strokeStyle = `rgba(142,216,255,${0.45 + flash * 0.35})`;
|
||||
ctx.lineWidth = Math.max(1.0, this.r * 0.04);
|
||||
for (const sx of [-1, 1]) {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const dir = sx < 0 ? -1 : 1;
|
||||
const spread = (i - 1) * 0.18;
|
||||
const len = this.r * (0.95 + i * 0.20);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(sx * this.r * 0.80, 0);
|
||||
ctx.quadraticCurveTo(sx * this.r * (1.10 + i * 0.14), -this.r * (0.10 + Math.abs(spread) * 0.4), sx * this.r * (1.36 + i * 0.18), -this.r * (0.18 + spread));
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.arc(sx * this.r * (1.36 + i * 0.18), -this.r * (0.18 + spread), this.r * 0.035 * (1.0 + i * 0.14), 0, Math.PI * 2);
|
||||
ctx.fillStyle = `rgba(164,228,255,${0.35 + flash * 0.25})`;
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.restore();
|
||||
const rangePulse = 0.30 + flash * 0.45;
|
||||
ctx.strokeStyle = `rgba(88,157,214,${rangePulse})`;
|
||||
ctx.lineWidth = 1.4;
|
||||
ctx.setLineDash([4, 5]);
|
||||
// top cap
|
||||
ctx.fillStyle = metalA;
|
||||
ctx.strokeStyle = darkMetal;
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, Math.max(this.r * 2.15, 44), 0, Math.PI * 2);
|
||||
ctx.arc(0, -this.r * 0.40, this.r * 0.16, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
const rangePulse = 0.14 + flash * 0.30;
|
||||
ctx.strokeStyle = `rgba(88,157,214,${rangePulse})`;
|
||||
ctx.lineWidth = 1.2;
|
||||
ctx.setLineDash([7, 6]);
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, range, 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
} else if (this.type === "duplicator") {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ function initializeItemTypeState(item, type, x, y) {
|
|||
item.sprinklerTimer = rand(0.18, 0.72);
|
||||
item.sprinklerSpin = rand(0, Math.PI * 2);
|
||||
item.sprinklerFlash = 0;
|
||||
item.sprinklerJustActivated = 0;
|
||||
}
|
||||
if (type === "ball") {
|
||||
item.vx = rand(-5, 5);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,73 @@
|
|||
return true;
|
||||
}
|
||||
|
||||
function ownedStructureDestroyedLabel(structure) {
|
||||
if (structure?.type === "plushie") return "ぬいぐるみ";
|
||||
if (structure?.type === "grass_bed") return "かんたんベッド";
|
||||
return structure?.label || "自分のもの";
|
||||
}
|
||||
|
||||
function shouldPanicOwnerOnStructureGone(structure, reason = "") {
|
||||
if (!structure || !structure.ownerId) return false;
|
||||
if (!(structure.type === "plushie" || structure.type === "grass_bed")) return false;
|
||||
if (String(reason || "") === "grass-bed-wake-used") return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function triggerOwnedStructureDestroyedPanic(worldRef, structure, breaker = null, reason = "structure-destroyed") {
|
||||
if (!worldRef || !shouldPanicOwnerOnStructureGone(structure, reason)) return false;
|
||||
const owner = (worldRef.tarinai || []).find(t => t && !t.dead && t.id === structure.ownerId);
|
||||
if (!owner) return false;
|
||||
const label = ownedStructureDestroyedLabel(structure);
|
||||
const detail = `自分の${label}が壊された`;
|
||||
const panicReason = `自分の${label}を壊されてパニックになっている`;
|
||||
const threat = breaker || {
|
||||
id: `${structure.id || "structure"}:destroyed`,
|
||||
x: Number(structure.x) || owner.x,
|
||||
y: Number(structure.y) || owner.y,
|
||||
type: "owned_structure_destroyed",
|
||||
dead: false,
|
||||
};
|
||||
owner.lastPanicDetail = detail;
|
||||
owner.fearTimer = Math.max(owner.fearTimer || 0, structure.type === "plushie" ? 3.2 : 2.6);
|
||||
owner.surpriseTimer = Math.max(owner.surpriseTimer || 0, 0.85);
|
||||
owner.awakeLockTimer = Math.max(owner.awakeLockTimer || 0, 2.4);
|
||||
if (owner.enterPanic) {
|
||||
owner.enterPanic({
|
||||
threat,
|
||||
target: owner.panicDestination?.(threat, true) || null,
|
||||
forceDestination: true,
|
||||
reason: panicReason,
|
||||
fear: structure.type === "plushie" ? 1.65 : 1.35,
|
||||
stress: structure.type === "plushie" ? 20 : 15,
|
||||
wake: true,
|
||||
cause: `${structure.type}_destroyed`,
|
||||
surpriseTimer: 0.85,
|
||||
awakeLockTimer: 2.4,
|
||||
});
|
||||
} else {
|
||||
owner.setActionState?.("panic", { target: owner.panicDestination?.(threat, true) || null, reason: panicReason, wake: true, sleeping: false });
|
||||
}
|
||||
if (typeof global.setBehaviorText === "function") {
|
||||
global.setBehaviorText(owner, {
|
||||
need: "safety",
|
||||
actionId: "panic_escape",
|
||||
actionLabel: "パニックになっている",
|
||||
reasonText: panicReason,
|
||||
causeText: detail,
|
||||
target: threat,
|
||||
phase: "perform",
|
||||
source: "behavior",
|
||||
});
|
||||
}
|
||||
owner.thought = panicReason;
|
||||
owner.behaviorLockTimer = Math.max(owner.behaviorLockTimer || 0, 1.2);
|
||||
worldRef.spawnBubble?.(owner.x, owner.y - (owner.radius || 20) * 1.20, "!?", "rgba(188,66,76,0.88)");
|
||||
worldRef.log?.(`${owner.name || "たりない"}は自分の${label}を壊されてパニックになった。`, "danger");
|
||||
worldRef.drawListDirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
function releaseStructureUsers(worldRef, structure, opts = {}) {
|
||||
if (!worldRef || !structure) return 0;
|
||||
const reason = opts.reason || "\u306a\u304f\u306a\u3063\u305f";
|
||||
|
|
@ -51,8 +118,10 @@
|
|||
if (!structure || structure.dead) return false;
|
||||
const reason = opts.reason || "structure-deleted";
|
||||
releaseStructureUsers(worldRef, structure, { reason: opts.userReason || "\u7f6e\u304d\u3082\u306e\u304c\u306a\u304f\u306a\u3063\u305f", wake: opts.wake !== false });
|
||||
const ownerPanic = shouldPanicOwnerOnStructureGone(structure, reason);
|
||||
const changed = markStructureGone(structure, reason);
|
||||
if (!changed) return false;
|
||||
if (ownerPanic && opts.panicOwner !== false) triggerOwnedStructureDestroyedPanic(worldRef, structure, opts.breaker || null, reason);
|
||||
markWorldAfterStructureChange(worldRef, reason);
|
||||
worldRef?.emit?.("structure:deleted", { structure, type: structure.type, reason });
|
||||
return true;
|
||||
|
|
@ -73,7 +142,7 @@
|
|||
|
||||
function deleteItem(worldRef, item, opts = {}) {
|
||||
if (!item || item.dead) return false;
|
||||
if (item.isStructure) return deleteStructure(worldRef, item, { reason: opts.reason || "delete-tool", userReason: opts.userReason, wake: opts.wake });
|
||||
if (item.isStructure) return deleteStructure(worldRef, item, { reason: opts.reason || "delete-tool", userReason: opts.userReason, wake: opts.wake, breaker: opts.breaker || null, panicOwner: opts.panicOwner });
|
||||
item.amount = 0;
|
||||
item.hp = 0;
|
||||
item.removedReason = String(opts.reason || "delete-tool");
|
||||
|
|
@ -87,5 +156,6 @@
|
|||
deleteStructure,
|
||||
consumeGrassBedOnWake,
|
||||
deleteItem,
|
||||
triggerOwnedStructureDestroyedPanic,
|
||||
});
|
||||
})(window);
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@
|
|||
this.dead = true;
|
||||
this.amount = 0;
|
||||
global.StructureRegistry?.get?.(this.type)?.onDestroyed?.(this, worldRef, breaker);
|
||||
global.TarinaiStructureLifecycle?.triggerOwnedStructureDestroyedPanic?.(worldRef, this, breaker, "structure-damaged-destroyed");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,36 +61,18 @@ function bindUI() {
|
|||
}
|
||||
|
||||
function openAutoCollapsedSectionsForMobileScroll() {
|
||||
if (!isTouchMobileLayout()) return;
|
||||
for (const card of document.querySelectorAll(".collapsible-card.collapsed")) {
|
||||
if (card.dataset.userCollapsed === "1") continue;
|
||||
markCollapsibleCardState(card, false);
|
||||
}
|
||||
for (const category of ui.toolPalette?.querySelectorAll(".tool-category.collapsed") || []) {
|
||||
if (category.dataset.userCollapsed === "1") continue;
|
||||
markToolCategoryState(category, false);
|
||||
}
|
||||
// v39.15.47: 自動スクロールは折り畳み状態を一切変更しない。
|
||||
return;
|
||||
}
|
||||
|
||||
function scrollWindowToElement(target, offset = 0, behavior = "smooth") {
|
||||
function scrollWindowToElement(target, offset = 0, behavior = "auto") {
|
||||
if (!target) return;
|
||||
const scroller = document.scrollingElement || document.documentElement;
|
||||
const clampTop = (value) => Math.max(0, Math.min(value, Math.max(0, scroller.scrollHeight - window.innerHeight)));
|
||||
const desiredTop = () => clampTop(window.scrollY + target.getBoundingClientRect().top - offset);
|
||||
const go = (b = behavior) => window.scrollTo({ top: desiredTop(), behavior: b });
|
||||
go(behavior);
|
||||
// Explicit button scroll only. No scroll-snap listener is used; these
|
||||
// delayed corrections are only for DOM height changes after opening cards
|
||||
// or rendering the family graph.
|
||||
let tries = 0;
|
||||
const correct = () => {
|
||||
tries += 1;
|
||||
const rect = target.getBoundingClientRect();
|
||||
const delta = rect.top - offset;
|
||||
if (Math.abs(delta) > 10) go("auto");
|
||||
if (tries < 5 && Math.abs(delta) > 10) window.setTimeout(correct, tries < 2 ? 180 : 320);
|
||||
};
|
||||
window.setTimeout(correct, 180);
|
||||
const rect = target.getBoundingClientRect?.();
|
||||
if (!rect) return;
|
||||
const maxTop = Math.max(0, scroller.scrollHeight - window.innerHeight);
|
||||
const top = Math.max(0, Math.min(window.scrollY + rect.top - offset, maxTop));
|
||||
window.scrollTo({ top, behavior: "auto" });
|
||||
}
|
||||
|
||||
function scrollPanelToTab(tab = "") {
|
||||
|
|
@ -108,30 +90,18 @@ function bindUI() {
|
|||
if (!target) return false;
|
||||
target.classList?.remove?.("hidden");
|
||||
if (tab === "family") {
|
||||
// Auto update OFF should not make the family graph disappear.
|
||||
// A direct jump to the family section is treated as a manual refresh,
|
||||
// while continuous auto-refresh remains disabled.
|
||||
// 更新OFFでも、ユーザーが家系図へ移動した時だけ現在スナップショットを描画する。
|
||||
window.resetArchiveRenderState?.();
|
||||
renderArchive?.();
|
||||
}
|
||||
if (isTouchMobileLayout()) openAutoCollapsedSectionsForMobileScroll();
|
||||
const collapsedCard = target.closest?.(".collapsible-card") || (target.classList?.contains?.("collapsible-card") ? target : null);
|
||||
if (collapsedCard?.classList?.contains?.("collapsed") && collapsedCard.dataset.userCollapsed !== "1") {
|
||||
markCollapsibleCardState(collapsedCard, false);
|
||||
}
|
||||
const tabsH = ui.panelQuickTabs?.offsetHeight || 0;
|
||||
const style = window.getComputedStyle ? window.getComputedStyle(panel) : null;
|
||||
const panelScrollable = panel.scrollHeight > panel.clientHeight + 4 && style?.overflowY !== "visible";
|
||||
const mobileOffset = isTouchMobileLayout()
|
||||
? ((ui.panelQuickTabs?.getBoundingClientRect?.().height || tabsH || 0) + 10)
|
||||
: 0;
|
||||
if (tab === "game") {
|
||||
scrollWindowToElement(target, 8);
|
||||
} else if (panelScrollable && !isTouchMobileLayout()) {
|
||||
const isDesktop = window.matchMedia?.("(min-width: 981px)")?.matches ?? window.innerWidth >= 981;
|
||||
if (isDesktop && tab !== "game" && tab !== "family") {
|
||||
const tabsH = ui.panelQuickTabs?.offsetHeight || 0;
|
||||
const top = Math.max(0, target.offsetTop - tabsH - 8);
|
||||
panel.scrollTo({ top, behavior: "smooth" });
|
||||
panel.scrollTo({ top, behavior: "auto" });
|
||||
} else {
|
||||
scrollWindowToElement(target, mobileOffset);
|
||||
// スマホはページ全体スクロール。目的地UIの上端を画面上端へ合わせる。
|
||||
scrollWindowToElement(target, 0, "auto");
|
||||
}
|
||||
setActivePanelTab(tab);
|
||||
return true;
|
||||
|
|
@ -143,7 +113,7 @@ function bindUI() {
|
|||
const btn = e.target.closest("[data-panel-tab]");
|
||||
if (!btn) return;
|
||||
const now = performance?.now?.() ?? Date.now();
|
||||
if (now - Number(ui.panelQuickTabs?.dataset?.lastActivateAt || 0) < 160) return;
|
||||
if (now - Number(ui.panelQuickTabs?.dataset?.lastActivateAt || 0) < 50) return;
|
||||
if (ui.panelQuickTabs) ui.panelQuickTabs.dataset.lastActivateAt = String(now);
|
||||
e.preventDefault();
|
||||
audio.uiClick?.();
|
||||
|
|
|
|||
|
|
@ -42,12 +42,18 @@ function lineageTreeNodeHtml(n, x, y) {
|
|||
const reason = briefReason ? ` / ${briefReason}` : "";
|
||||
const rawTags = live?.personalityTags ? live.personalityTags() : (Array.isArray(n?.personalityTags) ? n.personalityTags : []);
|
||||
const tags = rawTags.slice();
|
||||
const displayTags = tags.slice(0, 4);
|
||||
const tagText = tags.length ? ` / ${tags.join(" ")}` : "";
|
||||
const title = escapeHtml(`${n?.name || "\u4e0d\u660e"} / \u4e16\u4ee3 ${n?.generation || "?"} / ${status}${reason}${tagText}`);
|
||||
const deathLine = briefReason ? `<span class="death-reason">${escapeHtml(briefReason)}</span>` : "";
|
||||
const tagLine = tags.length ? `<span class="family-personality-tags">${tags.map(tag => `<em>${escapeHtml(tag)}</em>`).join("")}</span>` : "";
|
||||
const tagLine = displayTags.length ? `<span class="family-personality-tags">${displayTags.map(tag => `<em>${escapeHtml(tag)}</em>`).join("")}</span>` : "";
|
||||
const champion = !!(live?.isTarinaiChampion || n?.isTarinaiChampion);
|
||||
const statLine = n ? `<span class="family-genetic-stats" title="寿命 / 体格 / 攻撃 / 速度 / 勝率"><b>寿</b>${escapeHtml(lineageSignedPercentText(lineageGeneticPercentFromNode(n, "life")))} <b>体</b>${escapeHtml(lineageSignedPercentText(lineageGeneticPercentFromNode(n, "size")))} <b>攻</b>${escapeHtml(lineageSignedPercentText(lineageGeneticPercentFromNode(n, "attack")))} <b>速</b>${escapeHtml(lineageSignedPercentText(lineageGeneticPercentFromNode(n, "speed")))} <b>勝</b>${escapeHtml(lineageFightRateText(n))}</span>` : "";
|
||||
const statLine = n ? `<span class="family-genetic-stats" title="寿命 / 体格 / 攻撃 / 速度">
|
||||
<span><b>寿</b>${escapeHtml(lineageSignedPercentText(lineageGeneticPercentFromNode(n, "life")))}</span>
|
||||
<span><b>体</b>${escapeHtml(lineageSignedPercentText(lineageGeneticPercentFromNode(n, "size")))}</span>
|
||||
<span><b>攻</b>${escapeHtml(lineageSignedPercentText(lineageGeneticPercentFromNode(n, "attack")))}</span>
|
||||
<span><b>速</b>${escapeHtml(lineageSignedPercentText(lineageGeneticPercentFromNode(n, "speed")))}</span>
|
||||
</span><span class="family-fight-stat" title="けんか勝率 / 回数"><b>勝</b>${escapeHtml(lineageFightRateText(n))}</span>` : "";
|
||||
const cls = n ? ((live || n.alive) ? "alive" : "dead") : "missing";
|
||||
const childCls = (n?.parents || []).length ? " child" : "";
|
||||
const iconBaseScale = live
|
||||
|
|
@ -382,7 +388,7 @@ function lineageLayoutRows(rows, idSet) {
|
|||
const SIBLING_GAP = 32;
|
||||
const GROUP_GAP = 52;
|
||||
const PARTNER_GROUP_GAP = 24;
|
||||
const ROW_GAP = 138;
|
||||
const ROW_GAP = 108;
|
||||
const LEFT_PAD = 82;
|
||||
const TOP_PAD = 44;
|
||||
const RIGHT_PAD = 48;
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ function lineageLayoutRowsFast(rows, idSet) {
|
|||
const NODE_W = 150;
|
||||
const NODE_H = 104;
|
||||
const SIBLING_GAP = 22;
|
||||
const ROW_GAP = 116;
|
||||
const ROW_GAP = 92;
|
||||
const LEFT_PAD = 82;
|
||||
const TOP_PAD = 44;
|
||||
const RIGHT_PAD = 48;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
mode = inputModeManager?.setMode?.(mode) || (["auto", "camera", "tool"].includes(mode) ? mode : "auto");
|
||||
uiCache.mobileInputMode = mode;
|
||||
ui.mobileModeControls?.querySelectorAll("[data-mobile-mode]").forEach(btn => btn.classList.toggle("active", btn.dataset.mobileMode === mode));
|
||||
document.body.classList.toggle("mobile-mode-auto", mode === "auto");
|
||||
document.body.classList.toggle("mobile-mode-camera", mode === "camera");
|
||||
document.body.classList.toggle("mobile-mode-tool", mode === "tool");
|
||||
return mode;
|
||||
|
|
|
|||
|
|
@ -5,12 +5,18 @@
|
|||
if (!ctx?.canvas) return false;
|
||||
const { canvas, world, uiCache } = ctx;
|
||||
ctx.bindMobileModeControls();
|
||||
const isMobileAutoScrollMode = () => {
|
||||
const mode = ctx.mobileInputMode?.() || "auto";
|
||||
const touchMobile = document.body.classList.contains("touch-mobile-ui") || window.matchMedia?.("(max-width: 980px)")?.matches;
|
||||
return touchMobile && mode === "auto" && (!world.tool || world.tool === "observe");
|
||||
};
|
||||
|
||||
canvas.addEventListener("touchstart", (e) => {
|
||||
if (!e.touches?.length) return;
|
||||
e.preventDefault();
|
||||
uiCache.touchMoved = false;
|
||||
const mode = ctx.mobileInputMode();
|
||||
const autoPageScroll = isMobileAutoScrollMode();
|
||||
if (!autoPageScroll) e.preventDefault();
|
||||
if (mode === "camera" && e.touches.length === 1) {
|
||||
const t = e.touches[0];
|
||||
uiCache.touchStartX = t.clientX;
|
||||
|
|
@ -62,12 +68,12 @@
|
|||
return;
|
||||
}
|
||||
if (world.tool === "pinch" && p.inside && ctx.beginGrab(p, t.clientX, t.clientY, { touchMode: "grab" })) return;
|
||||
uiCache.touchMode = mode === "tool" ? "toolTapOnly" : "panOrTap";
|
||||
uiCache.touchMode = autoPageScroll ? "autoTapOrScroll" : (mode === "tool" ? "toolTapOnly" : "panOrTap");
|
||||
}, { passive: false });
|
||||
|
||||
canvas.addEventListener("touchmove", (e) => {
|
||||
if (!e.touches?.length || !uiCache.touchMode) return;
|
||||
e.preventDefault();
|
||||
if (uiCache.touchMode !== "autoTapOrScroll") e.preventDefault();
|
||||
if (uiCache.touchMode === "pinchZoom" && e.touches.length >= 2) {
|
||||
const c = ctx.touchCenter(e.touches);
|
||||
const distNow = Math.max(1, ctx.touchDistance(e.touches));
|
||||
|
|
@ -116,6 +122,10 @@
|
|||
ctx.moveGrab(p, t.clientX, t.clientY, { touch: true, renderFrame: true });
|
||||
return;
|
||||
}
|
||||
if (uiCache.touchMode === "autoTapOrScroll") {
|
||||
if (totalMove > 7) uiCache.touchMoved = true;
|
||||
return;
|
||||
}
|
||||
if (uiCache.touchMode === "panOrTap") {
|
||||
if (totalMove > 7) {
|
||||
uiCache.touchMoved = true;
|
||||
|
|
@ -126,7 +136,7 @@
|
|||
}, { passive: false });
|
||||
|
||||
canvas.addEventListener("touchend", (e) => {
|
||||
e.preventDefault();
|
||||
if (uiCache.touchMode !== "autoTapOrScroll") e.preventDefault();
|
||||
if (uiCache.areaDeleting) ctx.endAreaDelete();
|
||||
if (uiCache.hosing) { ctx.endHose(); if (uiCache.hoseHoldTimer) { clearInterval(uiCache.hoseHoldTimer); uiCache.hoseHoldTimer = null; } }
|
||||
if (uiCache.shooting) ctx.endShoot();
|
||||
|
|
@ -138,6 +148,13 @@
|
|||
window.TarinaiCommands?.dispatch?.(world, { type: "pointer.primary", x: p.x, y: p.y });
|
||||
renderStats();
|
||||
}
|
||||
} else if (uiCache.touchMode === "autoTapOrScroll" && !uiCache.touchMoved) {
|
||||
e.preventDefault();
|
||||
const p = ctx.screenToWorldClient(uiCache.touchLastX || uiCache.touchStartX, uiCache.touchLastY || uiCache.touchStartY);
|
||||
if (p.inside) {
|
||||
window.TarinaiCommands?.dispatch?.(world, { type: "pointer.primary", x: p.x, y: p.y });
|
||||
renderStats();
|
||||
}
|
||||
} else if (uiCache.touchMode === "panOrTap" && !uiCache.touchMoved) {
|
||||
const p = ctx.screenToWorldClient(uiCache.touchLastX || uiCache.touchStartX, uiCache.touchLastY || uiCache.touchStartY);
|
||||
if (p.inside) {
|
||||
|
|
|
|||
17
js/ui_log.js
17
js/ui_log.js
|
|
@ -83,12 +83,27 @@ function pushLogNotification(entry = {}) {
|
|||
|
||||
window.pushLogNotification = pushLogNotification;
|
||||
|
||||
function shouldShowInEventLog(entry = {}) {
|
||||
if (!entry) return false;
|
||||
if (entry.eventType === "player_item_placement") return false;
|
||||
const text = String(entry.text || "");
|
||||
const kind = entry.kind || "";
|
||||
// Older saves may contain direct placement records without eventType.
|
||||
// Hide only participant-less player-like placement lines, preserving births, accidents, fights, and natural events.
|
||||
if ((kind === "observe" || kind === "note") && !(entry.participants || []).length && /(?:を配置した。|を落とした。)$/.test(text)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function renderLog(logs) {
|
||||
ui.log.innerHTML = "";
|
||||
const frag = document.createDocumentFragment();
|
||||
const touchFirst = Boolean(window.TarinaiInputMode?.classification?.touchFirst || document.body.classList.contains("touch-mobile-ui") || window.innerWidth <= 980);
|
||||
const limit = touchFirst ? 34 : 80;
|
||||
const rows = Array.isArray(logs) ? logs.slice(0, limit) : [];
|
||||
const visibleLogs = Array.isArray(logs) ? logs.filter(raw => {
|
||||
const entry = typeof raw === "string" ? { time: world.time, text: raw, kind: "note" } : raw;
|
||||
return shouldShowInEventLog(entry);
|
||||
}) : [];
|
||||
const rows = visibleLogs.slice(0, limit);
|
||||
for (const raw of rows) {
|
||||
const entry = typeof raw === "string" ? { time: world.time, text: raw, kind: "note" } : raw;
|
||||
const kind = entry.kind || "note";
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
"use strict";
|
||||
|
||||
(function () {
|
||||
const APP_VERSION = "39.15.39";
|
||||
const APP_BUILD = "tarinai-pc-rollback-mobile-family-fix-v39-15-39";
|
||||
const APP_VERSION = "39.15.47";
|
||||
const APP_BUILD = "tarinai-event-family-oshibyo-fix-v39-15-45";
|
||||
const APP_CACHE_NAME = `tarinai-colony-${APP_VERSION}`;
|
||||
const STATIC_VERSION_PARAM = `v=${APP_VERSION}`;
|
||||
|
||||
|
|
|
|||
|
|
@ -1433,7 +1433,7 @@
|
|||
if (!placed) return;
|
||||
forceImmediateToolVisualRefresh(this, "tool-place");
|
||||
const label = toolLabel(itemType);
|
||||
this.log(itemType === "genkotsu" || isPinType(itemType) ? `${label}\u3092\u843d\u3068\u3057\u305f\u3002` : `${label}\u3092\u914d\u7f6e\u3057\u305f\u3002`);
|
||||
this.log(itemType === "genkotsu" || isPinType(itemType) ? `${label}\u3092\u843d\u3068\u3057\u305f\u3002` : `${label}\u3092\u914d\u7f6e\u3057\u305f\u3002`, "observe", { eventType: "player_item_placement" });
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -378,8 +378,8 @@
|
|||
if (!aim?.inside) return false;
|
||||
const hit = this.findShootToolTargetAt?.(aim.x, aim.y);
|
||||
audio.shoot?.();
|
||||
this.effects?.push(new Effect("shoot_impact", aim.x, aim.y, { size: hit ? 10 : 7, life: hit ? 0.20 : 0.14, color: hit ? "rgba(255,208,116,0.96)" : "rgba(190,212,240,0.58)" }));
|
||||
this.effects?.push(new Effect("ring", aim.x, aim.y, { size: hit ? 8 : 5, life: hit ? 0.11 : 0.08, color: hit ? "rgba(255,246,206,0.72)" : "rgba(196,220,255,0.34)" }));
|
||||
this.effects?.push(new Effect("shoot_impact", aim.x, aim.y, { size: hit ? 22 : 16, life: hit ? 0.28 : 0.22, color: hit ? "rgba(255,208,116,0.96)" : "rgba(190,212,240,0.64)" }));
|
||||
this.effects?.push(new Effect("ring", aim.x, aim.y, { size: hit ? 20 : 14, life: hit ? 0.18 : 0.14, color: hit ? "rgba(255,246,206,0.74)" : "rgba(196,220,255,0.42)" }));
|
||||
let handled = false;
|
||||
if (hit?.kind === "tarinai") {
|
||||
const t = hit.target;
|
||||
|
|
|
|||
104
scripts/ai_inventory.ps1
Normal file
104
scripts/ai_inventory.ps1
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
param(
|
||||
[string]$Query = "",
|
||||
[switch]$All,
|
||||
[switch]$Json
|
||||
)
|
||||
|
||||
$Root = Resolve-Path (Join-Path $PSScriptRoot "..")
|
||||
$RoutingPath = Join-Path $Root "FILES.json"
|
||||
$Routing = Get-Content -Raw -LiteralPath $RoutingPath | ConvertFrom-Json
|
||||
|
||||
$Exact = @{
|
||||
".gitignore" = "ignore rules for local/generated artifacts"
|
||||
".htaccess" = "Apache/static-host cache, MIME, and service-worker behavior"
|
||||
"app_manifest.json" = "canonical version plus CSS/JS/static asset manifest"
|
||||
"favicon.ico" = "legacy favicon"
|
||||
"index.html" = "app DOM shell, canvas, panels, dialogs, and ordered script tags"
|
||||
"README.md" = "small AI entrypoint and read strategy"
|
||||
"AI_MAP.md" = "subsystem map and task-oriented read sets"
|
||||
"FILES.json" = "machine-readable file routing and glob semantics"
|
||||
"service-worker.js" = "generated offline/cache service worker"
|
||||
}
|
||||
|
||||
$JsExact = @{
|
||||
"ants.js" = "ant actor and ant nest worker behavior"
|
||||
"assets.js" = "image loading, image metrics, and render caches"
|
||||
"audio.js" = "sample playback and audio controls"
|
||||
"command_dispatcher.js" = "central command routing for UI/tools/pointers/save"
|
||||
"data.js" = "sprite/decor definitions, needs, config, field tuning"
|
||||
"debug_tools.js" = "browser debug overlay and diagnostics"
|
||||
"family_graph.js" = "lineage graph helpers"
|
||||
"ground_types.js" = "ground definitions, multipliers, limits, labels"
|
||||
"health.js" = "health, stress, need, damage, and death helpers"
|
||||
"history_system.js" = "rolling world statistics/history"
|
||||
"input_mode_manager.js" = "pointer/touch/mobile input classification"
|
||||
"items.js" = "item constructors, food/grass helpers, item preview drawing"
|
||||
"main.js" = "startup, restore, UI binding, update/render loop"
|
||||
"render.js" = "main canvas renderer"
|
||||
"system_order.js" = "named update phase order"
|
||||
"text_catalog.js" = "Japanese UI/state/behavior labels"
|
||||
"version.js" = "app version/cache/static query config"
|
||||
"weather_system.js" = "weather selection and application"
|
||||
}
|
||||
|
||||
function Get-GroupId($Path) {
|
||||
foreach ($Group in $Routing.groups) {
|
||||
foreach ($Pattern in $Group.patterns) {
|
||||
if ($Path -like $Pattern) { return $Group.id }
|
||||
}
|
||||
}
|
||||
return ($Path -split "/")[0]
|
||||
}
|
||||
|
||||
function Get-Meaning($Path) {
|
||||
if ($Exact.ContainsKey($Path)) { return $Exact[$Path] }
|
||||
if ($Path.StartsWith("js/")) {
|
||||
$Name = Split-Path -Leaf $Path
|
||||
if ($JsExact.ContainsKey($Name)) { return $JsExact[$Name] }
|
||||
if ($Name.StartsWith("world_")) { return "world state/view/update/tool/environment/combat/social subsystem" }
|
||||
if ($Name.StartsWith("tarinai_")) { return "creature action/needs/social/item/update subsystem" }
|
||||
if ($Name.StartsWith("item_lifecycle_step_")) { return "item lifecycle pipeline stage" }
|
||||
if ($Name.StartsWith("item_lifecycle_")) { return "item lifecycle subsystem" }
|
||||
if ($Name.StartsWith("item_dynamic_")) { return "dynamic item behavior subsystem" }
|
||||
if ($Name.StartsWith("item_")) { return "item registry/runtime/update/render subsystem" }
|
||||
if ($Name.StartsWith("simulation_")) { return "simulation phase subsystem" }
|
||||
if ($Name.StartsWith("ui_family_")) { return "family tree UI subsystem" }
|
||||
if ($Name.StartsWith("ui_input_")) { return "input handling subsystem" }
|
||||
if ($Name.StartsWith("ui_")) { return "DOM/panel UI subsystem" }
|
||||
if ($Name.StartsWith("save_")) { return "save/load persistence subsystem" }
|
||||
if ($Name.StartsWith("physics_")) { return "physics helper/world subsystem" }
|
||||
return "runtime JavaScript module"
|
||||
}
|
||||
if ($Path.StartsWith("scripts/")) { return "repo script/tooling" }
|
||||
if ($Path.StartsWith("css/")) { return "stylesheet layer" }
|
||||
if ($Path.StartsWith("assets/sprites/")) { return "creature state sprite" }
|
||||
if ($Path.StartsWith("assets/ui/")) { return "UI/app/tool image asset" }
|
||||
if ($Path.StartsWith("assets/objects/")) { return "field object image asset" }
|
||||
if ($Path.StartsWith("assets/sounds/")) { return "voice or sound-effect sample" }
|
||||
return "project file"
|
||||
}
|
||||
|
||||
$Files = Get-ChildItem -LiteralPath $Root -Recurse -File -Force |
|
||||
Where-Object { $_.FullName -notmatch "\\.git\\" -and $_.FullName -notmatch "\\__pycache__\\" } |
|
||||
ForEach-Object { $_.FullName.Substring($Root.Path.Length + 1).Replace("\", "/") } |
|
||||
Sort-Object
|
||||
|
||||
$Records = foreach ($Path in $Files) {
|
||||
$Group = Get-GroupId $Path
|
||||
$Meaning = Get-Meaning $Path
|
||||
if ($Query -and ($Path.ToLowerInvariant() -notlike "*$($Query.ToLowerInvariant())*" -and $Group.ToLowerInvariant() -notlike "*$($Query.ToLowerInvariant())*" -and $Meaning.ToLowerInvariant() -notlike "*$($Query.ToLowerInvariant())*")) {
|
||||
continue
|
||||
}
|
||||
[pscustomobject]@{ path = $Path; group = $Group; meaning = $Meaning }
|
||||
}
|
||||
|
||||
if ($Json) {
|
||||
$Records | ConvertTo-Json -Depth 4
|
||||
} elseif ($All) {
|
||||
$Records | ForEach-Object { "$($_.path): $($_.meaning)" }
|
||||
} else {
|
||||
$Records | Group-Object group | Sort-Object Name | ForEach-Object {
|
||||
"$($_.Name): $($_.Count) files"
|
||||
$_.Group | Select-Object -ExpandProperty meaning -Unique | Sort-Object | Select-Object -First 8 | ForEach-Object { " - $_" }
|
||||
}
|
||||
}
|
||||
207
scripts/ai_inventory.py
Normal file
207
scripts/ai_inventory.py
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import fnmatch
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
ROUTING_FILE = ROOT / "FILES.json"
|
||||
|
||||
|
||||
EXCLUDE_DIRS = {".git", "__pycache__"}
|
||||
|
||||
|
||||
EXACT_DESCRIPTIONS = {
|
||||
".gitignore": "ignore rules for local/generated artifacts",
|
||||
".htaccess": "Apache/static-host cache, MIME, and service-worker behavior",
|
||||
"app_manifest.json": "canonical version plus CSS/JS/static asset manifest",
|
||||
"favicon.ico": "legacy favicon",
|
||||
"index.html": "app DOM shell, canvas, panels, dialogs, and ordered script tags",
|
||||
"README.md": "small AI entrypoint and read strategy",
|
||||
"AI_MAP.md": "subsystem map and task-oriented read sets",
|
||||
"FILES.json": "machine-readable file routing and glob semantics",
|
||||
"service-worker.js": "generated offline/cache service worker",
|
||||
}
|
||||
|
||||
|
||||
GROUP_DESCRIPTIONS = {
|
||||
"css": "stylesheet layer",
|
||||
"scripts": "repo tooling",
|
||||
"js": "runtime JavaScript",
|
||||
"assets/sprites": "creature state sprite",
|
||||
"assets/ui": "UI/app/tool image asset",
|
||||
"assets/objects": "field object image asset",
|
||||
"assets/sounds": "voice or sound-effect sample",
|
||||
}
|
||||
|
||||
|
||||
JS_PREFIX_DESCRIPTIONS = [
|
||||
("world_", "world state/view/update/tool/environment/combat/social subsystem"),
|
||||
("tarinai_", "creature action/needs/social/item/update subsystem"),
|
||||
("item_lifecycle_step_", "item lifecycle pipeline stage"),
|
||||
("item_lifecycle_", "item lifecycle subsystem"),
|
||||
("item_dynamic_", "dynamic item behavior subsystem"),
|
||||
("item_", "item registry/runtime/update/render subsystem"),
|
||||
("simulation_", "simulation phase subsystem"),
|
||||
("ui_family_", "family tree UI subsystem"),
|
||||
("ui_input_", "input handling subsystem"),
|
||||
("ui_", "DOM/panel UI subsystem"),
|
||||
("save_", "save/load persistence subsystem"),
|
||||
("physics_", "physics helper/world subsystem"),
|
||||
]
|
||||
|
||||
|
||||
JS_EXACT_DESCRIPTIONS = {
|
||||
"ants.js": "ant actor and ant nest worker behavior",
|
||||
"assets.js": "image loading, image metrics, and render caches",
|
||||
"audio.js": "sample playback and audio controls",
|
||||
"collision_footprint_system.js": "collision footprint shapes and overlap tests",
|
||||
"command_dispatcher.js": "central command routing for UI/tools/pointers/save",
|
||||
"constraint_system.js": "attachments, ropes, push/pull, and collision constraints",
|
||||
"data.js": "sprite/decor definitions, needs, config, field tuning",
|
||||
"debug_tools.js": "browser debug overlay and diagnostics",
|
||||
"disease_registry.js": "disease definitions and lookup",
|
||||
"event_bus.js": "global event bus singleton",
|
||||
"family_graph.js": "lineage graph helpers",
|
||||
"ground_types.js": "ground definitions, multipliers, limits, labels",
|
||||
"health.js": "health, stress, need, damage, and death helpers",
|
||||
"history_system.js": "rolling world statistics/history",
|
||||
"input_mode_manager.js": "pointer/touch/mobile input classification",
|
||||
"items.js": "item constructors, food/grass helpers, item preview drawing",
|
||||
"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",
|
||||
"restore_coordinator.js": "post-load restore ordering/fixups",
|
||||
"sim_core.js": "simulation primitives and shared helpers",
|
||||
"sound_pack.js": "logical sound ID to asset mapping",
|
||||
"snapshot_system.js": "world snapshot capture/restore",
|
||||
"structures.js": "structure definitions and placement/runtime helpers",
|
||||
"structure_lifecycle.js": "structure update lifecycle",
|
||||
"system_order.js": "named update phase order",
|
||||
"text_catalog.js": "Japanese UI/state/behavior labels",
|
||||
"version.js": "app version/cache/static query config",
|
||||
"weather_system.js": "weather selection and application",
|
||||
}
|
||||
|
||||
|
||||
def rel(path: Path) -> str:
|
||||
return path.relative_to(ROOT).as_posix()
|
||||
|
||||
|
||||
def iter_files() -> list[str]:
|
||||
files: list[str] = []
|
||||
for path in ROOT.rglob("*"):
|
||||
if not path.is_file():
|
||||
continue
|
||||
if any(part in EXCLUDE_DIRS for part in path.relative_to(ROOT).parts):
|
||||
continue
|
||||
files.append(rel(path))
|
||||
return sorted(files)
|
||||
|
||||
|
||||
def load_routing() -> dict:
|
||||
return json.loads(ROUTING_FILE.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def group_for(path: str, routing: dict) -> str:
|
||||
for group in routing.get("groups", []):
|
||||
for pattern in group.get("patterns", []):
|
||||
if fnmatch.fnmatch(path, pattern):
|
||||
return group["id"]
|
||||
return path.split("/", 1)[0]
|
||||
|
||||
|
||||
def describe(path: str, routing: dict) -> str:
|
||||
if path in EXACT_DESCRIPTIONS:
|
||||
return EXACT_DESCRIPTIONS[path]
|
||||
if path.startswith("js/"):
|
||||
name = path.rsplit("/", 1)[-1]
|
||||
if name in JS_EXACT_DESCRIPTIONS:
|
||||
return JS_EXACT_DESCRIPTIONS[name]
|
||||
for prefix, desc in JS_PREFIX_DESCRIPTIONS:
|
||||
if name.startswith(prefix):
|
||||
return desc
|
||||
return "runtime JavaScript module"
|
||||
if path.startswith("scripts/"):
|
||||
return "repo script/tooling"
|
||||
if path.startswith("css/"):
|
||||
return "stylesheet layer"
|
||||
if path.startswith("assets/"):
|
||||
for prefix, desc in GROUP_DESCRIPTIONS.items():
|
||||
if path.startswith(prefix + "/"):
|
||||
return desc
|
||||
return "static asset"
|
||||
group_id = group_for(path, routing)
|
||||
for group in routing.get("groups", []):
|
||||
if group.get("id") == group_id:
|
||||
return group.get("meaning", "project file")
|
||||
return "project file"
|
||||
|
||||
|
||||
def matches_query(path: str, query: str, routing: dict) -> bool:
|
||||
if not query:
|
||||
return True
|
||||
q = query.lower()
|
||||
if q in path.lower():
|
||||
return True
|
||||
group_id = group_for(path, routing).lower()
|
||||
if q == group_id or q in group_id:
|
||||
return True
|
||||
return q in describe(path, routing).lower()
|
||||
|
||||
|
||||
def build_records(query: str = "") -> list[dict]:
|
||||
routing = load_routing()
|
||||
records = []
|
||||
for path in iter_files():
|
||||
if not matches_query(path, query, routing):
|
||||
continue
|
||||
records.append({
|
||||
"path": path,
|
||||
"group": group_for(path, routing),
|
||||
"meaning": describe(path, routing),
|
||||
})
|
||||
return records
|
||||
|
||||
|
||||
def print_compact(records: list[dict]) -> None:
|
||||
groups: dict[str, list[dict]] = {}
|
||||
for record in records:
|
||||
groups.setdefault(record["group"], []).append(record)
|
||||
for group_id in sorted(groups):
|
||||
print(f"{group_id}: {len(groups[group_id])} files")
|
||||
meanings = sorted({record["meaning"] for record in groups[group_id]})
|
||||
for meaning in meanings[:8]:
|
||||
print(f" - {meaning}")
|
||||
|
||||
|
||||
def print_all(records: list[dict]) -> None:
|
||||
for record in records:
|
||||
print(f"{record['path']}: {record['meaning']}")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Print token-aware AI file inventory.")
|
||||
parser.add_argument("query", nargs="?", default="", help="Optional group/path/meaning filter, e.g. ui, world, assets.")
|
||||
parser.add_argument("--all", action="store_true", help="Print every matching file instead of grouped summary.")
|
||||
parser.add_argument("--json", action="store_true", help="Print JSON records.")
|
||||
args = parser.parse_args()
|
||||
|
||||
records = build_records(args.query)
|
||||
if args.json:
|
||||
print(json.dumps(records, ensure_ascii=False, indent=2))
|
||||
elif args.all:
|
||||
print_all(records)
|
||||
else:
|
||||
print_compact(records)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
const APP_VERSION = "39.15.39";
|
||||
const APP_VERSION = "39.15.47";
|
||||
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