Compare commits
41 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 28591233f3 | |||
| 091afc2b5c | |||
| f6d7412744 | |||
| d163ceb291 | |||
| f657b6a4a4 | |||
| 61718e2981 | |||
| 2cc2f003df | |||
| fc7c94bd69 | |||
| 63e10af865 | |||
| d14550dad6 | |||
| 2636d580a8 | |||
| f1c3af2b89 | |||
| 8f9f5b5100 | |||
| 88f618cadf | |||
| ee22e1a929 | |||
| acf93ec6df | |||
| c9dd7182aa | |||
| 4d3fc2cd47 | |||
| 7455d630fc | |||
| ad2cc76429 | |||
| f941200504 | |||
| e00bfd8879 | |||
| 4fdc567e08 | |||
| 348f355eb2 | |||
| 0e432c62ae | |||
| cefa2ace5a | |||
| f500279abd | |||
| 5ed977788c | |||
| 129c07183a | |||
| 0f1cb2fe6c | |||
| 077d4ab9ac | |||
| 86226ccc94 | |||
| fbdac9ebf1 | |||
| 16074232aa | |||
| 434f07cc4e | |||
| 2741b93dea | |||
| d875df8600 | |||
| 9ef1b8dbb1 | |||
| d5c661bc22 | |||
| 8bd4405fe8 | |||
| 54aff02658 |
0
.achievement_data/.gitkeep
Normal file
6
.achievement_data/.htaccess
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<IfModule mod_authz_core.c>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule mod_access_compat.c>
|
||||
Deny from all
|
||||
</IfModule>
|
||||
1
.achievement_data/state.json
Normal file
21
.htaccess
|
|
@ -1,3 +1,5 @@
|
|||
AddType application/manifest+json .webmanifest
|
||||
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
ExpiresByType text/html "access plus 0 seconds"
|
||||
|
|
@ -8,22 +10,35 @@
|
|||
ExpiresByType image/webp "access plus 30 days"
|
||||
ExpiresByType audio/mpeg "access plus 30 days"
|
||||
ExpiresByType audio/wav "access plus 30 days"
|
||||
|
||||
</IfModule>
|
||||
<IfModule mod_headers.c>
|
||||
<FilesMatch "achievement_api\.php$">
|
||||
Header set Cache-Control "no-store, no-cache, must-revalidate, max-age=0"
|
||||
Header set Pragma "no-cache"
|
||||
Header set Expires "0"
|
||||
</FilesMatch>
|
||||
<FilesMatch "\.html?$">
|
||||
Header set Cache-Control "no-cache, must-revalidate"
|
||||
Header set Pragma "no-cache"
|
||||
Header set Expires "0"
|
||||
</FilesMatch>
|
||||
<FilesMatch "\.(css|js|json|webmanifest|ico|png|webp|mp3|wav)$">
|
||||
Header set Cache-Control "public, max-age=2592000, immutable"
|
||||
</FilesMatch>
|
||||
<FilesMatch "service-worker\.js$">
|
||||
Header set Cache-Control "no-cache, no-store, must-revalidate"
|
||||
Header set Pragma "no-cache"
|
||||
Header set Expires "0"
|
||||
</FilesMatch>
|
||||
<FilesMatch "\.(css|js|json|png|webp|mp3|wav)$">
|
||||
Header set Cache-Control "public, max-age=2592000, immutable"
|
||||
</FilesMatch>
|
||||
</IfModule>
|
||||
<IfModule mod_deflate.c>
|
||||
AddOutputFilterByType DEFLATE text/html text/css application/javascript application/json text/plain
|
||||
</IfModule>
|
||||
|
||||
|
||||
# Never serve hidden server-data paths.
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteRule (^|/)\. - [R=404,L]
|
||||
</IfModule>
|
||||
|
|
|
|||
3
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"liveServer.settings.port": 5501
|
||||
}
|
||||
123
PATCH_NOTES.md
|
|
@ -1,123 +0,0 @@
|
|||
# Patch notes
|
||||
|
||||
## 15.20.16
|
||||
|
||||
- Added canonical item visual, food, and disease registries; wired food decay/visual helpers and mystery-drug disease sampling to registry data.
|
||||
- Generated the tool palette from `TOOL_CATEGORIES` at runtime and removed duplicated category markup from `index.html`.
|
||||
- Added compact family-tree personality tags, daily personality-change details, and selected-Tarinai rolling recent-change causes for personality, damage, item-effect, disease, and ant-carry changes.
|
||||
- Added `InputModeManager` for touch-first mobile classification and `WeatherSystem` for weather transitions/rain-water spawning.
|
||||
- Added central domain events for placement, eating, decay, effects, diseases, damage, death, birth, fights, ants, weather, and rain item drops.
|
||||
- Moved item-effect modifiers, active summaries, apply/tick behavior callbacks, and food interest class queries into registries.
|
||||
- Moved explosion-disease base infection chance reads into `DiseaseRegistry` while preserving protein/niterop disease multipliers.
|
||||
- Moved serving-food nutrition and hunger-relief reads into `FoodRegistry`, including laxative's zero hunger relief.
|
||||
- Routed direct love/fight/sleep item eating through `EffectRegistry` while preserving the previous bite-specific stress, fear, and sleep-disease chance behavior.
|
||||
- Routed sleep drug, rain-water item, and water-bowl field visuals through `item_visual_registry.js`; added water-bowl icon-generation support.
|
||||
- Moved canonical sound gains, sample paths, categories, cooldowns, and ID category mapping out of `audio.js` into `sound_pack.js`.
|
||||
- Added named ant ecology extension hooks/comments without changing ant balance.
|
||||
- Bumped cache/version references to `15.20.16`; documented the new source-of-truth rules.
|
||||
|
||||
## 15.20.12
|
||||
|
||||
- Updated documentation after inspecting the current game surface: field choices, startup flow, tool groups, chart/family/debug surfaces, localStorage settings, and validation fallback when Node is unavailable.
|
||||
- Removed unused `css/styles.css` shim and no-op `js/ui_family.js` loader entry; refactored duplicate world initialization and allocation-heavy live-count paths.
|
||||
|
||||
- Removed the unwanted item-effect speech bubbles listed in `docs/prompt.txt`.
|
||||
- Reorganized the tool palette into color-coded collapsible groups: operation, food/water/grass, medicine/effects, habitat/living things, hazards/obstacles.
|
||||
- Changed the tool UI ball icon to a soccer-ball emoji preview.
|
||||
- Changed time phases: morning 05:00, day 10:00, evening 17:00, night 20:00.
|
||||
- Increased genkotsu knockback for Tarinai and ants; genkotsu now also blasts zunchi away.
|
||||
- Genkotsu damage now adds fear/stress and decreases Tarinai openness.
|
||||
- Rain weather overlay is now diagonal streaks. Fallen rain-water items, zunda juice, and mercury remain horizontal oval item sprites.
|
||||
- Tightened mobile operation UI visibility to smartphone-class compact touch devices.
|
||||
- Sleep-disease Tarinai can be moved by ant hauling without snapping back to their sleep anchor.
|
||||
|
||||
## 15.20.12 and earlier summary
|
||||
|
||||
- Consolidated documentation into dense English files; left `docs/prompt.txt` untouched.
|
||||
- Restored rain, revised water/zunda/mercury item sprites, added zunchi-slave head zunchi, fight win rate, fight defeat chance, and post-wake lock.
|
||||
- Earlier patches added parameter items, persistent item effects, WebP assets, sound categories, push notifications, ant systems, family tree UI, performance manager, event bus, manifest generation, CSS split, and large-file JS split.
|
||||
|
||||
## Debug patch: panic chain and action reasons
|
||||
|
||||
- Removed the friend-only panic chain that made only a best friend follow a panic target.
|
||||
- Added general nearby panic contagion: any nearby live tarinai in panic can cause another tarinai to panic. Neuroticism, distance, and current stress affect the probability.
|
||||
- Added / completed visible action reasons for:
|
||||
- eating all food and effect items
|
||||
- eating grass, ant corpses, and zunchi
|
||||
- drinking / touching water
|
||||
- resting near stone, bed, and nest box
|
||||
- sunbathing
|
||||
- zunchi-slave avoidance
|
||||
- being pinched / dragged
|
||||
- firecracker blast panic
|
||||
- explosion-disease blast panic
|
||||
- falling object / stone / genkotsu impact
|
||||
- high-speed ball collision
|
||||
- high-speed tarinai collision
|
||||
- intimidation, intimidation escape, fights, birth ritual, and thaw surprise
|
||||
- fence-blocked path give-up
|
||||
- Added state labels for `sunbath`, `ant_attack`, `ant_intimidate`, and `frozen`.
|
||||
- Expanded target labels for all current tool/item target types, including medicine/effect items, ant corpses, ant nests, firecrackers, pushpins, signboards, and fences.
|
||||
- Verification performed:
|
||||
- `node --check` over every `js/*.js`
|
||||
- state-label coverage scan: no uncovered tarinai states
|
||||
- old friend-panic chain scan: zero matches
|
||||
|
||||
|
||||
## おしり鋲・冷凍操作変更パッチ
|
||||
- ずんちどれい化条件の喧嘩勝率を30%以下に変更。
|
||||
- 衰弱死因判定を厳格化。
|
||||
- 冷凍手段をクリック式から「つまむで冷凍UIへ運ぶ」方式へ変更。
|
||||
- 新アイテム「おしり鋲」を追加。刺さるとずんちを溜め、日光浴不可。抜くと溜まったずんちが高速で飛散。
|
||||
- 高速ずんち衝突時の吹き飛び・パニック理由文を追加。
|
||||
|
||||
## 15.22.0 統合リファクタ
|
||||
|
||||
- `snapshot_system.js` を追加し、セーブ/ロードと冷凍で重複していた `clonePlain` / `copyOwnData` / `applyOwnData` / 参照復元 / スナップショット作成・復元を共通化。
|
||||
- `tarinai_action_state.js` を追加し、`setActionState()` / `enterPanic()` / `beginEating()` により、状態変更・パニック化・食事開始の理由文設定を統合。
|
||||
- 直接 `state = "panic"` していた処理を `enterPanic()` 経由へ変更。爆竹、爆発病、高速ボール、高速衝突、アリ、画鋲、近傍パニック連鎖、関係者死亡、解凍時の訃報などが対象。
|
||||
- 直接 `state = "eat"` していた食事処理を `beginEating()` 経由へ変更。
|
||||
- `physics_helpers.js` を追加し、壁反射・摩擦・回転を `TarinaiPhysics.applyKinematicItemMotion()` に共通化。おしり鋲/画鋲/高速ずんちの移動処理に適用。
|
||||
- `ITEM_TRAITS` と `PIN_BEHAVIORS` を追加し、アイテム分類と画鋲/おしり鋲の差分挙動をテーブル化。
|
||||
- `text_catalog.js` に状態ラベル、対象ラベル、理由文補完を集約。`ui_selected.js` はカタログ参照へ変更。
|
||||
- `family_graph.js` を追加し、家系グラフの連結成分探索を共通化。同期家系データと描画側に適用。
|
||||
- `relation_event_system.js` を追加し、関係者死亡の即時反応と冷凍個体への遅延通知を共通化。
|
||||
- `ui_helpers.js` を追加し、HTMLエスケープ等のUI共通処理を切り出し。
|
||||
- 構文チェック: 全 `js/*.js` OK。
|
||||
- 重複確認: `clonePlain` / `copyOwnData` / `applyOwnData` は `snapshot_system.js` に集約、直接 `state = "panic"` と直接 `state = "eat"` は除去済み。
|
||||
|
||||
## 15.23.0 safe duplicate integration pass
|
||||
|
||||
- `registry_base.js` を追加し、`food_registry.js` / `effect_registry.js` / `disease_registry.js` の `get` / `has` / `label` / `color` / `debugSnapshot` 系の共通基盤を統合。
|
||||
- `patch_helpers.js` を追加し、後付けの `World.prototype` パッチを `TarinaiPatcher.patchWorld()` 経由に統合。冷凍・関係者死亡通知で利用。
|
||||
- `data.js` に `sleepFurniture` / `draggable` trait と、`isPinType()` / `isSleepFurnitureType()` / `lodgedPinFor()` / `hasLodgedPinEffect()` / `normalizedToolSizeFor()` / `toolSizeScaleFor()` を追加。
|
||||
- `ui_tools.js` と `world_view.js` のツールサイズ算出を `normalizedToolSizeFor()` / `toolSizeScaleFor()` に統合。
|
||||
- `tarinai_action_state.js` に `currentLodgedPin()` / `currentLodgedPinBehavior()` / `hasLodgedPinEffect()` / `goIdle()` / `startSleeping()` / `seekTarget()` を追加。
|
||||
- `blockedByOshibyo` 系の重複判定を `hasLodgedPinEffect("blocksZunchi")` に統合。
|
||||
- 画鋲/おしり鋲の型判定を `isPinType()` に寄せ、描画・配置・つまみ・削除判定の分岐を整理。
|
||||
- 睡眠家具判定を `isSleepFurnitureType()` に寄せ、ベッド/巣箱の直書き判定を削減。
|
||||
- 手動アリ近傍探索の一部を `world.nearbyAnts()` に寄せた。
|
||||
- `pruneExtinctFamilies()` を `TarinaiFamilyGraph.connectedComponents()` に寄せた。`family_graph.js` 側のデフォルト edge も親子参照を使うようにした。
|
||||
- `save_system.js` / `freeze_system.js` / `ui_selected.js` のHTMLエスケープを `TarinaiUIHelpers.htmlEscape()` に統合。
|
||||
- `assets.js` のスプライト/汚れオーバーレイ用キャンバス生成を `TarinaiPhysics.createCachedCanvas()` に寄せた。
|
||||
- 危険度の高い `render.js` 全体再設計、`Tarinai.update()` の状態機械化、`ants.js` の状態機械化、`items.js` 描画全体のレジストリ化は未実施。
|
||||
- 確認: 全 `js/*.js` の `node --check` 通過。
|
||||
|
||||
### 15.23.0 追確認
|
||||
|
||||
- `service-worker.js` / `app_manifest.json` のスクリプト一覧を `index.html` の実ロード順に同期。新規共通化ファイルもキャッシュ対象に追加。
|
||||
- アリ運搬中に寝ているたりないの状態変更も `startSleeping()` 経由へ寄せた。
|
||||
- 最終確認: 全 `js/*.js` と `service-worker.js` の `node --check` 通過。
|
||||
- 最終確認: `blockedByOshibyo`、`type === "oshibyo"`、`type === "pushpin"` の直接判定は 0件。`clonePlain` / `copyOwnData` / `applyOwnData` / `isPlainObject` は `snapshot_system.js` のみ。
|
||||
|
||||
## 15.24.0 safe integration / UI escape recursion fix
|
||||
|
||||
- Fixed `Maximum call stack size exceeded` caused by `ui_helpers.htmlEscape()` calling the global `escapeHtml()` while `ui_selected.escapeHtml()` called back into `TarinaiUIHelpers.htmlEscape()`.
|
||||
- Made `TarinaiUIHelpers.htmlEscape()` a pure escaping function and loaded `ui_helpers.js` before `ui_selected.js`.
|
||||
- Added a guard in `ui_selected.escapeHtml()` so it never delegates to itself.
|
||||
- Added a data URL favicon link to stop the browser's default `/favicon.ico` 404 request.
|
||||
- Bumped static asset version to `15.24.0` in `app_manifest.json`, `index.html`, `service-worker.js`, and `js/version.js` to avoid stale cached scripts.
|
||||
- Reduced target-label duplication: item labels now come from `TOOL_DEFINITIONS` / `toolLabel()`, with `text_catalog.js` holding only non-tool overrides.
|
||||
- Removed the remaining synchronous family-tree DFS fallback blocks in `ui_family_data.js` and `ui_family_render.js`; both now use `TarinaiFamilyGraph.connectedComponents()`.
|
||||
- Simplified the remaining manual ant proximity fallback in `tarinai_needs_items.js` to `world.nearbyAnts()`.
|
||||
- Verification: all `js/*.js` and `service-worker.js` pass `node --check`.
|
||||
87
README.md
|
|
@ -1,51 +1,60 @@
|
|||
# Tarinai Colony Observation Game
|
||||
# たりない観察 v39.18.6
|
||||
|
||||
Buildless single-page browser simulation. Open `index.html`; no package manager, transpiler, server API, or module loader is required. Runtime order is explicit `<script defer>` order generated from `app_manifest.json`. Current inspected version: `15.20.16`.
|
||||
## 実績システム
|
||||
|
||||
## Edit contract
|
||||
実績定義は `js/achievement_catalog.js` を唯一のカタログとして管理します。表示名、説明、カテゴリ、未解除時の公開設定、同期可能な進捗条件、累積上限は、クライアントと `achievement_api.php` が同じカタログから読み取ります。
|
||||
|
||||
- Preserve Japanese in-game UI tone and strings.
|
||||
- Do not edit `docs/prompt.txt`; it is external user material.
|
||||
- Use `app_manifest.json` as the source of truth for CSS/JS load order and service-worker cache entries.
|
||||
- Run `python3 scripts/generate_app_files.py` after version or asset-list changes.
|
||||
- Run `node --check js/*.js service-worker.js` before zipping.
|
||||
- Verify ZIP integrity with `zip -T`.
|
||||
累積進捗の正規データは端末・タブ別の増加台帳です。表示用 `progress` に同じ累積値を重複保存しません。旧形式の重複カウンターと `synced` 一覧は読込時だけ移行し、現行形式では保存しません。
|
||||
|
||||
## Documentation map
|
||||
「敵の敵は味方」は実績画面の「生態」カテゴリに掲載されます。
|
||||
|
||||
- `docs/ARCHITECTURE.md`: runtime architecture, file ownership, system boundaries.
|
||||
- `docs/CONTENT_GUIDE.md`: item visuals, UI/field visual parity, audio, bubble text inventory.
|
||||
- `docs/MAINTENANCE.md`: release process, validation, debugging, cache/version rules.
|
||||
- `docs/prompt.txt`: untouched user prompt/context archive.
|
||||
## ローカル保存
|
||||
|
||||
## Player-facing surface
|
||||
実績状態とサーバー資格情報は、通常保存、予備保存、同一タブ保存、IndexedDBへ世代付きで保存します。起動時は同じ世代の状態を追加方向に統合し、明示的な実績リセット時だけ世代を進めます。
|
||||
|
||||
- Startup uses a loading overlay that eagerly preloads a small sprite set, initializes UI/world state, then defers remaining image work.
|
||||
- Field choices are cage, garden, and park. `FIELD_TYPES` controls world scale, starting population, initial grass count, and responsive field height.
|
||||
- The default field is garden. Reset opens the field dialog; cancel keeps the current field.
|
||||
- Tools are grouped in collapsible categories: operation/observation, food/water/grass, medicine/effects, habitat/living things, hazards/obstacles.
|
||||
- The top bar owns pause, speed, ecology, sound, credits, and reset controls. Event push toasts live inside the Event card, while sound category toggles live only in the sound menu.
|
||||
- The colony panel charts population, mental state, environment, and object counts. The family tree is a separate main section with manual/auto refresh and reset controls.
|
||||
- Mobile operation modes are available only on compact touch-first phone viewports: auto, camera, tool, family.
|
||||
v39.18.6は、v39.18.0以前の実績保存、旧ワールド実績メタデータ、実績スペルv9・v10、旧バイナリセーブを読み込めます。旧データは現行形式へ正規化され、新規保存には廃止済みフィールドを書きません。
|
||||
|
||||
## Core systems
|
||||
## サーバー同期
|
||||
|
||||
- `World`: `js/world.js` plus `js/world_*.js`; time, weather, placement, effects, family records, spatial queries, ants, combat, logging.
|
||||
- `Tarinai`: `js/tarinai.js` plus `js/tarinai_*.js`; identity, personality, needs, items, disease, movement, social behavior, rendering.
|
||||
- `Item`: `js/items.js`; lifecycle, scaling, field rendering. Field rendering is the visual source of truth for tool icons.
|
||||
- `ItemVisualRegistry`: `js/item_visual_registry.js`; canonical item visuals used by field sprites and generated icon previews.
|
||||
- `EffectRegistry`: `js/effect_registry.js`; item-effect names, modifiers, exclusivity, display metadata, random-effect candidates.
|
||||
- `DiseaseRegistry`: `js/disease_registry.js`; disease flags, labels, display metadata, cure-rule notes, mystery-drug eligibility.
|
||||
- `FoodRegistry`: `js/food_registry.js`; serving classes, labels, decay, hygiene penalty, special effect IDs.
|
||||
- `InputModeManager`: `js/input_mode_manager.js`; mobile/touch classification, gesture capability policy, mode persistence.
|
||||
- `WeatherSystem`: `js/weather_system.js`; weather state transitions and rain-spawned water items.
|
||||
- `Audio`: `js/sound_pack.js` owns sound metadata; `js/audio.js` loads samples, applies settings, enforces cooldowns, and runs synthetic fallbacks.
|
||||
- `Performance`: `js/performance_manager.js`; FPS quality level, ant stride, effect budget, terrain redraw cadence.
|
||||
同期プロトコルはv5です。
|
||||
|
||||
## Current high-risk areas
|
||||
- 解除状態と途中進捗を追加型で統合
|
||||
- 同じ端末台帳の再送を重複加算しない
|
||||
- リセット要求の再送を無害化
|
||||
- 古いサーバーバックアップから新しいローカル世代を復旧
|
||||
- 古い端末からリセット前の状態を復活させない
|
||||
- プレイヤーIDと所有秘密鍵を耐久保存から復旧
|
||||
|
||||
- Item effects touch `effect_registry`, `tarinai_item_effects`, `tarinai_needs_items`, `items`, and selected-info UI.
|
||||
- Mobile input must remain hidden on desktop-class devices; `TarinaiInputMode.snapshot().touchFirst` controls body class `touch-mobile-ui`.
|
||||
- Weather rain overlay is diagonal line streaks. Fallen water items, zunda juice, and mercury share the same horizontal oval item-droplet silhouette; recolor only.
|
||||
- Tool UI icons must be generated previews of `js/items.js` drawing helpers, never separate designs.
|
||||
共有達成率は、サーバーがゲームシミュレーションを再実行した検証値ではなく、クライアント端末の報告値です。UIでは共有値を「達成率」「達成人数」と表示します。
|
||||
|
||||
## 旧サーバーデータの自動移行
|
||||
|
||||
データディレクトリ、またはアプリ直下の `.achievement_data` に旧コンパクト形式の `state.json`(スキーマ4)がある場合、最初のAPI要求時に現行形式へ自動移行します。環境変数で新保存先を指定した場合や、v39.18.1が空の現行保存先を先に作成済みの場合も検出します。
|
||||
|
||||
- 原本は `legacy-backup/` へSHA-256付きで保存
|
||||
- 旧プレイヤーIDから決定的なUUIDを生成
|
||||
- プレイヤー数、解除実績、解除時刻、実績別集計を移行
|
||||
- 旧データに存在しない途中進捗は0から開始
|
||||
- `legacy-v4-migration.json` に完了件数を記録
|
||||
- 中断時は次のAPI要求で冪等に再開
|
||||
- 切替中にコピー元の旧 `state.json` が更新された場合も、内容ハッシュを再確認して追加方向で再取込
|
||||
- ファイルサイズとmtimeが同じ更新もSHA-256比較で検出
|
||||
|
||||
旧ブラウザーに22文字のプレイヤーIDが残っている場合、クライアントはUUIDへ上書きする前に専用領域へ退避し、初回セッションで一度だけ新しい所有秘密鍵へ引き継ぎます。引継ぎ済みの旧IDを別の秘密鍵で再取得することはできません。
|
||||
|
||||
旧版から直接v39.18.6へ更新したブラウザーは自動引継ぎできます。v39.18.1を既に起動して旧22文字IDがブラウザーから失われている場合、サーバーデータだけから端末との本人対応を再構成することはできません。解除履歴と共有集計自体は正常に移行されます。
|
||||
|
||||
## 実績APIの保存先
|
||||
|
||||
本番環境では、PHP実行ユーザーが書き込める非公開ディレクトリを用意し、環境変数 `TARINAI_ACHIEVEMENT_DATA_DIR` に絶対パスを設定してください。未設定時は、HTTP公開ディレクトリ外の安定したユーザーデータ領域を使用します。
|
||||
|
||||
|
||||
## PHP組み込みサーバーでの起動
|
||||
|
||||
PHP組み込みサーバーは `.htaccess` を解釈しません。旧 `.achievement_data/state.json` には引継ぎ用IDが含まれるため、必ず同梱のルーターを使用してください。
|
||||
|
||||
```sh
|
||||
./start_server.sh 8000
|
||||
```
|
||||
|
||||
Windowsでは `start_server.bat 8000` を使用します。本番環境では引き続き `TARINAI_ACHIEVEMENT_DATA_DIR` をHTTP公開ディレクトリ外へ設定してください。
|
||||
|
|
|
|||
1227
achievement_api.php
Normal file
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "15.24.0",
|
||||
"version": "39.18.18",
|
||||
"css": [
|
||||
"css/base.css",
|
||||
"css/layout.css",
|
||||
|
|
@ -10,56 +10,166 @@
|
|||
"js": [
|
||||
"js/version.js",
|
||||
"js/event_bus.js",
|
||||
"js/performance_manager.js",
|
||||
"js/domain_ids.js",
|
||||
"js/registry_base.js",
|
||||
"js/disease_registry.js",
|
||||
"js/effect_registry.js",
|
||||
"js/sound_pack.js",
|
||||
"js/deterministic_helpers.js",
|
||||
"js/item_tool_metadata.js",
|
||||
"js/item_tool_definitions.js",
|
||||
"js/item_visual_definitions.js",
|
||||
"js/item_food_definitions.js",
|
||||
"js/item_effect_definitions.js",
|
||||
"js/item_registry.js",
|
||||
"js/data.js",
|
||||
"js/item_visual_registry.js",
|
||||
"js/food_registry.js",
|
||||
"js/ground_types.js",
|
||||
"js/input_mode_manager.js",
|
||||
"js/math.js",
|
||||
"js/geometry_helpers.js",
|
||||
"js/collision_footprint_system.js",
|
||||
"js/physics_helpers.js",
|
||||
"js/placement_preview_system.js",
|
||||
"js/pin_attachment_system.js",
|
||||
"js/physics_shape_editor_system.js",
|
||||
"js/mechanical_system.js",
|
||||
"js/mechanical_shape_bridge.js",
|
||||
"js/constraint_system.js",
|
||||
"js/physics_world_system.js",
|
||||
"js/physics_projection_system.js",
|
||||
"js/circuit_board_system.js",
|
||||
"js/signal_system.js",
|
||||
"js/assets.js",
|
||||
"js/audio.js",
|
||||
"js/perf_profiler.js",
|
||||
"js/display_helpers.js",
|
||||
"js/render.js",
|
||||
"js/sim_core.js",
|
||||
"js/tarinai_seed_factory.js",
|
||||
"js/structures.js",
|
||||
"js/item_type_catalog.js",
|
||||
"js/items.js",
|
||||
"js/item_type_initializers.js",
|
||||
"js/item_lifecycle_support.js",
|
||||
"js/item_update_policy.js",
|
||||
"js/fire_runtime_system.js",
|
||||
"js/robot_cleaner_system.js",
|
||||
"js/item_dynamic_tool_system.js",
|
||||
"js/item_dynamic_ball_system.js",
|
||||
"js/item_dynamic_duplicator_system.js",
|
||||
"js/item_dynamic_pin_system.js",
|
||||
"js/item_dynamic_zunchi_system.js",
|
||||
"js/item_environment_hazard_system.js",
|
||||
"js/item_dynamic_system.js",
|
||||
"js/item_lifecycle_decay_system.js",
|
||||
"js/item_lifecycle_growth_system.js",
|
||||
"js/item_lifecycle_step_frame.js",
|
||||
"js/item_lifecycle_step_decay.js",
|
||||
"js/item_lifecycle_step_dynamic.js",
|
||||
"js/item_lifecycle_step_growth.js",
|
||||
"js/update_step_pipeline_runner.js",
|
||||
"js/item_lifecycle_pipeline.js",
|
||||
"js/item_runtime.js",
|
||||
"js/structure_lifecycle.js",
|
||||
"js/memorial_bell_system.js",
|
||||
"js/item_render_helpers.js",
|
||||
"js/item_render_runtime.js",
|
||||
"js/burn_motion_util.js",
|
||||
"js/ants.js",
|
||||
"js/health.js",
|
||||
"js/tarinai.js",
|
||||
"js/tarinai_action_spec.js",
|
||||
"js/tarinai_behavior_state.js",
|
||||
"js/tarinai_identity_social.js",
|
||||
"js/tarinai_action_state.js",
|
||||
"js/tarinai_disease_nest.js",
|
||||
"js/tarinai_item_effects.js",
|
||||
"js/tarinai_needs_core.js",
|
||||
"js/tarinai_behavior_text.js",
|
||||
"js/tarinai_forced_behavior.js",
|
||||
"js/tarinai_nest_sleep_system.js",
|
||||
"js/tarinai_item_targeting.js",
|
||||
"js/tarinai_consumable_behavior.js",
|
||||
"js/tarinai_social_action_runtime.js",
|
||||
"js/tarinai_building_behavior.js",
|
||||
"js/seesaw_system.js",
|
||||
"js/tarinai_action_definitions.js",
|
||||
"js/tarinai_needs_items.js",
|
||||
"js/tarinai_food_prototype_mixin.js",
|
||||
"js/tarinai_direct_feeding_system.js",
|
||||
"js/tarinai_need_planner_system.js",
|
||||
"js/tarinai_item_interaction_context.js",
|
||||
"js/tarinai_food_interaction_system.js",
|
||||
"js/tarinai_contact_item_system.js",
|
||||
"js/tarinai_item_interaction_system.js",
|
||||
"js/tarinai_sunbath_system.js",
|
||||
"js/tarinai_cursor_care_system.js",
|
||||
"js/tarinai_local_environment_system.js",
|
||||
"js/tarinai_social_move_life.js",
|
||||
"js/tarinai_update_step_frame.js",
|
||||
"js/tarinai_update_step_ai.js",
|
||||
"js/tarinai_update_step_environment.js",
|
||||
"js/tarinai_update_step_movement.js",
|
||||
"js/tarinai_update_step_health.js",
|
||||
"js/tarinai_update_pipeline.js",
|
||||
"js/tarinai_runtime.js",
|
||||
"js/tarinai_render.js",
|
||||
"js/world.js",
|
||||
"js/world_view.js",
|
||||
"js/family_graph.js",
|
||||
"js/world_reset_presets.js",
|
||||
"js/world_family_social.js",
|
||||
"js/impact_core_system.js",
|
||||
"js/impact_response_system.js",
|
||||
"js/collision_response_system.js",
|
||||
"js/world_combat_effects.js",
|
||||
"js/world_environment.js",
|
||||
"js/world_temperature_system.js",
|
||||
"js/world_pathfinding_system.js",
|
||||
"js/world_grass_placement_system.js",
|
||||
"js/world_spatial_budget.js",
|
||||
"js/world_ants_system.js",
|
||||
"js/weather_system.js",
|
||||
"js/item_update_scheduler.js",
|
||||
"js/simulation_runtime_helpers.js",
|
||||
"js/tarinai_update_policy.js",
|
||||
"js/simulation_environment_system.js",
|
||||
"js/simulation_item_ant_system.js",
|
||||
"js/simulation_effects_system.js",
|
||||
"js/simulation_creature_system.js",
|
||||
"js/simulation_maintenance_system.js",
|
||||
"js/simulation_ambient_system.js",
|
||||
"js/simulation_systems.js",
|
||||
"js/system_order.js",
|
||||
"js/simulation.js",
|
||||
"js/colony_situation_system.js",
|
||||
"js/world_update.js",
|
||||
"js/world_tool_actions.js",
|
||||
"js/world_placement_log.js",
|
||||
"js/world_bootstrap.js",
|
||||
"js/world_event_effects.js",
|
||||
"js/command_dispatcher.js",
|
||||
"js/text_catalog.js",
|
||||
"js/ui.js",
|
||||
"js/ui_log.js",
|
||||
"js/game_dialogs.js",
|
||||
"js/achievement_catalog.js",
|
||||
"js/achievements.js",
|
||||
"js/ui_helpers.js",
|
||||
"js/ui_log.js",
|
||||
"js/ui_selected.js",
|
||||
"js/save_schema.js",
|
||||
"js/snapshot_system.js",
|
||||
"js/restore_coordinator.js",
|
||||
"js/history_system.js",
|
||||
"js/save_codec.js",
|
||||
"js/save_storage.js",
|
||||
"js/save_system.js",
|
||||
"js/patch_helpers.js",
|
||||
"js/freeze_system.js",
|
||||
"js/relation_event_system.js",
|
||||
"js/ui_tooltips.js",
|
||||
"js/ui_layout_dialogs.js",
|
||||
"js/ui_ground.js",
|
||||
"js/ui_tools.js",
|
||||
"js/ui_input_shared.js",
|
||||
"js/ui_pointer_action_system.js",
|
||||
"js/ui_input_touch.js",
|
||||
"js/ui_input_mouse.js",
|
||||
"js/ui_bind.js",
|
||||
"js/ui_charts.js",
|
||||
"js/ui_family_data.js",
|
||||
|
|
@ -69,13 +179,5 @@
|
|||
"js/ui_family_render.js",
|
||||
"js/main.js",
|
||||
"js/debug_tools.js"
|
||||
],
|
||||
"assetGlobs": [
|
||||
"assets/ui/*.webp",
|
||||
"assets/objects/*.webp",
|
||||
"assets/sprites/*.webp",
|
||||
"assets/sounds/*.wav",
|
||||
"assets/sounds/*.mp3"
|
||||
],
|
||||
"notes": "Source of truth for static loading order; generated files are index.html script/link tags and service-worker.js cache list."
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
BIN
assets/objects/mamekusaredake.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
assets/objects/plushie_bear.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
assets/sounds/achievement_unlock.mp3
Normal file
BIN
assets/sounds/acquired_stress_001.wav
Normal file
BIN
assets/sounds/acquired_stress_002.wav
Normal file
BIN
assets/sounds/acquired_stress_003.wav
Normal file
BIN
assets/sounds/acquired_stress_004.wav
Normal file
BIN
assets/sounds/acquired_stress_005.wav
Normal file
BIN
assets/sounds/acquired_stress_006.wav
Normal file
BIN
assets/sounds/acquired_stress_007.wav
Normal file
BIN
assets/sounds/acquired_stress_008.wav
Normal file
BIN
assets/sounds/acquired_stress_009.wav
Normal file
BIN
assets/sounds/bicycle_bell.mp3
Normal file
BIN
assets/sounds/intimidate_alt_001.wav
Normal file
BIN
assets/sounds/major_damage_01.wav
Normal file
BIN
assets/sounds/major_damage_02.wav
Normal file
BIN
assets/sounds/major_damage_03.wav
Normal file
BIN
assets/sounds/major_damage_04.wav
Normal file
BIN
assets/sounds/ochibi_calm_001.wav
Normal file
BIN
assets/sounds/ochibi_calm_002.wav
Normal file
BIN
assets/sounds/ochibi_calm_003.wav
Normal file
BIN
assets/sounds/ochibi_calm_004.wav
Normal file
BIN
assets/sounds/ochibi_calm_005.wav
Normal file
BIN
assets/sounds/ochibi_calm_006.wav
Normal file
BIN
assets/sounds/ochibi_calm_007.wav
Normal file
BIN
assets/sounds/ochibi_calm_008.wav
Normal file
BIN
assets/sounds/ochibi_calm_009.wav
Normal file
BIN
assets/sounds/ochibi_damage_001.wav
Normal file
BIN
assets/sounds/ochibi_damage_002.wav
Normal file
BIN
assets/sounds/ochibi_damage_003.wav
Normal file
BIN
assets/sounds/ochibi_dirty_001.wav
Normal file
BIN
assets/sounds/ochibi_dirty_002.wav
Normal file
BIN
assets/sounds/ochibi_dirty_003.wav
Normal file
BIN
assets/sounds/ochibi_dirty_004.wav
Normal file
BIN
assets/sounds/ochibi_disease_001.wav
Normal file
BIN
assets/sounds/ochibi_electric_006.wav
Normal file
BIN
assets/sounds/ochibi_fight_hit_004.wav
Normal file
BIN
assets/sounds/ochibi_hot_007.wav
Normal file
BIN
assets/sounds/ochibi_panic_005.wav
Normal file
BIN
assets/sounds/ochibi_sleep_002.wav
Normal file
BIN
assets/sounds/ochibi_sleep_003.wav
Normal file
BIN
assets/sounds/ochibi_stress_008.wav
Normal file
BIN
assets/sounds/shoot_bolt_action.mp3
Normal file
BIN
assets/sounds/shoot_pistol.mp3
Normal file
BIN
assets/sounds/voice_007_sunbath_pokapoka.wav
Normal file
BIN
assets/sounds/voice_008_temperature_buruburu.wav
Normal file
BIN
assets/sounds/voice_009_temperature_achui.wav
Normal file
BIN
assets/sprites/tarinai_34_hot_01.webp
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
assets/sprites/tarinai_35_hot_02.webp
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
assets/sprites/tarinai_36_hot_03.webp
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
assets/sprites/tarinai_37_cold_01.webp
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
assets/sprites/tarinai_38_cold_02.webp
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
assets/sprites/tarinai_39_cold_03.webp
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
assets/sprites/tarinai_40_intimidate_alt.webp
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
assets/sprites/tarinai_acquired_overlay.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
assets/ui/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
assets/ui/favicon.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/ui/pwa-icon-192.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/ui/pwa-icon-512.png
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
assets/ui/tool_acquired_tarinai.webp
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
assets/ui/tool_duplicator.webp
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/ui/tool_mamekusaredake.webp
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
assets/ui/tool_oshibyo.webp
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/ui/tool_shoot.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/ui/tool_signboard.webp
Normal file
|
After Width: | Height: | Size: 968 B |
|
Before Width: | Height: | Size: 278 B |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
862
css/base.css
|
|
@ -36,23 +36,6 @@ button {
|
|||
color: inherit;
|
||||
}
|
||||
|
||||
.loading-screen {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 9999;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 24px;
|
||||
background:
|
||||
radial-gradient(circle at 50% 30%, rgba(164, 225, 104, 0.22), transparent 32%),
|
||||
linear-gradient(180deg, rgba(239, 248, 255, 0.98), rgba(247, 245, 236, 0.98));
|
||||
transition: opacity 180ms ease, visibility 180ms ease;
|
||||
}
|
||||
.loading-screen.hidden {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
.loading-panel {
|
||||
width: min(420px, 92vw);
|
||||
padding: 24px 22px;
|
||||
|
|
@ -62,37 +45,6 @@ button {
|
|||
box-shadow: 0 18px 48px rgba(64, 48, 32, 0.16);
|
||||
text-align: center;
|
||||
}
|
||||
.loading-title {
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
letter-spacing: 0.04em;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.loading-text {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
line-height: 1.55;
|
||||
min-height: 1.55em;
|
||||
}
|
||||
.loading-bar {
|
||||
height: 12px;
|
||||
margin: 18px 0 8px;
|
||||
border-radius: 999px;
|
||||
overflow: hidden;
|
||||
background: rgba(90, 70, 48, 0.12);
|
||||
}
|
||||
.loading-bar-fill {
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(90deg, #9ce84b, #f0d35b);
|
||||
transition: width 160ms ease;
|
||||
}
|
||||
.loading-percent {
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
#app {
|
||||
width: min(1480px, 100vw);
|
||||
|
|
@ -104,15 +56,17 @@ button {
|
|||
.topbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 18px;
|
||||
gap: 14px;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.top-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
gap: 10px;
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
|
|
@ -124,10 +78,19 @@ button {
|
|||
min-width: 0;
|
||||
}
|
||||
|
||||
.room-links {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 6px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.room-link {
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
background: rgba(255,255,255,0.86);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
|
@ -137,26 +100,26 @@ h1 {
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: clamp(11px, 1.1vw, 13px);
|
||||
line-height: 1.35;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.speed-select-native { display: none; }
|
||||
.speed-menu { position: relative; display: inline-block; }
|
||||
.speed-menu-btn { min-width: 92px; text-align: center; white-space: nowrap; }
|
||||
|
||||
.top-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
flex: 0 0 auto;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn, .tool {
|
||||
border: 1px solid var(--line);
|
||||
background: rgba(255,255,255,0.76);
|
||||
border-radius: 999px;
|
||||
padding: 9px 13px;
|
||||
padding: 8px 11px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 14px rgba(80,60,40,0.06);
|
||||
transition: transform 130ms ease, background 130ms ease, border-color 130ms ease;
|
||||
|
|
@ -216,7 +179,7 @@ h1 {
|
|||
width: min(440px, 100%);
|
||||
max-height: calc(100vh - 36px);
|
||||
overflow: auto;
|
||||
border-radius: 16px;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
background: rgba(255, 252, 245, 0.96);
|
||||
border: 1px solid rgba(84,68,48,0.16);
|
||||
|
|
@ -240,9 +203,30 @@ h1 {
|
|||
color: var(--ink);
|
||||
}
|
||||
.field-choice:hover { border-color: rgba(112,178,66,0.45); }
|
||||
.field-choice.active,
|
||||
.field-choice[aria-pressed="true"] {
|
||||
border-color: rgba(112,178,66,0.72);
|
||||
box-shadow: 0 0 0 2px rgba(112,178,66,0.18), inset 0 0 0 1px rgba(255,255,255,0.64);
|
||||
background: linear-gradient(180deg, rgba(248,255,240,0.96), rgba(231,246,210,0.82));
|
||||
}
|
||||
.field-choice strong, .field-choice span { display: block; }
|
||||
.field-choice strong { font-size: 15px; }
|
||||
.field-choice span { margin-top: 4px; color: var(--muted); font-size: 12px; }
|
||||
.field-dialog-lead {
|
||||
margin: -4px 0 12px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.field-dialog-section-title {
|
||||
margin: 12px 0 7px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: rgba(75,62,43,0.78);
|
||||
}
|
||||
.reset-dialog-panel { width: min(760px, 100%); }
|
||||
.reset-preset-choice-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
|
||||
.reset-preset-choice-grid .field-choice { min-height: 66px; padding: 7px 5px; }
|
||||
.field-dialog-actions {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
|
|
@ -260,7 +244,6 @@ h1 {
|
|||
min-width: 132px;
|
||||
}
|
||||
|
||||
|
||||
.ecology-dialog-panel {
|
||||
width: min(760px, 100%);
|
||||
max-height: calc(100vh - 36px);
|
||||
|
|
@ -309,7 +292,7 @@ h1 {
|
|||
color: white;
|
||||
padding: 10px 16px;
|
||||
border-radius: 999px;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
pointer-events: none;
|
||||
opacity: 1;
|
||||
transition: opacity 200ms ease, transform 200ms ease;
|
||||
|
|
@ -346,75 +329,728 @@ h1 {
|
|||
|
||||
.tool-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0,1fr));
|
||||
gap: 8px;
|
||||
grid-template-columns: repeat(3, minmax(0,1fr));
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tool {
|
||||
border-radius: 14px;
|
||||
display: flex;
|
||||
--tool-main-icon-size: 28px;
|
||||
--tool-icon-zoom: 1.05;
|
||||
--tool-watermark-size: 0px;
|
||||
border-radius: 12px;
|
||||
display: grid;
|
||||
grid-template-columns: 31px minmax(0,1fr);
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
justify-content: flex-start;
|
||||
padding: 8px 8px 8px 42px;
|
||||
min-height: 46px;
|
||||
justify-items: stretch;
|
||||
gap: 2px;
|
||||
padding: 3px 3px 3px 2px;
|
||||
min-height: 47px;
|
||||
font-size: 11.4px;
|
||||
line-height: 1.06;
|
||||
letter-spacing: -0.015em;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 7px center, center;
|
||||
background-size: 30px 30px, cover;
|
||||
background-position: left 3px center, center;
|
||||
background-size: var(--tool-main-icon-size) var(--tool-main-icon-size), cover;
|
||||
}
|
||||
.tool::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 7px;
|
||||
left: 3px;
|
||||
top: 50%;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
transform: translateY(-50%);
|
||||
width: var(--tool-main-icon-size);
|
||||
height: var(--tool-main-icon-size);
|
||||
transform: translateY(-50%) scale(var(--tool-icon-zoom, 1));
|
||||
transform-origin: center;
|
||||
background: var(--tool-icon) center / contain no-repeat;
|
||||
opacity: 0.92;
|
||||
opacity: 0.98;
|
||||
z-index: 1;
|
||||
filter: drop-shadow(0 2px 3px rgba(52,40,26,0.12));
|
||||
}
|
||||
.tool::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: var(--tool-icon) right -8px center / 54px 54px no-repeat;
|
||||
opacity: 0.10;
|
||||
background: none;
|
||||
display: none;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
.tool-label {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: block;
|
||||
grid-column: 2;
|
||||
align-self: center;
|
||||
justify-self: start;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
font-size: 11.4px;
|
||||
line-height: 1.08;
|
||||
font-weight: 700;
|
||||
max-width: 5.35em;
|
||||
word-break: normal;
|
||||
overflow-wrap: anywhere;
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,0.62);
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.tool.tool-has-canvas-icon { --tool-icon: none; grid-template-columns: 31px minmax(0,1fr); min-height: 47px; padding-top: 4px; }
|
||||
|
||||
.tool.tool-has-canvas-icon::before,
|
||||
.tool.tool-has-canvas-icon::after { background: none !important; }
|
||||
.tool-sprite-preview {
|
||||
position: absolute;
|
||||
left: 1px;
|
||||
top: 50%;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
transform: translateY(-50%);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
filter: drop-shadow(0 2px 3px rgba(52,40,26,0.12));
|
||||
}
|
||||
.tool[data-tool="giant_drug"] .tool-sprite-preview { width: 39px; height: 39px; left: -4px; }
|
||||
.tool[data-tool="dwarf_drug"] .tool-sprite-preview,
|
||||
.tool[data-tool="oshibyo"] .tool-sprite-preview { width: 22px; height: 22px; left: 5px; }
|
||||
.tool.tool-has-physics-icon .tool-physics-preview { width: 31px; height: 31px; left: 0; filter: drop-shadow(0 1px 2px rgba(52,40,26,0.16)); }
|
||||
.tool[data-tool="poke"], .tool[data-tool="water_hose"] { --tool-icon-zoom: 0.92; }
|
||||
|
||||
.signboard-editor {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 42;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 18px;
|
||||
background: rgba(34, 29, 22, 0.34);
|
||||
backdrop-filter: blur(3px);
|
||||
}
|
||||
.signboard-editor.hidden { display: none; }
|
||||
.signboard-editor-panel {
|
||||
width: min(220px, calc(100vw - 28px));
|
||||
border-radius: 12px;
|
||||
padding: 14px;
|
||||
background: linear-gradient(180deg, rgba(255,253,247,0.98), rgba(245,237,221,0.96));
|
||||
border: 1px solid rgba(94, 70, 42, 0.20);
|
||||
box-shadow: 0 22px 60px rgba(36, 28, 20, 0.28), inset 0 1px 0 rgba(255,255,255,0.72);
|
||||
max-height: calc(100dvh - 36px);
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
.signboard-editor-titlebar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
margin: 0 0 10px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid rgba(94, 70, 42, 0.13);
|
||||
}
|
||||
.signboard-editor-titlebar h2 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
letter-spacing: .03em;
|
||||
}
|
||||
.signboard-editor-close {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(84,68,48,0.16);
|
||||
background: rgba(255,255,255,0.72);
|
||||
color: var(--ink);
|
||||
font-size: 19px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
.signboard-editor-close:hover { background: rgba(255,248,232,0.96); }
|
||||
#signboardEditorText {
|
||||
width: 7.4em;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
resize: none;
|
||||
min-height: 88px;
|
||||
max-height: 88px;
|
||||
overflow: hidden;
|
||||
white-space: pre;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(84,68,48,0.18);
|
||||
background: rgba(255,255,255,0.84);
|
||||
padding: 10px 11px;
|
||||
color: var(--ink);
|
||||
font: 15px/1.55 ui-rounded, "Hiragino Maru Gothic ProN", "Yu Gothic", sans-serif;
|
||||
letter-spacing: 0.03em;
|
||||
outline: none;
|
||||
}
|
||||
#signboardEditorText:focus {
|
||||
border-color: rgba(112,178,66,0.54);
|
||||
box-shadow: 0 0 0 3px rgba(112,178,66,0.14);
|
||||
}
|
||||
.signboard-editor-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2px 8px;
|
||||
justify-content: space-between;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
margin: 6px 2px 0;
|
||||
}
|
||||
.signboard-editor-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.robot-cleaner-editor-panel { width: min(300px, calc(100vw - 28px)); }
|
||||
.robot-cleaner-editor-help {
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
margin: 0 2px 10px;
|
||||
}
|
||||
.robot-cleaner-targets {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin: 8px 0 2px;
|
||||
}
|
||||
.robot-cleaner-targets label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 10px;
|
||||
background: rgba(255,255,255,0.58);
|
||||
border: 1px solid rgba(84,68,48,0.11);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
.robot-cleaner-targets input { accent-color: #70b242; }
|
||||
|
||||
/* v16.07.08: right menu fixed quick tabs */
|
||||
.panel-quick-tabs {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 12;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
gap: 6px;
|
||||
padding: 8px 4px 9px;
|
||||
margin: -2px 0 2px;
|
||||
background: linear-gradient(180deg, rgba(244, 250, 247, 0.96), rgba(244, 250, 247, 0.78));
|
||||
border-bottom: 1px solid rgba(102, 169, 215, 0.34);
|
||||
box-shadow: 0 8px 16px rgba(79, 103, 121, 0.10);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
.panel-quick-tabs button {
|
||||
min-width: 0;
|
||||
border: 1px solid rgba(102, 169, 215, 0.34);
|
||||
border-radius: 999px;
|
||||
padding: 7px 5px;
|
||||
background: rgba(255,255,255,0.76);
|
||||
color: var(--ink);
|
||||
font-size: 11px;
|
||||
line-height: 1.1;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 3px 9px rgba(68, 92, 110, 0.08);
|
||||
}
|
||||
.panel-quick-tabs button:hover,
|
||||
.panel-quick-tabs button.active {
|
||||
background: #efffde;
|
||||
border-color: rgba(103,178,44,0.46);
|
||||
}
|
||||
|
||||
.rotator-editor {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 43;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 18px;
|
||||
background: rgba(34, 29, 22, 0.34);
|
||||
backdrop-filter: blur(3px);
|
||||
}
|
||||
.rotator-editor.hidden { display: none; }
|
||||
.rotator-editor-panel {
|
||||
width: min(620px, calc(100vw - 28px));
|
||||
border-radius: 14px;
|
||||
padding: 14px;
|
||||
background: linear-gradient(180deg, rgba(255,253,247,0.98), rgba(245,237,221,0.97));
|
||||
border: 1px solid rgba(94, 70, 42, 0.20);
|
||||
box-shadow: 0 22px 60px rgba(36, 28, 20, 0.28), inset 0 1px 0 rgba(255,255,255,0.72);
|
||||
max-height: calc(100dvh - 36px);
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
.rotator-editor-titlebar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid rgba(94, 70, 42, 0.13);
|
||||
}
|
||||
.rotator-editor-titlebar h2 { margin: 0; font-size: 16px; letter-spacing: .03em; }
|
||||
.rotator-editor-close {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(84,68,48,0.16);
|
||||
background: rgba(255,255,255,0.72);
|
||||
color: var(--ink);
|
||||
font-size: 19px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
.rotator-editor-close:hover { background: rgba(255,248,232,0.96); }
|
||||
.rotator-editor-controls {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.rotator-editor-controls label {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr 72px auto;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
color: var(--ink);
|
||||
}
|
||||
.rotator-editor-controls input[type="number"] {
|
||||
width: 72px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(84,68,48,0.18);
|
||||
background: rgba(255,255,255,0.84);
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
|
||||
.pressure-numeric-control[hidden] { display: none; }
|
||||
.pressure-numeric-control {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
padding: 10px 10px 8px;
|
||||
border-radius: 11px;
|
||||
border: 1px solid rgba(84,68,48,0.13);
|
||||
background: rgba(255,255,255,0.58);
|
||||
}
|
||||
.pressure-value-slider {
|
||||
width: calc(100% - 14px);
|
||||
height: 34px;
|
||||
margin: 0 7px;
|
||||
padding: 0;
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
background: transparent;
|
||||
}
|
||||
.pressure-value-slider::-webkit-slider-runnable-track {
|
||||
height: 6px;
|
||||
border-radius: 999px;
|
||||
background: rgba(67,158,218,0.78);
|
||||
box-shadow: inset 0 1px 2px rgba(43,55,66,0.16);
|
||||
}
|
||||
.pressure-value-slider::-webkit-slider-thumb {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: -7px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(42,121,172,0.88);
|
||||
background: #fafdff;
|
||||
box-shadow: 0 2px 6px rgba(42,78,104,0.24);
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
cursor: grab;
|
||||
}
|
||||
.pressure-value-slider::-moz-range-track {
|
||||
height: 6px;
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
background: rgba(67,158,218,0.78);
|
||||
box-shadow: inset 0 1px 2px rgba(43,55,66,0.16);
|
||||
}
|
||||
.pressure-value-slider::-moz-range-thumb {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(42,121,172,0.88);
|
||||
background: #fafdff;
|
||||
box-shadow: 0 2px 6px rgba(42,78,104,0.24);
|
||||
cursor: grab;
|
||||
}
|
||||
.pressure-value-slider:focus-visible::-webkit-slider-thumb {
|
||||
outline: 3px solid rgba(73,157,216,0.30);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
.pressure-value-slider:focus-visible::-moz-range-thumb {
|
||||
outline: 3px solid rgba(73,157,216,0.30);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.pressure-time-control[hidden] { display: none; }
|
||||
.pressure-spatial-control[hidden] { display: none; }
|
||||
.pressure-time-control {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
padding: 10px 10px 8px;
|
||||
border-radius: 11px;
|
||||
border: 1px solid rgba(84,68,48,0.13);
|
||||
background: rgba(255,255,255,0.58);
|
||||
}
|
||||
.pressure-time-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
color: var(--ink);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.pressure-time-summary output {
|
||||
display: inline-block;
|
||||
min-width: 42px;
|
||||
text-align: center;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.pressure-time-range {
|
||||
position: relative;
|
||||
height: 34px;
|
||||
margin: 0 7px;
|
||||
}
|
||||
.pressure-time-track {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 14px;
|
||||
height: 6px;
|
||||
border-radius: 999px;
|
||||
background: rgba(94,112,128,0.22);
|
||||
box-shadow: inset 0 1px 2px rgba(43,55,66,0.16);
|
||||
}
|
||||
.pressure-time-thumb {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 34px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
background: transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
.tool > span:not(.tool-size-badge) { display: none; }
|
||||
.tool[data-tool="observe"] { --tool-icon: url("../assets/ui/tool_observe.webp"); }
|
||||
.tool[data-tool="zunchi"] { --tool-icon: url("../assets/ui/tool_zunchi.webp"); }
|
||||
.tool[data-tool="sweet"] { --tool-icon: url("../assets/ui/tool_sweet.webp"); }
|
||||
.tool[data-tool="grass"] { --tool-icon: url("../assets/ui/tool_grass.webp"); }
|
||||
.tool[data-tool="stone"] { --tool-icon: url("../assets/ui/tool_stone.webp"); }
|
||||
.tool[data-tool="genkotsu"] { --tool-icon: url("../assets/ui/tool_genkotsu.webp"); }
|
||||
.tool[data-tool="firecracker"] { --tool-icon: url("../assets/ui/tool_firecracker.webp"); }
|
||||
.tool[data-tool="fence_v"] { --tool-icon: url("../assets/ui/tool_fence_v.webp"); }
|
||||
.tool[data-tool="fence_h"] { --tool-icon: url("../assets/ui/tool_fence_h.webp"); }
|
||||
.tool[data-tool="bed"] { --tool-icon: url("../assets/ui/tool_bed.webp"); }
|
||||
.tool[data-tool="love_mochi"] { --tool-icon: url("../assets/ui/tool_love_mochi.webp"); }
|
||||
.tool[data-tool="fight_mochi"] { --tool-icon: url("../assets/ui/tool_fight_mochi.webp"); }
|
||||
.tool[data-tool="water_bowl"] { --tool-icon: url("../assets/ui/tool_water_bowl.webp"); }
|
||||
|
||||
/* Generated parameter item tool icons. */
|
||||
.tool[data-tool="laxative"] { --tool-icon: url("../assets/ui/tool_laxative.webp?v=15.20.16"); }
|
||||
.tool[data-tool="protein"] { --tool-icon: url("../assets/ui/tool_protein.webp?v=15.20.16"); }
|
||||
.tool[data-tool="niteropu"] { --tool-icon: url("../assets/ui/tool_niteropu.webp?v=15.20.16"); }
|
||||
.tool[data-tool="ammo"] { --tool-icon: url("../assets/ui/tool_ammo.webp?v=15.20.16"); }
|
||||
.tool[data-tool="mystery_drug"] { --tool-icon: url("../assets/ui/tool_mystery_drug.webp?v=15.20.16"); }
|
||||
.tool[data-tool="mercury"] { --tool-icon: url("../assets/ui/tool_mercury.webp?v=15.20.16"); }
|
||||
.tool[data-tool="giant_drug"] { --tool-icon: url("../assets/ui/tool_giant_drug.webp?v=15.20.16"); }
|
||||
.tool[data-tool="dwarf_drug"] { --tool-icon: url("../assets/ui/tool_dwarf_drug.webp?v=15.20.16"); }
|
||||
.tool[data-tool="zunda_juice"] { --tool-icon: url("../assets/ui/tool_zunda_juice.webp?v=15.20.16"); }
|
||||
.tool[data-tool="freeze"] { --tool-icon: none; }
|
||||
.tool[data-tool="freeze"]::before {
|
||||
content: "🧊";
|
||||
background: none;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
.pressure-time-thumb::-webkit-slider-runnable-track {
|
||||
height: 6px;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}
|
||||
.tool[data-tool="freeze"]::after { content: "🧊"; background: none; font-size: 52px; right: -10px; left: auto; opacity: 0.08; display:flex; align-items:center; justify-content:flex-end; }
|
||||
.pressure-time-thumb::-webkit-slider-thumb {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: -7px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(42,121,172,0.88);
|
||||
background: #fafdff;
|
||||
box-shadow: 0 2px 6px rgba(42,78,104,0.24);
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
pointer-events: auto;
|
||||
cursor: grab;
|
||||
}
|
||||
.pressure-time-thumb::-moz-range-track {
|
||||
height: 6px;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}
|
||||
.pressure-time-thumb::-moz-range-thumb {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(42,121,172,0.88);
|
||||
background: #fafdff;
|
||||
box-shadow: 0 2px 6px rgba(42,78,104,0.24);
|
||||
pointer-events: auto;
|
||||
cursor: grab;
|
||||
}
|
||||
.pressure-time-thumb:focus-visible::-webkit-slider-thumb {
|
||||
outline: 3px solid rgba(73,157,216,0.30);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
.pressure-time-thumb:focus-visible::-moz-range-thumb {
|
||||
outline: 3px solid rgba(73,157,216,0.30);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
.pressure-time-thumb-start { z-index: 2; }
|
||||
.pressure-time-thumb-end { z-index: 3; }
|
||||
.pressure-time-range[data-wraps="1"] .pressure-time-thumb-start { z-index: 4; }
|
||||
.signal-channel-label {
|
||||
font-size: 13px;
|
||||
color: var(--ink);
|
||||
font-weight: 700;
|
||||
}
|
||||
.signal-channel-picker {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(44px, 1fr));
|
||||
gap: 7px;
|
||||
}
|
||||
.signal-channel-picker button {
|
||||
min-height: 38px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(84,68,48,0.18);
|
||||
background: rgba(255,255,255,0.78);
|
||||
color: var(--ink);
|
||||
font: 700 14px ui-rounded, sans-serif;
|
||||
cursor: pointer;
|
||||
}
|
||||
.signal-channel-picker button:hover { background: rgba(255,248,232,0.98); }
|
||||
.signal-channel-picker button.active {
|
||||
background: rgba(78,179,224,0.22);
|
||||
border-color: rgba(42,133,184,0.58);
|
||||
box-shadow: inset 0 0 0 1px rgba(42,133,184,0.18);
|
||||
}
|
||||
.rotator-editor-tools {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.rotator-editor-tools button {
|
||||
border: 1px solid rgba(84,68,48,0.16);
|
||||
background: rgba(255,255,255,0.74);
|
||||
color: var(--ink);
|
||||
border-radius: 999px;
|
||||
padding: 5px 10px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.rotator-editor-tools button.active {
|
||||
background: rgba(137, 101, 198, 0.18);
|
||||
border-color: rgba(112, 80, 170, 0.38);
|
||||
font-weight: 700;
|
||||
}
|
||||
#rotatorCanvas,
|
||||
#reciprocatorCanvas {
|
||||
width: 100%;
|
||||
height: min(58vh, 360px);
|
||||
display: block;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(84,68,48,0.20);
|
||||
background: #f7f3ea;
|
||||
touch-action: none;
|
||||
cursor: crosshair;
|
||||
}
|
||||
.rotator-editor-hint {
|
||||
margin: 7px 2px 0;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
.rotator-editor-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.rotator-editor {
|
||||
place-items: start center;
|
||||
padding: 8px;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
.rotator-editor-panel {
|
||||
width: calc(100vw - 16px);
|
||||
max-height: calc(100dvh - 16px);
|
||||
padding: 10px;
|
||||
}
|
||||
.rotator-editor-controls label { grid-template-columns: 1fr; align-items: stretch; }
|
||||
.rotator-editor-tools { max-height: 22vh; overflow-y: auto; padding-right: 2px; }
|
||||
#rotatorCanvas, #reciprocatorCanvas { height: min(44vh, 270px); }
|
||||
}
|
||||
|
||||
.fan-editor-panel { width: min(430px, calc(100vw - 28px)); }
|
||||
.fan-editor-help {
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
margin: 0 2px 10px;
|
||||
}
|
||||
.fan-editor-controls label {
|
||||
grid-template-columns: 56px 1fr 72px auto;
|
||||
}
|
||||
.fan-editor-controls .fan-editor-check {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 10px;
|
||||
background: rgba(255,255,255,0.58);
|
||||
border: 1px solid rgba(84,68,48,0.11);
|
||||
}
|
||||
.fan-editor-controls input[type="checkbox"] { accent-color: #70aee8; }
|
||||
@media (max-width: 560px) {
|
||||
.fan-editor-controls label { grid-template-columns: 1fr; align-items: stretch; }
|
||||
.fan-editor-panel { width: calc(100vw - 16px); }
|
||||
}
|
||||
|
||||
/* v39.15.130: only in-game overlay windows scroll when they exceed the viewport.
|
||||
Right-panel cards and tool categories intentionally keep the older stable layout. */
|
||||
.field-dialog,
|
||||
.signboard-editor,
|
||||
.rotator-editor {
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
align-items: flex-start;
|
||||
justify-items: center;
|
||||
box-sizing: border-box;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.field-dialog-panel,
|
||||
.signboard-editor-panel,
|
||||
.rotator-editor-panel {
|
||||
max-height: calc(100dvh - 24px);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
overscroll-behavior: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.save-confirm-dialog { z-index: 36; }
|
||||
.save-confirm-panel { width: min(390px, 100%); }
|
||||
.save-confirm-message { margin-bottom: 8px; color: var(--ink); font-size: 14px; }
|
||||
|
||||
/* Logic-board editor */
|
||||
.circuit-board-editor{position:fixed;inset:0;z-index:1200;display:grid;place-items:center;padding:18px;background:rgba(18,22,27,.58);backdrop-filter:blur(3px)}
|
||||
.circuit-board-editor.hidden{display:none}
|
||||
.circuit-board-panel{width:min(980px,96vw);max-height:92vh;overflow:hidden;background:rgba(250,249,244,.98);color:#2e3531;border:1px solid rgba(44,74,57,.35);border-radius:18px;box-shadow:0 24px 70px rgba(0,0,0,.35);display:flex;flex-direction:column}
|
||||
.circuit-board-titlebar{display:flex;align-items:center;justify-content:space-between;padding:14px 18px;background:linear-gradient(135deg,#235b3a,#347b50);color:white}
|
||||
.circuit-board-titlebar h2{margin:0;font-size:1.18rem}
|
||||
.circuit-board-titlebar button{border:0;background:transparent;color:white;font-size:1.7rem;line-height:1;cursor:pointer}
|
||||
.circuit-board-help{margin:12px 18px 6px;font-size:.88rem;color:#536059}
|
||||
.circuit-board-layout{display:grid;grid-template-columns:minmax(280px,42%) minmax(360px,58%);gap:14px;padding:10px 18px;min-height:0;flex:1}
|
||||
.circuit-board-preview{align-self:start;background:#17231c;border-radius:14px;padding:10px;min-height:250px;position:sticky;top:0}
|
||||
.circuit-board-preview svg{width:100%;height:auto;display:block}
|
||||
.circuit-preview-lines path{fill:none;stroke:#d7b84d;stroke-width:2.4}
|
||||
.circuit-preview-gates rect{fill:#26352d;stroke:#d9e9dd;stroke-width:1.3}.circuit-preview-gates text{fill:#f4f7f2;text-anchor:middle;font:700 11px sans-serif}
|
||||
.circuit-preview-pins circle{fill:#73c8ff;stroke:#f4f1d6;stroke-width:1.2}.circuit-preview-pins circle:nth-of-type(n+4){fill:#ff9a60}.circuit-preview-pins text{fill:#eef4ef;font:700 11px sans-serif}
|
||||
.circuit-board-scroll{overflow:auto;padding-right:4px}.circuit-board-scroll section{margin:0 0 14px;padding:12px;border:1px solid rgba(45,83,60,.18);border-radius:12px;background:white}.circuit-board-scroll h3{margin:0 0 10px;font-size:.94rem}
|
||||
.circuit-port-row,.circuit-gate-row,.circuit-output-row{display:grid;grid-template-columns:minmax(72px,1fr) auto auto auto;gap:8px;align-items:center;padding:7px 0;border-bottom:1px solid rgba(0,0,0,.07)}
|
||||
.circuit-port-row:last-child,.circuit-gate-row:last-child,.circuit-output-row:last-child{border-bottom:0}.circuit-port-row label,.circuit-gate-row label{display:flex;align-items:center;gap:4px;font-size:.8rem}
|
||||
.circuit-board-panel select,.circuit-board-panel button{font:inherit}.circuit-board-panel select{padding:5px 7px;border:1px solid rgba(37,77,53,.27);border-radius:7px;background:white}
|
||||
.circuit-gate-actions{display:flex;gap:7px;margin-bottom:8px}.circuit-gate-actions button,.circuit-gate-row button,.circuit-board-actions button{padding:7px 12px;border:1px solid rgba(38,82,54,.26);border-radius:9px;background:#f1f5f1;cursor:pointer}.circuit-gate-row button{padding:4px 9px;color:#a13f3f}
|
||||
.circuit-empty{margin:6px 0;color:#7a837d;font-size:.84rem}.circuit-output-row{grid-template-columns:50px 1fr}.circuit-board-actions{display:flex;justify-content:flex-end;gap:9px;padding:12px 18px 16px;border-top:1px solid rgba(0,0,0,.09)}.circuit-board-actions .primary{background:#2e754b;color:white;border-color:#245f3d}
|
||||
@media(max-width:760px){.circuit-board-layout{grid-template-columns:1fr}.circuit-board-preview{position:relative;min-height:180px}.circuit-port-row,.circuit-gate-row{grid-template-columns:1fr 1fr}.circuit-gate-row button{justify-self:end}.circuit-board-panel{max-height:96vh}}
|
||||
|
||||
/* Grid circuit editor reuses the mechanical editor shell and toolbar. */
|
||||
.circuit-grid-panel { width: min(930px, calc(100vw - 28px)); }
|
||||
.circuit-grid-tools { max-height: none; overflow: visible; }
|
||||
.circuit-grid-canvas-wrap {
|
||||
max-width: 100%;
|
||||
max-height: min(62vh, 560px);
|
||||
overflow: auto;
|
||||
padding: 6px;
|
||||
border-radius: 12px;
|
||||
background: rgba(20,35,27,0.35);
|
||||
}
|
||||
#circuitGridCanvas {
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: none;
|
||||
display: block;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(84,68,48,0.20);
|
||||
background: #14231b;
|
||||
touch-action: none;
|
||||
cursor: crosshair;
|
||||
}
|
||||
.circuit-grid-hint { line-height: 1.55; }
|
||||
@media (max-width: 560px) {
|
||||
.circuit-grid-panel { width: calc(100vw - 16px); }
|
||||
.circuit-grid-tools { max-height: 20vh; overflow-y: auto; }
|
||||
.circuit-grid-canvas-wrap { max-height: min(48vh, 360px); }
|
||||
#circuitGridCanvas { max-width: none; }
|
||||
}
|
||||
|
||||
/* v39.16.69: circuit terminal count controls and detector numeric range inputs. */
|
||||
.circuit-port-count-controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
.circuit-port-count-controls label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
padding: 7px 9px;
|
||||
border: 1px solid rgba(84,68,48,0.14);
|
||||
border-radius: 10px;
|
||||
background: rgba(255,255,255,0.56);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.circuit-port-count-controls button {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
padding: 0;
|
||||
border: 1px solid rgba(38,82,54,.26);
|
||||
border-radius: 8px;
|
||||
background: #f1f5f1;
|
||||
cursor: pointer;
|
||||
}
|
||||
.circuit-port-count-controls input {
|
||||
width: 54px;
|
||||
min-height: 30px;
|
||||
padding: 3px 6px;
|
||||
border: 1px solid rgba(37,77,53,.27);
|
||||
border-radius: 7px;
|
||||
background: white;
|
||||
text-align: center;
|
||||
font: inherit;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.pressure-number-inputs {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
.pressure-number-inputs label {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(72px, 1fr);
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.pressure-number-inputs input {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid rgba(42,121,172,0.28);
|
||||
border-radius: 8px;
|
||||
background: rgba(255,255,255,0.92);
|
||||
color: var(--ink);
|
||||
font: inherit;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.circuit-port-count-controls { display: grid; grid-template-columns: 1fr; }
|
||||
.circuit-port-count-controls label { justify-content: space-between; }
|
||||
}
|
||||
|
||||
/* v39.16.70: resizable circuit board and movable junctions. */
|
||||
.circuit-port-count-controls label:nth-child(-n+2){background:rgba(236,246,239,.82)}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
.tool-tip-popover {
|
||||
position: fixed;
|
||||
z-index: 2147483000;
|
||||
width: 220px;
|
||||
max-width: min(220px, calc(100vw - 16px));
|
||||
width: fit-content;
|
||||
min-width: min(160px, calc(100vw - 18px));
|
||||
max-width: min(620px, calc(100vw - 18px));
|
||||
box-sizing: border-box;
|
||||
padding: 7px 9px;
|
||||
border-radius: 10px;
|
||||
background: rgba(46, 38, 30, 0.78);
|
||||
|
|
@ -15,6 +17,9 @@
|
|||
backdrop-filter: blur(1px);
|
||||
text-shadow: 0 1px 3px rgba(25, 18, 12, 0.55);
|
||||
pointer-events: none;
|
||||
white-space: pre-line;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
}
|
||||
.tool-tip-popover.hidden { display: none; }
|
||||
.selected-actions {
|
||||
|
|
@ -22,6 +27,9 @@
|
|||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin: 8px 0 4px;
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
isolation: isolate;
|
||||
}
|
||||
.favorite-toggle {
|
||||
border: 1px solid rgba(128, 102, 56, 0.32);
|
||||
|
|
@ -29,8 +37,15 @@
|
|||
background: rgba(255, 248, 221, 0.92);
|
||||
color: #5a4630;
|
||||
font-weight: 700;
|
||||
padding: 5px 10px;
|
||||
padding: 7px 12px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
display: block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
pointer-events: auto;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
.favorite-toggle.active {
|
||||
background: rgba(255, 229, 122, 0.95);
|
||||
|
|
@ -241,12 +256,6 @@
|
|||
font-size: 12px;
|
||||
color: rgba(88, 73, 60, 0.82);
|
||||
}
|
||||
.loading-hint {
|
||||
margin: 10px 0 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.55;
|
||||
color: rgba(111, 92, 73, 0.78);
|
||||
}
|
||||
|
||||
.collapsible-card {
|
||||
padding-top: 10px;
|
||||
|
|
@ -278,14 +287,14 @@
|
|||
position: relative;
|
||||
}
|
||||
.panel-card-state::before {
|
||||
content: "▾";
|
||||
content: "\25be ";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-52%);
|
||||
color: rgba(98, 82, 64, 0.72);
|
||||
}
|
||||
.collapsible-card.collapsed .panel-card-state::before { content: "▸"; }
|
||||
.collapsible-card.collapsed .panel-card-state::before { content: "\25b8 "; }
|
||||
.collapsible-card.collapsed .panel-card-body { display: none; }
|
||||
.panel-card-body { padding-top: 12px; }
|
||||
|
||||
|
|
@ -336,13 +345,20 @@
|
|||
border: 1px solid rgba(192, 138, 96, 0.24);
|
||||
background: rgba(255, 247, 239, 0.74);
|
||||
}
|
||||
.event-category-heading {
|
||||
margin-bottom: 7px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: rgba(82, 66, 52, 0.88);
|
||||
}
|
||||
.push-master {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 8px;
|
||||
margin-top: 9px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.push-kind-grid {
|
||||
display: grid;
|
||||
|
|
@ -419,6 +435,14 @@
|
|||
.log-push-toast.log-push-food::before { background: #e9bd61; }
|
||||
.log-push-toast.log-push-weather::before { background: #7bbbdc; }
|
||||
.log-push-toast.log-push-grass::before { background: #8cc77a; }
|
||||
.log-push-toast.log-push-event::before { background: #8b82d6; }
|
||||
|
||||
.log-push-toast.log-push-life::before { background: #82bd72; }
|
||||
.log-push-toast.log-push-danger::before { background: #df845d; }
|
||||
.log-push-toast.log-push-social::before { background: #b28bd0; }
|
||||
.log-push-toast.log-push-living::before { background: #d7ad58; }
|
||||
.log-push-toast.log-push-environment::before { background: #70aac8; }
|
||||
.log-push-toast.log-push-observe::before { background: #9b9184; }
|
||||
.log-push-toast.leaving {
|
||||
animation: logPushOut .18s ease both;
|
||||
}
|
||||
|
|
@ -431,17 +455,6 @@
|
|||
to { transform: translateY(8px); opacity: 0; }
|
||||
}
|
||||
|
||||
/* v15.12.0: loading / collapsible cards / sound settings */
|
||||
.loading-screen {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1000;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 20px;
|
||||
background: radial-gradient(circle at top, rgba(255,251,243,0.98), rgba(245,238,225,0.96) 56%, rgba(234,225,208,0.98));
|
||||
transition: opacity .22s ease, visibility .22s ease;
|
||||
}
|
||||
|
||||
.selected-info .info-row[data-metric] {
|
||||
border-radius: 8px;
|
||||
|
|
@ -451,6 +464,34 @@
|
|||
.selected-info .info-row[data-metric="loneliness"] { background: rgba(116, 150, 214, 0.10); }
|
||||
.selected-info .info-row[data-metric="energy"] { background: rgba(104, 184, 96, 0.10); }
|
||||
.selected-info .info-row[data-metric="stress"] { background: rgba(214, 104, 88, 0.10); }
|
||||
.selected-info .need-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 6px;
|
||||
margin: 7px 0 8px;
|
||||
}
|
||||
.selected-info .need-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 44px;
|
||||
padding: 6px 4px;
|
||||
border: 1px solid rgba(84,68,48,0.12);
|
||||
border-radius: 10px;
|
||||
background: rgba(255,255,255,0.42);
|
||||
}
|
||||
.selected-info .need-cell span { font-size: 11px; color: var(--muted); }
|
||||
.selected-info .need-cell strong { font-size: 16px; color: #3f3225; }
|
||||
.selected-info .need-cell[data-chosen-need="true"] {
|
||||
border-color: rgba(65, 112, 74, 0.58);
|
||||
background: rgba(106, 154, 84, 0.20);
|
||||
box-shadow: inset 0 0 0 1px rgba(65, 112, 74, 0.18);
|
||||
}
|
||||
.selected-info .need-cell[data-chosen-need="true"] strong {
|
||||
color: #315f32;
|
||||
}
|
||||
.selected-info .action-reason-row strong { text-align: right; }
|
||||
.selected-info .info-row[data-metric="zunchi"] { background: rgba(112, 142, 70, 0.10); }
|
||||
.selected-info .info-row[data-metric="mood"] { background: rgba(222, 196, 86, 0.10); }
|
||||
.selected-info .info-row[data-metric="lack"] { background: rgba(142, 112, 184, 0.10); }
|
||||
|
|
@ -559,19 +600,29 @@
|
|||
}
|
||||
.save-slot-list {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
margin: 14px 0;
|
||||
}
|
||||
.save-slot-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(86px, 0.7fr) minmax(180px, 2fr) auto;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
padding: 9px 10px;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
gap: 8px;
|
||||
align-items: stretch;
|
||||
min-height: 132px;
|
||||
padding: 10px;
|
||||
border: 1px solid rgba(115, 91, 62, 0.20);
|
||||
border-radius: 12px;
|
||||
background: rgba(255, 255, 255, 0.56);
|
||||
}
|
||||
.save-warning {
|
||||
color: #8a4f2f;
|
||||
font-weight: 800;
|
||||
background: rgba(255, 239, 220, 0.72);
|
||||
border: 1px solid rgba(190, 118, 72, 0.22);
|
||||
border-radius: 10px;
|
||||
padding: 7px 9px;
|
||||
}
|
||||
.save-slot-title {
|
||||
font-weight: 800;
|
||||
color: #4f3928;
|
||||
|
|
@ -583,9 +634,12 @@
|
|||
.save-slot-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
justify-content: flex-end;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
.save-slot-list { grid-template-columns: 1fr; }
|
||||
}
|
||||
.save-slot-actions .btn[disabled] {
|
||||
opacity: 0.42;
|
||||
pointer-events: none;
|
||||
|
|
@ -613,11 +667,605 @@
|
|||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.save-slot-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.save-slot-actions {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
/* v39.16.40: compact header menu and folded colony settings. */
|
||||
.top-menu { position: relative; display: inline-block; }
|
||||
.top-menu-btn {
|
||||
min-width: 40px;
|
||||
padding-inline: 11px;
|
||||
font-size: 20px;
|
||||
line-height: 1;
|
||||
font-family: system-ui, sans-serif;
|
||||
}
|
||||
.top-menu-panel {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: calc(100% + 8px);
|
||||
z-index: 14000;
|
||||
width: min(250px, calc(100vw - 20px));
|
||||
padding: 8px;
|
||||
border: 1px solid rgba(84,68,48,.16);
|
||||
border-radius: 16px;
|
||||
background: rgba(255,252,246,.98);
|
||||
box-shadow: 0 16px 42px rgba(44,34,25,.20);
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
}
|
||||
.top-menu-panel.hidden { display: none; }
|
||||
.speed-menu-panel { width: 116px; min-width: 116px; }
|
||||
.speed-menu-panel .top-menu-item { text-align: center; }
|
||||
.speed-menu-panel .top-menu-item.active,
|
||||
.speed-menu-panel .top-menu-item[aria-checked="true"] { background: rgba(224,242,201,.92); }
|
||||
.top-menu-item {
|
||||
width: 100%;
|
||||
min-height: 38px;
|
||||
border: 0;
|
||||
border-radius: 11px;
|
||||
padding: 8px 10px;
|
||||
background: transparent;
|
||||
color: var(--ink);
|
||||
text-align: left;
|
||||
font: inherit;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
.top-menu-item:hover,
|
||||
.top-menu-item:focus-visible { background: rgba(238,245,225,.88); outline: none; }
|
||||
.top-menu-sound { display: block; width: 100%; }
|
||||
.top-menu-sound .sound-menu-btn { display: block; border: 0; box-shadow: none; }
|
||||
.top-menu-sound .sound-panel {
|
||||
position: static;
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
margin: 3px 3px 5px;
|
||||
padding: 9px 10px;
|
||||
border-radius: 12px;
|
||||
box-shadow: none;
|
||||
background: rgba(245,240,229,.78);
|
||||
}
|
||||
|
||||
.top-menu-visual { display: block; width: 100%; }
|
||||
.sound-panel-subtitle {
|
||||
padding: 2px 0 1px;
|
||||
color: var(--muted);
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
letter-spacing: .05em;
|
||||
}
|
||||
.top-menu-visual .sound-panel label { min-height: 26px; }
|
||||
|
||||
.reset-top-btn {
|
||||
color: #8e2424 !important;
|
||||
border-color: rgba(182,69,69,.48) !important;
|
||||
background: linear-gradient(180deg, rgba(255,245,245,.96), rgba(255,224,224,.92)) !important;
|
||||
font-weight: 800;
|
||||
}
|
||||
.reset-top-btn:hover { background: linear-gradient(180deg, #fff4f4, #ffd6d6) !important; }
|
||||
|
||||
|
||||
.colony-settings-body { display: grid; gap: 0; }
|
||||
.colony-setting-section { padding: 10px; }
|
||||
.colony-setting-section + .colony-setting-section { border-top: 1px solid rgba(118,92,61,.12); }
|
||||
.colony-setting-section-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 7px; color: var(--ink); font-size: 12px; font-weight: 800; }
|
||||
.colony-setting-section-head strong { font-size: 15px; }
|
||||
.colony-setting-section-head small { margin-left: auto; color: var(--muted); font-size: 10px; font-weight: 500; }
|
||||
|
||||
.colony-setting-fold {
|
||||
margin: 8px 0 4px;
|
||||
border: 1px solid rgba(118,92,61,.16);
|
||||
border-radius: 14px;
|
||||
background: rgba(255,251,244,.56);
|
||||
overflow: hidden;
|
||||
}
|
||||
.colony-setting-fold > summary {
|
||||
min-height: 38px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 11px;
|
||||
color: rgba(68,55,41,.84);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
user-select: none;
|
||||
}
|
||||
.colony-setting-fold > summary::-webkit-details-marker { display: none; }
|
||||
.colony-setting-fold > summary::before { content: "\25b8"; color: var(--muted); width: 12px; }
|
||||
.colony-setting-fold[open] > summary::before { content: "\25be"; }
|
||||
.colony-setting-fold > summary strong { margin-left: auto; font-size: 15px; }
|
||||
.colony-setting-fold > summary small { margin-left: auto; color: var(--muted); font-size: 10px; font-weight: 500; }
|
||||
.colony-setting-fold[open] > summary { border-bottom: 1px solid rgba(118,92,61,.12); }
|
||||
.colony-setting-fold .temperature-control,
|
||||
.colony-setting-fold .colony-limit-control {
|
||||
margin: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
}
|
||||
.colony-setting-fold .temperature-control { display: grid; gap: 5px; }
|
||||
.colony-setting-fold .temperature-auto { margin-left: 0; justify-self: end; }
|
||||
|
||||
/* v39.16.40: one unified colony settings fold. */
|
||||
.colony-settings-fold .temperature-control,
|
||||
.colony-settings-fold .colony-limit-control { padding: 0; border: 0; border-radius: 0; background: transparent; }
|
||||
|
||||
|
||||
/* v39.16.40: restore the compact sticky operation palette from the earlier UI. */
|
||||
.tool-category[data-tool-category="operate"]:not(.collapsed) {
|
||||
position: sticky;
|
||||
top: 58px;
|
||||
z-index: 11;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(180deg, rgba(238,247,255,.98), rgba(221,237,255,.98));
|
||||
border: 3px solid rgba(52,112,188,.72);
|
||||
box-shadow: 0 9px 22px rgba(58,82,112,.18), inset 0 0 0 1px rgba(255,255,255,.86);
|
||||
}
|
||||
.tool-category[data-tool-category="operate"] .tool-category-toggle {
|
||||
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(231,243,255,.98));
|
||||
border-bottom-color: rgba(82,128,190,.30);
|
||||
}
|
||||
.tool-category[data-tool-category="operate"] .tool-category-body {
|
||||
grid-template-columns: repeat(6, minmax(0,1fr));
|
||||
gap: 5px;
|
||||
background: rgba(238,247,255,.98);
|
||||
}
|
||||
.tool-category[data-tool-category="operate"] .tool {
|
||||
grid-template-columns: 1fr;
|
||||
min-height: 38px;
|
||||
padding: 4px;
|
||||
justify-items: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
background: rgba(255,255,255,.96);
|
||||
border-color: rgba(82,128,190,.28);
|
||||
}
|
||||
.tool-category[data-tool-category="operate"] .tool-label { display: none; }
|
||||
.tool-category[data-tool-category="operate"] .tool::before {
|
||||
left: 50%; top: 50%; transform: translate(-50%,-50%) scale(var(--tool-icon-zoom,1));
|
||||
}
|
||||
.tool-category[data-tool-category="operate"] .tool-sprite-preview {
|
||||
left: 50%; top: 50%; transform: translate(-50%,-50%);
|
||||
}
|
||||
.tool-category[data-tool-category="operate"] .tool-shortcut {
|
||||
position: absolute; right: 3px; bottom: 2px; min-width: 13px; height: 13px;
|
||||
box-sizing: border-box; padding: 0 3px; border: 1px solid rgba(82,128,190,.26);
|
||||
border-radius: 5px; background: rgba(255,255,255,.88); color: rgba(48,74,112,.86);
|
||||
font: 700 9px/12px system-ui,sans-serif; text-align: center; pointer-events: none;
|
||||
}
|
||||
|
||||
/* A trash can inside a selection square for range delete. */
|
||||
.tool[data-tool="area_delete"].tool-emoji-icon::before {
|
||||
content: "";
|
||||
box-sizing: border-box;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border: 2.5px solid #a8444d;
|
||||
border-radius: 4px;
|
||||
background: rgba(255,255,255,.40);
|
||||
filter: none;
|
||||
}
|
||||
.tool[data-tool="area_delete"].tool-emoji-icon::after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 52%;
|
||||
width: 11px;
|
||||
height: 13px;
|
||||
box-sizing: border-box;
|
||||
transform: translate(-50%,-50%);
|
||||
border: 2px solid #a8444d;
|
||||
border-top-width: 3px;
|
||||
border-radius: 1px 1px 3px 3px;
|
||||
background: repeating-linear-gradient(90deg, transparent 0 2px, rgba(168,68,77,.75) 2px 3px, transparent 3px 5px);
|
||||
box-shadow: 0 -5px 0 -3px #a8444d;
|
||||
z-index: 3;
|
||||
pointer-events: none;
|
||||
}
|
||||
.tool-category[data-tool-category="operate"] .tool[data-tool="area_delete"].tool-emoji-icon::before {
|
||||
top: 50% !important;
|
||||
transform: translate(-50%,-50%) !important;
|
||||
}
|
||||
|
||||
/* Colony settings use the colony card's own button palette. */
|
||||
.colony-card .colony-setting-fold {
|
||||
border-color: var(--ui-theme-mid);
|
||||
background: var(--ui-theme-soft);
|
||||
}
|
||||
.colony-card .colony-setting-fold > summary {
|
||||
border-color: var(--ui-theme-mid);
|
||||
background: linear-gradient(180deg, rgba(255,255,255,.78), var(--ui-theme-soft-2));
|
||||
color: var(--ink);
|
||||
}
|
||||
.colony-card .colony-setting-fold[open] > summary { border-bottom-color: var(--ui-theme-mid); }
|
||||
.colony-card .colony-setting-section + .colony-setting-section { border-top-color: rgba(var(--ui-theme-rgb),.22); }
|
||||
.colony-stats { grid-template-columns: 1fr; }
|
||||
.ground-type-btn.colony-setting-action {
|
||||
width: 100%;
|
||||
min-height: 38px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding: 7px 10px;
|
||||
border: 1px solid var(--ui-theme-mid);
|
||||
border-radius: 11px;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,.76), var(--ui-theme-soft-2));
|
||||
color: var(--ink);
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
}
|
||||
.ground-type-btn.colony-setting-action:hover { background: rgba(var(--ui-theme-rgb),.16); }
|
||||
.ground-type-btn.colony-setting-action strong { font-size: 15px; }
|
||||
.ground-type-btn.colony-setting-action span { color: var(--muted); font-size: 11px; font-weight: 700; }
|
||||
|
||||
/* Shared panel-search primitives. */
|
||||
.panel-search-control {
|
||||
grid-column: 1 / -1;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0,1fr) auto 32px;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
margin-top: 2px;
|
||||
padding-top: 6px;
|
||||
border-top: 1px solid rgba(82,128,190,.20);
|
||||
}
|
||||
.panel-search-input {
|
||||
width: 100%; min-width: 0; height: 32px; box-sizing: border-box;
|
||||
padding: 6px 8px; border: 1px solid rgba(82,128,190,.34); border-radius: 9px;
|
||||
background: rgba(255,255,255,.94); color: var(--ink); font: 12px/1.2 var(--font,sans-serif);
|
||||
}
|
||||
.panel-search-input:focus { outline: 2px solid rgba(40,112,180,.28); border-color: rgba(40,112,180,.58); }
|
||||
.panel-search-status { min-width: 23px; color: rgba(48,74,112,.86); font: 700 10px/1 system-ui,sans-serif; text-align: center; }
|
||||
.panel-search-next {
|
||||
width: 32px; height: 32px; padding: 0; border: 1px solid rgba(82,128,190,.34); border-radius: 9px;
|
||||
background: rgba(255,255,255,.94); color: #315f91; font: 800 17px/1 system-ui,sans-serif; cursor: pointer;
|
||||
}
|
||||
.panel-search-next:disabled { opacity: .36; cursor: default; }
|
||||
.panel-search-match {
|
||||
position: relative;
|
||||
outline: 3px solid rgba(255,190,38,.72) !important;
|
||||
outline-offset: 1px;
|
||||
background-color: rgba(255,235,135,.62) !important;
|
||||
scroll-margin-block: 150px 80px;
|
||||
}
|
||||
.panel-search-current {
|
||||
outline-color: rgba(225,91,38,.92) !important;
|
||||
box-shadow: 0 0 0 4px rgba(255,160,70,.22) !important;
|
||||
}
|
||||
|
||||
/* v39.16.40: header search uses partial matching and bidirectional navigation. */
|
||||
.top-panel-search {
|
||||
flex: 0 1 270px;
|
||||
width: clamp(190px, 21vw, 270px);
|
||||
min-width: 190px;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(92px, 1fr) auto 30px 30px;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
margin: 0 2px 0 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
.top-panel-search .panel-search-input { height: 36px; }
|
||||
.panel-search-prev,
|
||||
.panel-search-next {
|
||||
width: 30px;
|
||||
height: 32px;
|
||||
padding: 0;
|
||||
border: 1px solid rgba(82,128,190,.34);
|
||||
border-radius: 9px;
|
||||
background: rgba(255,255,255,.94);
|
||||
color: #315f91;
|
||||
font: 800 16px/1 system-ui,sans-serif;
|
||||
cursor: pointer;
|
||||
}
|
||||
.panel-search-prev:disabled,
|
||||
.panel-search-next:disabled { opacity: .36; cursor: default; }
|
||||
|
||||
/* Match the expanded colony settings surface to the colony graph surface. */
|
||||
.colony-card .colony-setting-fold[open] {
|
||||
background: linear-gradient(180deg, rgba(255,255,255,0.70), rgba(246,240,228,0.54));
|
||||
}
|
||||
|
||||
|
||||
/* Shared in-game confirmations */
|
||||
.game-confirm-dialog { z-index: 16000; }
|
||||
.game-confirm-panel { width: min(460px, 100%); }
|
||||
.game-confirm-message { white-space: pre-line; }
|
||||
.game-confirm-actions { justify-content: flex-end; }
|
||||
.game-confirm-actions .btn { width: auto; }
|
||||
|
||||
/* Achievements */
|
||||
.achievement-top-btn {
|
||||
border-color: rgba(177, 132, 38, .38);
|
||||
color: #72521a;
|
||||
background: linear-gradient(180deg, rgba(255, 251, 224, .96), rgba(247, 229, 153, .88));
|
||||
font-weight: 800;
|
||||
}
|
||||
.achievement-top-btn.complete {
|
||||
border-color: rgba(105, 158, 63, .44);
|
||||
color: #456729;
|
||||
background: linear-gradient(180deg, rgba(245, 255, 229, .98), rgba(213, 239, 173, .92));
|
||||
}
|
||||
.achievement-dialog-panel { width: min(720px, 100%); }
|
||||
.achievement-dialog-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 14px;
|
||||
}
|
||||
.achievement-dialog-head h2 { margin-bottom: 5px; }
|
||||
.achievement-dialog-kicker {
|
||||
margin-bottom: 3px;
|
||||
color: var(--muted);
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
letter-spacing: .08em;
|
||||
}
|
||||
.achievement-close-icon {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border: 1px solid rgba(84, 68, 48, .16);
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, .76);
|
||||
color: var(--ink);
|
||||
font-size: 22px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
.achievement-shared-status {
|
||||
margin: 10px 0;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid rgba(104, 86, 61, .12);
|
||||
border-radius: 10px;
|
||||
background: rgba(241, 235, 222, .54);
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
.achievement-list { display: grid; gap: 10px; }
|
||||
.achievement-group {
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(105, 87, 60, .15);
|
||||
border-radius: 13px;
|
||||
background: rgba(247, 243, 234, .72);
|
||||
}
|
||||
.achievement-group-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 10px 12px;
|
||||
color: #554b3e;
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
user-select: none;
|
||||
}
|
||||
.achievement-group-summary::-webkit-details-marker { display: none; }
|
||||
.achievement-group-summary::before {
|
||||
content: "\25b6";
|
||||
flex: 0 0 auto;
|
||||
color: #8b7657;
|
||||
font-size: 10px;
|
||||
transition: transform .16s ease;
|
||||
}
|
||||
.achievement-group[open] > .achievement-group-summary::before { transform: rotate(90deg); }
|
||||
.achievement-group-summary strong { margin-right: auto; font-size: 13px; }
|
||||
.achievement-group-count {
|
||||
color: var(--muted);
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.achievement-group-body {
|
||||
display: grid;
|
||||
gap: 9px;
|
||||
padding: 0 9px 9px;
|
||||
}
|
||||
.achievement-group-empty {
|
||||
margin: 0;
|
||||
padding: 12px;
|
||||
border-radius: 10px;
|
||||
background: rgba(255, 255, 255, .58);
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
.achievement-entry {
|
||||
--achievement-rate: 0%;
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
grid-template-columns: 42px minmax(0, 1fr);
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
min-height: 62px;
|
||||
padding: 9px 11px;
|
||||
border: 1px solid rgba(105, 87, 60, .15);
|
||||
border-radius: 13px;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, .88), rgba(244, 238, 226, .78));
|
||||
cursor: default;
|
||||
}
|
||||
.achievement-entry:focus-visible {
|
||||
outline: 2px solid rgba(85, 119, 149, .58);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
.achievement-entry::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
inset: 0 auto 0 0;
|
||||
pointer-events: none;
|
||||
width: var(--achievement-rate);
|
||||
background: linear-gradient(90deg, rgba(129, 158, 188, .27), rgba(156, 184, 207, .17));
|
||||
border-right: 1px solid rgba(85, 119, 149, .20);
|
||||
transition: width .32s ease;
|
||||
}
|
||||
.achievement-entry > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.achievement-entry.unlocked {
|
||||
border-color: rgba(173, 129, 35, .38);
|
||||
background: linear-gradient(180deg, rgba(255, 253, 235, .96), rgba(248, 231, 163, .70));
|
||||
}
|
||||
.achievement-entry.unlocked::before {
|
||||
background: linear-gradient(90deg, rgba(229, 179, 51, .34), rgba(244, 211, 102, .22));
|
||||
border-right-color: rgba(173, 129, 35, .28);
|
||||
}
|
||||
.achievement-entry-icon {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 11px;
|
||||
background: rgba(106, 91, 68, .09);
|
||||
color: #8b7657;
|
||||
font-size: 22px;
|
||||
}
|
||||
.achievement-entry.unlocked .achievement-entry-icon {
|
||||
background: rgba(242, 196, 74, .22);
|
||||
color: #b67b13;
|
||||
}
|
||||
.achievement-entry-copy { min-width: 0; }
|
||||
.achievement-entry-heading {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 9px;
|
||||
min-width: 0;
|
||||
}
|
||||
.achievement-entry-copy strong {
|
||||
flex: 0 0 auto;
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.achievement-entry-description {
|
||||
min-width: 0;
|
||||
color: #665d50;
|
||||
font-size: 11px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.achievement-entry.locked .achievement-entry-description {
|
||||
color: #8b8378;
|
||||
font-style: italic;
|
||||
}
|
||||
.achievement-entry-progress {
|
||||
margin-top: 4px;
|
||||
color: #526b82;
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.achievement-entry-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px 12px;
|
||||
margin-top: 5px;
|
||||
color: var(--muted);
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.achievement-entry-percentage {
|
||||
flex: 0 0 auto;
|
||||
margin-left: auto;
|
||||
color: #5d6570;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
@media (max-width: 620px) {
|
||||
.achievement-entry-heading { display: block; }
|
||||
.achievement-entry-description { display: block; margin-top: 2px; }
|
||||
}
|
||||
.achievement-dialog-actions { justify-content: flex-end; flex-wrap: wrap; }
|
||||
.achievement-dialog-actions .btn { width: auto; }
|
||||
.achievement-dialog-actions .achievement-reset-btn { margin-right: auto; }
|
||||
.achievement-toast {
|
||||
position: fixed;
|
||||
top: 18px;
|
||||
left: 18px;
|
||||
right: auto;
|
||||
z-index: 14000;
|
||||
width: min(360px, calc(100vw - 36px));
|
||||
display: grid;
|
||||
grid-template-columns: 44px minmax(0, 1fr);
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
padding: 11px 13px;
|
||||
border: 1px solid rgba(184, 137, 37, .42);
|
||||
border-radius: 14px;
|
||||
background: rgba(255, 249, 220, .97);
|
||||
color: #57401d;
|
||||
box-shadow: 0 16px 44px rgba(45, 34, 20, .24);
|
||||
transform-origin: center;
|
||||
transition: opacity .18s ease, transform .18s ease, filter .18s ease, visibility .18s ease;
|
||||
}
|
||||
.achievement-toast.absorbing {
|
||||
opacity: 0;
|
||||
transform: translate(var(--achievement-toast-dx, 0px), var(--achievement-toast-dy, 0px)) scale(.08);
|
||||
filter: blur(2px) saturate(1.4);
|
||||
border-radius: 999px;
|
||||
pointer-events: none;
|
||||
transition: opacity .72s ease-in, transform .72s cubic-bezier(.55, .02, .86, .3), filter .72s ease-in, border-radius .72s ease-in;
|
||||
}
|
||||
.achievement-top-btn.achievement-absorb-target {
|
||||
animation: achievement-target-pulse .22s ease-out .54s both;
|
||||
}
|
||||
@keyframes achievement-target-pulse {
|
||||
0% { transform: scale(1); }
|
||||
45% { transform: scale(1.09); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
.achievement-toast.hidden {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
.achievement-toast-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 12px;
|
||||
background: #efc34c;
|
||||
color: #fff8d8;
|
||||
font-size: 24px;
|
||||
}
|
||||
.achievement-toast-copy { min-width: 0; }
|
||||
.achievement-toast-copy small { display: block; margin-bottom: 2px; color: #8a6b2c; font-size: 10px; font-weight: 800; }
|
||||
.achievement-toast-copy strong { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; }
|
||||
.achievement-toast-condition { display: block; margin-top: 3px; max-width: 360px; color: #6f6248; font-size: 11px; line-height: 1.35; white-space: normal; }
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.achievement-toast {
|
||||
top: calc(env(safe-area-inset-top, 0px) + 10px);
|
||||
left: 12px;
|
||||
right: auto;
|
||||
width: min(360px, calc(100vw - 24px));
|
||||
}
|
||||
.achievement-dialog-actions { flex-direction: column-reverse; }
|
||||
.achievement-dialog-actions .btn { width: 100%; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.achievement-toast.absorbing { transition-duration: .12s; }
|
||||
.achievement-top-btn.achievement-absorb-target { animation: none; }
|
||||
}
|
||||
|
||||
/* v39.16.56: keep the upper-limit heading on one line in the compact panel. */
|
||||
.colony-limit-section .colony-setting-section-head > span { font-size: 10.8px; white-space: nowrap; }
|
||||
.colony-limit-section .colony-setting-section-head > small { font-size: 9px; white-space: nowrap; }
|
||||
|
||||
/* v39.16.60: align both upper-limit sliders independently of label length. */
|
||||
.colony-limit-grid { display: grid; gap: 7px; }
|
||||
.colony-limit-row { display: grid; grid-template-columns: 82px minmax(0, 1fr) 58px; align-items: center; gap: 8px; min-width: 0; }
|
||||
.colony-limit-row > span { min-width: 0; font-size: 11px; font-weight: 700; white-space: nowrap; }
|
||||
.colony-limit-slider { width: 100%; min-width: 0; margin: 0; }
|
||||
.colony-limit-number { width: 58px; min-width: 0; box-sizing: border-box; }
|
||||
@media (max-width: 520px) {
|
||||
.colony-limit-row { grid-template-columns: 72px minmax(0, 1fr) 54px; gap: 6px; }
|
||||
.colony-limit-number { width: 54px; }
|
||||
}
|
||||
|
|
|
|||
250
css/layout.css
|
|
@ -1,80 +1,49 @@
|
|||
/* CSS split file; preserve manifest load order. */
|
||||
.tool[data-tool="sleep_drug"] { --tool-icon: url("../assets/ui/tool_sleep_drug.webp"); }
|
||||
.tool[data-tool="nest_box"] { --tool-icon: url("../assets/ui/tool_nest_box.webp"); }
|
||||
.tool[data-tool="ant_nest"] { --tool-icon: url("../assets/ui/tool_ant_nest.webp"); }
|
||||
.tool[data-tool="ball"] { --tool-icon: url("../assets/ui/tool_ball.webp"); }
|
||||
.tool[data-tool="poke"] { --tool-icon: none; }
|
||||
.tool[data-tool="poke"]::before {
|
||||
content: "\1F448";
|
||||
/* CSS split file; preserve manifest load order. Tool image paths come from TOOL_DEFINITIONS. */
|
||||
.tool[data-tool="ball"] { --tool-icon-zoom: 1.36; }
|
||||
.tool[data-tool="shoot"] { --tool-icon-zoom: 1.48; }
|
||||
|
||||
.tool.tool-emoji-icon { --tool-icon: none; }
|
||||
.tool.tool-emoji-icon::before {
|
||||
content: var(--tool-emoji, "");
|
||||
background: none;
|
||||
font-size: 25px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
|
||||
font-size: 34px;
|
||||
line-height: 1;
|
||||
filter: drop-shadow(0 2px 3px rgba(52,40,26,0.12));
|
||||
}
|
||||
.tool[data-tool="poke"]::after {
|
||||
content: "\1F448";
|
||||
background: none;
|
||||
font-size: 48px;
|
||||
.tool.tool-emoji-icon::after { content: none; display: none; }
|
||||
|
||||
.tool[data-tool="poke"].tool-emoji-icon::before,
|
||||
.tool[data-tool="water_hose"].tool-emoji-icon::before { font-size: 25px; }
|
||||
.tool[data-tool="rope"].tool-emoji-icon::before,
|
||||
.tool[data-tool="rod"].tool-emoji-icon::before,
|
||||
.tool[data-tool="reciprocator"].tool-emoji-icon::before,
|
||||
.tool[data-tool="undo"].tool-emoji-icon::before,
|
||||
.tool[data-tool="redo"].tool-emoji-icon::before { font-size: 29px; font-family: var(--font, sans-serif); }
|
||||
.tool[data-tool="delete"] { --tool-icon-zoom: 1.42; }
|
||||
.tool[data-tool="area_delete"] { --tool-icon-zoom: 1; }
|
||||
.tool[data-tool="pinch"].tool-emoji-icon::before { font-size: 22px; }
|
||||
.tool[data-tool="bell"].tool-emoji-icon::before { font-size: 21px; z-index: 1; }
|
||||
.tool[data-tool="area_delete"].tool-emoji-icon::before {
|
||||
font-size: 31px;
|
||||
line-height: 1;
|
||||
opacity: 0.11;
|
||||
right: -6px;
|
||||
left: auto;
|
||||
top: 50%;
|
||||
bottom: auto;
|
||||
transform: translateY(-50%);
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
color: #b14f58;
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,0.75), 0 2px 5px rgba(146,54,62,0.14);
|
||||
filter: none;
|
||||
transform: translateY(-54%) scale(var(--tool-icon-zoom, 1));
|
||||
}
|
||||
.tool-category[data-tool-category="operate"] .tool[data-tool="area_delete"].tool-emoji-icon::before {
|
||||
top: 42% !important;
|
||||
transform: translate(-50%, -50%) scale(var(--tool-icon-zoom, 1)) !important;
|
||||
}
|
||||
|
||||
.tool[data-tool="pinch"] { --tool-icon: none; }
|
||||
.tool[data-tool="pinch"]::before {
|
||||
content: "\1F90F";
|
||||
background: none;
|
||||
font-size: 25px;
|
||||
line-height: 1;
|
||||
filter: drop-shadow(0 2px 3px rgba(52,40,26,0.12));
|
||||
}
|
||||
.tool[data-tool="pinch"]::after {
|
||||
content: "\1F90F";
|
||||
background: none;
|
||||
font-size: 48px;
|
||||
line-height: 1;
|
||||
opacity: 0.10;
|
||||
right: -6px;
|
||||
left: auto;
|
||||
top: 50%;
|
||||
bottom: auto;
|
||||
transform: translateY(-50%);
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
}
|
||||
.tool[data-tool="water_hose"] { --tool-icon: none; }
|
||||
.tool[data-tool="water_hose"]::before {
|
||||
content: "\1F6BF";
|
||||
background: none;
|
||||
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
filter: drop-shadow(0 2px 3px rgba(52,40,26,0.12));
|
||||
}
|
||||
.tool[data-tool="water_hose"]::after {
|
||||
content: "\1F6BF";
|
||||
background: none;
|
||||
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
|
||||
font-size: 46px;
|
||||
line-height: 1;
|
||||
opacity: 0.10;
|
||||
right: -6px;
|
||||
left: auto;
|
||||
top: 50%;
|
||||
bottom: auto;
|
||||
transform: translateY(-50%);
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
}
|
||||
|
||||
.tool[data-tool="delete"] { --tool-icon: url("../assets/ui/tool_delete.webp"); }
|
||||
.tool[data-tool="new"] { --tool-icon: url("../assets/ui/tool_new.webp"); }
|
||||
.tool[data-tool="observe"],
|
||||
.tool[data-tool="new"] { --tool-icon-zoom: 1.20; }
|
||||
.tool[data-tool="giant_drug"] { --tool-icon-zoom: 1.58; }
|
||||
.tool[data-tool="dwarf_drug"],
|
||||
.tool[data-tool="oshibyo"] { --tool-icon-zoom: 0.62; }
|
||||
.tool.selected {
|
||||
background: linear-gradient(180deg, #ffffff, #efffde);
|
||||
border-color: rgba(103, 178, 44, 0.52);
|
||||
|
|
@ -88,23 +57,21 @@
|
|||
margin: 10px 0 0;
|
||||
}
|
||||
|
||||
.tool-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
||||
.tool-head h2 { margin-bottom: 0; }
|
||||
.btn.mini { padding: 6px 10px; font-size: 12px; }
|
||||
.tool-body { margin-top: 12px; }
|
||||
.tool-palette { display: flex; flex-direction: column; gap: 9px; }
|
||||
.tool-body { margin-top: 10px; }
|
||||
.tool-palette { display: flex; flex-direction: column; gap: 8px; }
|
||||
.tool-category {
|
||||
border: 1px solid rgba(84,68,48,0.10);
|
||||
border: 1px solid var(--tool-cat-line, rgba(84,68,48,0.10));
|
||||
border-radius: 14px;
|
||||
background: rgba(255,255,255,0.34);
|
||||
background: linear-gradient(180deg, var(--tool-cat-a, rgba(255,255,255,0.36)), rgba(255,255,255,0.28));
|
||||
overflow: hidden;
|
||||
}
|
||||
.tool-category-toggle {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
border-bottom: 1px solid rgba(84,68,48,0.08);
|
||||
background: rgba(255,252,244,0.66);
|
||||
padding: 8px 10px;
|
||||
border-bottom: 1px solid var(--tool-cat-line, rgba(84,68,48,0.08));
|
||||
background: linear-gradient(180deg, var(--tool-cat-a, rgba(255,252,244,0.66)), var(--tool-cat-b, rgba(255,252,244,0.54)));
|
||||
padding: 7px 10px;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
font-weight: 700;
|
||||
|
|
@ -112,7 +79,7 @@
|
|||
}
|
||||
.tool-category-toggle::before { content: "\25be "; display: inline-block; width: 16px; color: var(--muted); }
|
||||
.tool-category.collapsed .tool-category-toggle::before { content: "\25b8 "; }
|
||||
.tool-category-body { padding: 8px; }
|
||||
.tool-category-body { padding: 7px; }
|
||||
.tool-category.collapsed .tool-category-body { display: none; }
|
||||
|
||||
.selected-data-groups {
|
||||
|
|
@ -130,8 +97,8 @@
|
|||
.selected-category-toggle {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
border-bottom: 1px solid rgba(84,68,48,0.08);
|
||||
background: rgba(255,252,244,0.66);
|
||||
border-bottom: 1px solid var(--tool-cat-line, rgba(84,68,48,0.08));
|
||||
background: linear-gradient(180deg, var(--tool-cat-a, rgba(255,252,244,0.66)), var(--tool-cat-b, rgba(255,252,244,0.54)));
|
||||
padding: 8px 10px;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
|
|
@ -146,20 +113,22 @@
|
|||
.selected-category-body .info-row:last-child { border-bottom: 0; }
|
||||
.tool-size-badge {
|
||||
position: absolute;
|
||||
right: 7px;
|
||||
bottom: 6px;
|
||||
min-width: 20px;
|
||||
padding: 2px 5px;
|
||||
right: 4px;
|
||||
bottom: 3px;
|
||||
min-width: 22px;
|
||||
padding: 1px 4px;
|
||||
border-radius: 999px;
|
||||
background: rgba(42,36,29,0.68);
|
||||
background: rgba(42,36,29,0.72);
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
font-size: 9.5px;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
box-shadow: 0 1px 4px rgba(42,36,29,0.16);
|
||||
pointer-events: none;
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
z-index: 4;
|
||||
}
|
||||
.chart-tabs { display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px; margin: 3px 0 8px; }
|
||||
.chart-tabs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 5px; margin: 3px 0 8px; }
|
||||
.chart-tabs button { border: 1px solid var(--line); background: rgba(255,255,255,0.62); border-radius: 999px; padding: 5px 4px; font-size: 11px; cursor: pointer; }
|
||||
.chart-tabs button.active { background: #efffde; border-color: rgba(103,178,44,0.46); font-weight: 700; }
|
||||
.log-accident { border-left-color: rgba(226, 136, 54, 0.76); }
|
||||
|
|
@ -182,14 +151,6 @@
|
|||
.stat span { display: block; font-size: 11px; color: var(--muted); }
|
||||
.stat strong { font-size: 18px; }
|
||||
|
||||
.meter-row {
|
||||
display: grid;
|
||||
grid-template-columns: 72px 1fr;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
margin: 6px 0;
|
||||
}
|
||||
meter {
|
||||
width: 100%;
|
||||
height: 12px;
|
||||
|
|
@ -321,7 +282,7 @@ meter {
|
|||
margin-right: 6px;
|
||||
padding: 2px 5px;
|
||||
border-radius: 999px;
|
||||
font-size: 9px;
|
||||
font-size: 9.5px;
|
||||
line-height: 1.1;
|
||||
text-align: center;
|
||||
background: rgba(84,68,48,0.08);
|
||||
|
|
@ -336,56 +297,65 @@ meter {
|
|||
.log-grass { border-left-color: rgba(72, 148, 67, 0.68); }
|
||||
.log-weather { border-left-color: rgba(92, 142, 210, 0.62); }
|
||||
.log-relation { border-left-color: rgba(204, 126, 176, 0.62); }
|
||||
.log-observe { border-left-color: rgba(175, 146, 86, 0.62); }
|
||||
|
||||
@media (max-width: 980px) {
|
||||
#app { padding: 10px; }
|
||||
.topbar { align-items: flex-start; flex-direction: column; }
|
||||
.top-left { align-items: flex-start; flex-direction: column; }
|
||||
.brand { align-items: center; flex-direction: row; gap: 10px; }
|
||||
.layout { grid-template-columns: 1fr; }
|
||||
.panel { max-height: none; overflow: visible; }
|
||||
#gameCanvas { height: var(--field-mobile-height, 66vh); min-height: var(--field-mobile-min-height, 460px); }
|
||||
.stage-wrap { min-height: var(--field-mobile-min-height, 460px); }
|
||||
.field-choice-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.tree-note {
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
margin: 0 0 10px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 10px;
|
||||
background: rgba(255,255,255,0.54);
|
||||
border: 1px solid rgba(84,68,48,0.10);
|
||||
}
|
||||
.log-event { border-left-color: rgba(132, 120, 210, 0.62); }
|
||||
|
||||
.log-life { border-left-color: rgba(105, 166, 96, 0.68); }
|
||||
.log-danger { border-left-color: rgba(214, 104, 70, 0.72); }
|
||||
.log-social { border-left-color: rgba(184, 116, 184, 0.66); }
|
||||
.log-living { border-left-color: rgba(183, 151, 74, 0.68); }
|
||||
.log-environment { border-left-color: rgba(82, 142, 170, 0.66); }
|
||||
.log-observe { border-left-color: rgba(142, 132, 116, 0.62); }
|
||||
|
||||
/* Clearer tool category grouping. */
|
||||
.tool-category[data-tool-category="operate"] { --tool-cat-a: rgba(224,239,255,0.82); --tool-cat-b: rgba(203,226,255,0.74); --tool-cat-line: rgba(82,128,190,0.26); }
|
||||
.tool-category[data-tool-category="food"] { --tool-cat-a: rgba(236,252,219,0.82); --tool-cat-b: rgba(214,244,194,0.74); --tool-cat-line: rgba(96,156,72,0.26); }
|
||||
.tool-category[data-tool-category="medicine"] { --tool-cat-a: rgba(243,231,255,0.84); --tool-cat-b: rgba(230,214,252,0.78); --tool-cat-line: rgba(136,94,184,0.28); }
|
||||
.tool-category[data-tool-category="habitat"] { --tool-cat-a: rgba(255,245,218,0.84); --tool-cat-b: rgba(250,229,186,0.76); --tool-cat-line: rgba(174,125,54,0.28); }
|
||||
.tool-category[data-tool-category="physics"] { --tool-cat-a: rgba(226,247,246,0.84); --tool-cat-b: rgba(199,232,231,0.76); --tool-cat-line: rgba(70,145,148,0.28); }
|
||||
.tool-category[data-tool-category="hazard"] { --tool-cat-a: rgba(255,230,224,0.84); --tool-cat-b: rgba(250,207,197,0.78); --tool-cat-line: rgba(189,88,66,0.30); }
|
||||
.tool-category { border-color: var(--tool-cat-line, rgba(84,68,48,0.10)); background: linear-gradient(180deg, var(--tool-cat-a, rgba(255,255,255,0.36)), rgba(255,255,255,0.28)); }
|
||||
.tool-category-toggle { background: linear-gradient(180deg, var(--tool-cat-a, rgba(255,252,244,0.66)), var(--tool-cat-b, rgba(255,252,244,0.54))); border-bottom-color: var(--tool-cat-line, rgba(84,68,48,0.08)); }
|
||||
.tool[data-tool="ball"] { --tool-icon: none; }
|
||||
.tool[data-tool="ball"]::before {
|
||||
content: "⚽";
|
||||
background: none;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-size: 28px;
|
||||
line-height: 1;
|
||||
opacity: 1;
|
||||
filter: drop-shadow(0 2px 3px rgba(52,40,26,0.12));
|
||||
}
|
||||
.tool[data-tool="ball"]::after { background: none; }
|
||||
@media (min-width: 921px) {
|
||||
.mobile-mode-controls { display: none !important; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
.tool-pick-mode-control.tool-emoji-icon::before {
|
||||
opacity: 0.18;
|
||||
color: #3b78a0;
|
||||
font-size: 29px;
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.tool-pick-mode-control {
|
||||
--tool-icon-zoom: 1;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,0.92), rgba(235,246,255,0.88));
|
||||
border-color: rgba(48,126,176,0.34);
|
||||
}
|
||||
.tool-pick-mode-control .tool-pick-mode-value {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
right: auto;
|
||||
bottom: 5px;
|
||||
transform: translateX(-50%);
|
||||
min-width: 38px;
|
||||
padding: 3px 6px;
|
||||
border-radius: 999px;
|
||||
z-index: 4;
|
||||
background: rgba(25,85,128,0.94);
|
||||
color: #fff;
|
||||
font-size: 8.5px;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
box-shadow: 0 1px 4px rgba(25,85,128,0.18);
|
||||
}
|
||||
.tool-pick-mode-control[data-pick-mode="grid20"],
|
||||
.tool-pick-mode-control[data-pick-mode="grid40"],
|
||||
.tool-pick-mode-control[data-pick-mode="grid60"] {
|
||||
background: linear-gradient(180deg, #ffffff, #eaf7ff);
|
||||
border-color: rgba(38,132,190,0.56);
|
||||
box-shadow: inset 0 0 0 1px rgba(84,176,226,0.20), 0 8px 20px rgba(59,133,177,0.10);
|
||||
}
|
||||
|
||||
.chart-range-tabs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 5px; margin: -2px 0 7px; }
|
||||
.chart-range-tabs button { border: 1px solid var(--line); background: rgba(255,255,255,0.48); border-radius: 8px; padding: 4px 4px; font-size: 10px; cursor: pointer; color: var(--muted); }
|
||||
.chart-range-tabs button.active { background: rgba(239,255,222,0.92); border-color: rgba(103,178,44,0.42); color: var(--text); font-weight: 700; }
|
||||
|
|
|
|||
548
css/mobile.css
|
|
@ -1,70 +1,4 @@
|
|||
/* CSS split file; preserve manifest load order. */
|
||||
.loading-screen.hidden { opacity: 0; visibility: hidden; pointer-events: none; }
|
||||
.loading-kicker { font-size: 12px; letter-spacing: .14em; color: rgba(116,96,76,.82); margin-bottom: 6px; }
|
||||
.loading-title { margin: 0 0 8px; font-size: 28px; color: #3f3429; }
|
||||
.loading-text { margin: 0 0 12px; color: rgba(90,74,58,.86); }
|
||||
.loading-sprite-strip { display: grid; grid-template-columns: repeat(4,minmax(0,1fr)); gap: 10px; margin: 8px 0 14px; }
|
||||
.loading-sprite-card { border-radius: 16px; background: linear-gradient(180deg,rgba(255,255,255,.84),rgba(248,243,232,.90)); border: 1px solid rgba(126,104,80,.12); min-height: 92px; display: grid; place-items: center; padding: 8px; box-shadow: 0 8px 18px rgba(83,63,44,.06); }
|
||||
.loading-sprite-card img { width: 68px; height: 68px; object-fit: contain; }
|
||||
.loading-progress-track { width: 100%; height: 14px; border-radius: 999px; overflow: hidden; background: rgba(132,164,217,.16); border: 1px solid rgba(92,126,181,.14); }
|
||||
.loading-progress-bar { height: 100%; border-radius: inherit; background: linear-gradient(90deg,#92d1ff,#a3dfa0 58%,#ffe192); transition: width .18s ease; }
|
||||
.loading-progress-meta { margin-top: 8px; display: flex; justify-content: space-between; align-items: center; font-size: 12px; color: rgba(88,73,60,.82); }
|
||||
.loading-hint { margin: 10px 0 0; font-size: 12px; line-height: 1.55; color: rgba(111,92,73,.78); }
|
||||
|
||||
.collapsible-card { padding-top: 10px; }
|
||||
.panel-card-toggle { width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 10px; border: 0; border-radius: 14px; padding: 10px 12px; cursor: pointer; font: inherit; color: var(--ink); background: rgba(255,255,255,.48); box-shadow: inset 0 1px 0 rgba(255,255,255,.54); }
|
||||
.panel-card-title { font-size: 17px; font-weight: 700; letter-spacing: .03em; }
|
||||
.panel-card-state { font-size: 12px; color: rgba(98,82,64,.78); padding-left: 16px; position: relative; }
|
||||
.panel-card-state::before { content: "▾"; position: absolute; left: 0; top: 50%; transform: translateY(-52%); color: rgba(98,82,64,.72); }
|
||||
.collapsible-card.collapsed .panel-card-state::before { content: "▸"; }
|
||||
.collapsible-card.collapsed .panel-card-body { display: none; }
|
||||
.panel-card-body { padding-top: 12px; }
|
||||
.tool-card { background: linear-gradient(180deg, rgba(255,247,252,.92), rgba(249,240,249,.90)); border-color: rgba(200,153,186,.34); }
|
||||
.colony-card { background: linear-gradient(180deg, rgba(244,250,238,.92), rgba(235,246,230,.90)); border-color: rgba(132,178,128,.34); }
|
||||
.log-card { background: linear-gradient(180deg, rgba(255,246,238,.92), rgba(252,239,226,.90)); border-color: rgba(207,157,121,.34); }
|
||||
.tool-card .panel-card-toggle { background: linear-gradient(180deg,rgba(255,239,248,.82),rgba(253,234,246,.84)); }
|
||||
.colony-card .panel-card-toggle { background: linear-gradient(180deg,rgba(240,252,237,.82),rgba(230,247,228,.86)); }
|
||||
.log-card .panel-card-toggle { background: linear-gradient(180deg,rgba(255,242,231,.84),rgba(253,235,220,.86)); }
|
||||
|
||||
.sound-category-controls,
|
||||
.log-push-controls {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(84,68,48,.12);
|
||||
background: rgba(255,255,255,.46);
|
||||
font-size: 12px;
|
||||
}
|
||||
.control-group-title { font-weight: 700; margin-bottom: 7px; color: rgba(58,48,39,.88); }
|
||||
.sound-category-controls { display: grid; grid-template-columns: 1fr; gap: 6px; }
|
||||
.sound-category-controls label,
|
||||
.push-master,
|
||||
.push-kind-grid label { display: flex; align-items: center; gap: 6px; cursor: pointer; user-select: none; }
|
||||
.sound-category-controls input,
|
||||
.push-master input,
|
||||
.push-kind-grid input { accent-color: #82bf66; }
|
||||
.log-push-controls { display: flex; flex-direction: column; gap: 8px; }
|
||||
.push-kind-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 5px 8px; }
|
||||
.log-push-overlay { position: fixed; left: 18px; bottom: 18px; z-index: 9998; width: min(320px, calc(100vw - 36px)); display: flex; flex-direction: column; gap: 8px; pointer-events: none; }
|
||||
.log-push-toast { position: relative; overflow: hidden; padding: 10px 12px 10px 17px; border-radius: 15px; background: rgba(255,252,246,.94); border: 1px solid rgba(84,68,48,.16); box-shadow: 0 12px 32px rgba(44,34,25,.18); animation: logPushIn .18s ease-out; }
|
||||
.log-push-toast::before { content: ""; position: absolute; inset: 0 auto 0 0; width: 5px; background: #a5c77a; }
|
||||
.log-push-toast .log-push-tag { display: inline-block; margin-right: 6px; font-size: 11px; font-weight: 700; color: rgba(54,45,38,.88); }
|
||||
.log-push-toast .log-push-time { font-size: 10px; color: rgba(104,88,72,.72); }
|
||||
.log-push-toast .log-push-text { margin-top: 3px; font-size: 12px; line-height: 1.35; color: rgba(48,40,33,.92); }
|
||||
.log-push-toast.log-push-death::before { background: #8f817a; }
|
||||
.log-push-toast.log-push-accident::before { background: #ee9e5d; }
|
||||
.log-push-toast.log-push-birth::before { background: #98cf7a; }
|
||||
.log-push-toast.log-push-fight::before { background: #e47b80; }
|
||||
.log-push-toast.log-push-relation::before { background: #b99be0; }
|
||||
.log-push-toast.log-push-food::before { background: #e9bd61; }
|
||||
.log-push-toast.log-push-weather::before { background: #7bbbdc; }
|
||||
.log-push-toast.log-push-grass::before { background: #8cc77a; }
|
||||
.log-push-toast.leaving { opacity: 0; transform: translateY(8px); transition: opacity .22s ease, transform .22s ease; }
|
||||
@keyframes logPushIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
|
||||
@media (max-width: 920px) { .loading-sprite-strip { grid-template-columns: repeat(2,minmax(0,1fr)); } }
|
||||
|
||||
|
||||
/* v15.12.1: top-right sound settings panel */
|
||||
/* Sound menu */
|
||||
.sound-menu { position: relative; display: inline-block; }
|
||||
.sound-menu-btn.active { background: linear-gradient(180deg, #f3ffe9, #e7f8db); border-color: rgba(103,178,44,.46); }
|
||||
.sound-panel {
|
||||
|
|
@ -88,40 +22,8 @@
|
|||
.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; } }
|
||||
|
||||
|
||||
/* v15.12.3: collapsed event panel should be genuinely thin */
|
||||
.log-card.collapsed {
|
||||
min-height: 0 !important;
|
||||
padding: 5px 7px !important;
|
||||
flex: 0 0 auto !important;
|
||||
}
|
||||
.log-card.collapsed .panel-card-toggle {
|
||||
min-height: 30px;
|
||||
padding: 5px 9px;
|
||||
border-radius: 11px;
|
||||
}
|
||||
.log-card.collapsed .panel-card-title {
|
||||
font-size: 13px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
.log-card.collapsed .panel-card-state {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* v15.12.4: event log scroll chains to the right menu at top/bottom */
|
||||
.log {
|
||||
overscroll-behavior-y: auto !important;
|
||||
}
|
||||
|
||||
/* v15.12.4: sound settings live only in the top-right sound menu */
|
||||
.log-card .sound-category-controls {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* v15.17.0: mobile layout isolation and canvas touch gestures */
|
||||
/* Shared layout and gesture guards */
|
||||
#app { overflow-x: clip; }
|
||||
.layout { position: relative; z-index: 2; }
|
||||
.panel { position: relative; z-index: 3; }
|
||||
|
|
@ -142,100 +44,408 @@
|
|||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@media (max-width: 980px) {
|
||||
.layout {
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
}
|
||||
.panel {
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
isolation: isolate;
|
||||
}
|
||||
.lineage-main-card {
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin-top: 14px;
|
||||
clear: both;
|
||||
}
|
||||
.lineage-main-content {
|
||||
height: auto;
|
||||
min-height: 340px;
|
||||
max-height: 72vh;
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
touch-action: pan-x pan-y pinch-zoom;
|
||||
}
|
||||
.lineage-family-tree {
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
touch-action: pan-x pan-y pinch-zoom;
|
||||
}
|
||||
.lineage-family-tree .lineage-tree-panel {
|
||||
max-width: none;
|
||||
}
|
||||
.log {
|
||||
overscroll-behavior-y: auto !important;
|
||||
}
|
||||
body.touch-mobile-ui .tool-tip-popover {
|
||||
max-width: min(520px, calc(100vw - 24px));
|
||||
}
|
||||
body.touch-mobile-ui .tool-category:not([data-tool-category="operate"]).collapsed .tool-category-body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Item icons and mobile input mode */
|
||||
.tool[data-tool="laxative"] { --tool-icon: url("../assets/ui/tool_laxative.webp?v=15.20.16"); }
|
||||
.tool[data-tool="protein"] { --tool-icon: url("../assets/ui/tool_protein.webp?v=15.20.16"); }
|
||||
.tool[data-tool="niteropu"] { --tool-icon: url("../assets/ui/tool_niteropu.webp?v=15.20.16"); }
|
||||
.tool[data-tool="ammo"] { --tool-icon: url("../assets/ui/tool_ammo.webp?v=15.20.16"); }
|
||||
.tool[data-tool="mystery_drug"] { --tool-icon: url("../assets/ui/tool_mystery_drug.webp?v=15.20.16"); }
|
||||
.tool[data-tool="mercury"] { --tool-icon: url("../assets/ui/tool_mercury.webp?v=15.20.16"); }
|
||||
.tool[data-tool="giant_drug"] { --tool-icon: url("../assets/ui/tool_giant_drug.webp?v=15.20.16"); }
|
||||
.tool[data-tool="dwarf_drug"] { --tool-icon: url("../assets/ui/tool_dwarf_drug.webp?v=15.20.16"); }
|
||||
.tool[data-tool="zunda_juice"] { --tool-icon: url("../assets/ui/tool_zunda_juice.webp?v=15.20.16"); }
|
||||
.mobile-mode-controls {
|
||||
/* Mobile input controls */
|
||||
.mobile-mode-controls,
|
||||
.mobile-rotate-controls {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
bottom: 10px;
|
||||
z-index: 24;
|
||||
gap: 5px;
|
||||
gap: 6px;
|
||||
padding: 6px;
|
||||
border-radius: 14px;
|
||||
background: rgba(255, 251, 245, 0.90);
|
||||
background: rgba(255, 251, 245, 0.92);
|
||||
box-shadow: 0 8px 22px rgba(80,60,35,.18);
|
||||
border: 1px solid rgba(120, 95, 60, .16);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
.mobile-mode-controls button {
|
||||
.mobile-mode-controls {
|
||||
left: 10px;
|
||||
bottom: 10px;
|
||||
gap: 5px;
|
||||
background: rgba(255, 251, 245, 0.90);
|
||||
}
|
||||
.mobile-rotate-controls {
|
||||
right: 10px;
|
||||
bottom: 58px;
|
||||
}
|
||||
.mobile-mode-controls button,
|
||||
.mobile-rotate-controls button {
|
||||
border: 0;
|
||||
border-radius: 10px;
|
||||
padding: 7px 8px;
|
||||
padding: 7px 10px;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
color: #65533f;
|
||||
background: rgba(255,255,255,.78);
|
||||
background: rgba(255,255,255,.82);
|
||||
box-shadow: inset 0 -1px 0 rgba(0,0,0,.05);
|
||||
}
|
||||
.mobile-mode-controls button { padding: 7px 8px; background: rgba(255,255,255,.78); }
|
||||
.mobile-mode-controls button.active {
|
||||
color: #3d321f;
|
||||
background: linear-gradient(180deg, #ffe9a8, #ffd376);
|
||||
box-shadow: 0 3px 10px rgba(150, 105, 30, .20);
|
||||
}
|
||||
body.touch-mobile-ui .mobile-mode-controls:not([hidden]) { display: flex; }
|
||||
body:not(.touch-mobile-ui) .mobile-mode-controls { display: none !important; }
|
||||
.mobile-rotate-controls button:active { transform: translateY(1px); }
|
||||
body.touch-mobile-ui .mobile-mode-controls:not([hidden]),
|
||||
body.touch-mobile-ui .mobile-rotate-controls:not([hidden]) { display: flex; }
|
||||
body:not(.touch-mobile-ui) .mobile-mode-controls,
|
||||
body:not(.touch-mobile-ui) .mobile-rotate-controls { display: none !important; }
|
||||
body.touch-mobile-ui.mobile-mode-camera #gameCanvas { cursor: grab; }
|
||||
body.touch-mobile-ui.mobile-mode-tool #gameCanvas { cursor: crosshair; }
|
||||
body.touch-mobile-ui.mobile-mode-family .lineage-main-card { z-index: 9; }
|
||||
|
||||
|
||||
/* Keep panel themes on narrow layouts. */
|
||||
.card.tool-card { --ui-theme-rgb: 0, 114, 178; background: linear-gradient(180deg, rgba(242, 248, 252, 0.94), rgba(232, 243, 250, 0.91)); border-color: rgba(0, 114, 178, 0.24); }
|
||||
.card.selected-card { --ui-theme-rgb: 204, 121, 167; background: linear-gradient(180deg, rgba(253, 245, 250, 0.94), rgba(249, 235, 244, 0.91)); border-color: rgba(204, 121, 167, 0.24); }
|
||||
.card.colony-card { --ui-theme-rgb: 0, 158, 115; background: linear-gradient(180deg, rgba(240, 250, 247, 0.94), rgba(229, 245, 239, 0.91)); border-color: rgba(0, 158, 115, 0.24); }
|
||||
.card.log-card { --ui-theme-rgb: 230, 159, 0; background: linear-gradient(180deg, rgba(255, 249, 236, 0.94), rgba(253, 242, 215, 0.91)); border-color: rgba(230, 159, 0, 0.24); }
|
||||
.tool[data-tool="freeze"] { --tool-icon: none; }
|
||||
.tool[data-tool="freeze"]::before {
|
||||
content: "🧊";
|
||||
background: none;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
/* Back to canvas affordance */
|
||||
.back-to-game-btn {
|
||||
position: fixed;
|
||||
right: calc(env(safe-area-inset-right, 0px) + 12px);
|
||||
bottom: calc(env(safe-area-inset-bottom, 0px) + 68px);
|
||||
z-index: 12000;
|
||||
border: 1px solid rgba(103,178,44,.38);
|
||||
border-radius: 999px;
|
||||
padding: 9px 12px;
|
||||
background: rgba(246, 255, 236, 0.94);
|
||||
color: #3f5f2b;
|
||||
font-weight: 900;
|
||||
font-size: 12px;
|
||||
box-shadow: 0 10px 25px rgba(54, 76, 40, .20);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
.back-to-game-btn[hidden] { display: none !important; }
|
||||
|
||||
@media (min-width: 981px) {
|
||||
html,
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
#app {
|
||||
width: min(1480px, 100vw);
|
||||
padding: 18px;
|
||||
overflow: visible;
|
||||
}
|
||||
.topbar { gap: 12px; }
|
||||
.top-left { 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 { margin-left: auto; }
|
||||
.top-actions .btn,
|
||||
.top-actions .sound-menu-btn,
|
||||
.room-link {
|
||||
min-height: 38px;
|
||||
padding: 8px 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.12;
|
||||
}
|
||||
.room-link {
|
||||
max-width: 180px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.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(5, minmax(0, 1fr));
|
||||
}
|
||||
.panel-quick-tabs [data-panel-tab="game"] { display: none !important; }
|
||||
.lineage-main-card {
|
||||
display: block;
|
||||
visibility: visible;
|
||||
width: auto;
|
||||
margin: 16px 0 0;
|
||||
}
|
||||
.lineage-main-content {
|
||||
height: min(78vh, 980px);
|
||||
min-height: 520px;
|
||||
max-height: calc(100vh - 120px);
|
||||
overflow: auto;
|
||||
overscroll-behavior: auto;
|
||||
}
|
||||
.back-to-game-btn { display: none !important; }
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
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 {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 8px;
|
||||
}
|
||||
.top-left {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
.brand {
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
}
|
||||
.brand h1,
|
||||
h1 {
|
||||
max-width: 100%;
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
white-space: nowrap;
|
||||
font-size: clamp(20px, 7vw, 30px);
|
||||
}
|
||||
.top-actions {
|
||||
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;
|
||||
}
|
||||
.sound-panel { right: auto; left: 0; }
|
||||
.layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
overflow: visible;
|
||||
}
|
||||
.field-choice-grid { grid-template-columns: 1fr; }
|
||||
.reset-preset-choice-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
#gameCanvas {
|
||||
height: var(--field-mobile-height, 66vh);
|
||||
min-height: var(--field-mobile-min-height, 460px);
|
||||
}
|
||||
.stage-wrap {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-height: var(--field-mobile-min-height, 460px);
|
||||
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-rotate-controls:not([hidden]) {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
right: auto;
|
||||
bottom: calc(env(safe-area-inset-bottom, 0px) + 56px);
|
||||
transform: translateX(-50%);
|
||||
z-index: 12002;
|
||||
max-width: calc(100vw - 22px);
|
||||
}
|
||||
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;
|
||||
}
|
||||
.lineage-main-card {
|
||||
display: block !important;
|
||||
visibility: visible !important;
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
.back-to-game-btn {
|
||||
bottom: calc(env(safe-area-inset-bottom, 0px) + 62px);
|
||||
right: max(10px, env(safe-area-inset-right, 0px));
|
||||
}
|
||||
}
|
||||
|
||||
/* v39.16.40: selected tool descriptions appear at the top on touch devices. */
|
||||
body.touch-mobile-ui .tool-tip-popover.mobile-selection-tip {
|
||||
top: calc(env(safe-area-inset-top, 0px) + 8px) !important;
|
||||
left: 50% !important;
|
||||
transform: translateX(-50%);
|
||||
width: min(680px, calc(100vw - 18px));
|
||||
max-width: calc(100vw - 18px);
|
||||
padding: 10px 12px;
|
||||
border-radius: 12px;
|
||||
background: rgba(46,38,30,.90);
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
text-align: left;
|
||||
}
|
||||
@media (max-width: 980px) {
|
||||
.top-actions { overflow: visible; }
|
||||
.top-menu { flex: 0 0 auto; }
|
||||
.top-menu-panel { position: fixed; top: calc(env(safe-area-inset-top, 0px) + 58px); right: max(10px, env(safe-area-inset-right, 0px)); }
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.top-panel-search {
|
||||
flex: 0 0 220px;
|
||||
width: 220px;
|
||||
min-width: 220px;
|
||||
}
|
||||
.top-panel-search .panel-search-input { height: 34px; }
|
||||
}
|
||||
@media (max-width: 980px) {
|
||||
.top-actions { overflow-x: auto; overflow-y: hidden; }
|
||||
}
|
||||
|
||||
/* Mobile toolbar: keep every primary action reachable without hidden horizontal overflow. */
|
||||
@media (max-width: 520px) {
|
||||
.top-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
overflow: visible;
|
||||
align-items: center;
|
||||
}
|
||||
.top-panel-search {
|
||||
flex: 1 1 100%;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
.top-actions > .btn,
|
||||
.top-actions > .top-menu,
|
||||
.top-actions > .speed-menu {
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
}
|
||||
.tool[data-tool="freeze"]::after { content: "🧊"; background: none; font-size: 52px; right: -10px; left: auto; opacity: 0.08; display:flex; align-items:center; justify-content:flex-end; }
|
||||
|
|
|
|||
382
css/panel.css
|
|
@ -1,9 +1,4 @@
|
|||
/* CSS split file; preserve manifest load order. */
|
||||
.lineage-tree {
|
||||
overflow: auto;
|
||||
padding: 8px 10px 18px;
|
||||
contain: content;
|
||||
}
|
||||
|
||||
.lineage-main-card {
|
||||
margin-top: 16px;
|
||||
|
|
@ -24,6 +19,14 @@
|
|||
}
|
||||
|
||||
.lineage-head h2 { margin-bottom: 0; }
|
||||
.lineage-title-note {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: rgba(126, 86, 52, 0.78);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.lineage-main-content {
|
||||
height: min(78vh, 980px);
|
||||
|
|
@ -44,11 +47,6 @@
|
|||
gap: 12px;
|
||||
}
|
||||
|
||||
.lineage-main-content .lineage-tree {
|
||||
min-width: 360px;
|
||||
overflow: visible;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
|
||||
body::before {
|
||||
|
|
@ -101,6 +99,7 @@ body::before {
|
|||
box-shadow: 0 3px 8px rgba(64,48,32,0.045);
|
||||
overflow: hidden;
|
||||
isolation: isolate;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
.family-node.child {
|
||||
border-color: rgba(103, 178, 44, 0.38);
|
||||
|
|
@ -119,6 +118,52 @@ body::before {
|
|||
z-index: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
.family-death-time {
|
||||
display: block;
|
||||
margin-top: 1px;
|
||||
font-size: 10px;
|
||||
line-height: 1.15;
|
||||
color: rgba(93, 72, 61, 0.76);
|
||||
}
|
||||
.family-genetic-stats {
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 1px 5px;
|
||||
margin-top: 2px;
|
||||
font-size: 9.7px;
|
||||
line-height: 1.13;
|
||||
color: rgba(88, 70, 48, 0.86);
|
||||
letter-spacing: -0.02em;
|
||||
white-space: normal;
|
||||
max-width: 82px;
|
||||
}
|
||||
.family-genetic-stats span {
|
||||
display: inline-flex;
|
||||
gap: 1px;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.family-genetic-stats b {
|
||||
font-weight: 800;
|
||||
color: rgba(120, 86, 30, 0.92);
|
||||
}
|
||||
.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);
|
||||
}
|
||||
.lineage-family-tree .tree-node.champion::before {
|
||||
content: "\265B ";
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
top: 4px;
|
||||
z-index: 2;
|
||||
font-size: 14px;
|
||||
color: rgba(158, 102, 20, 0.95);
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,0.70);
|
||||
}
|
||||
|
||||
.family-node.alive .family-portrait { opacity: 0.80; }
|
||||
.family-node.dead .family-portrait { opacity: 0.20; }
|
||||
.family-portrait img {
|
||||
|
|
@ -134,141 +179,10 @@ body::before {
|
|||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.family-copy .death-reason {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1.12;
|
||||
margin-top: 1px;
|
||||
color: rgba(126, 80, 64, 0.92);
|
||||
}
|
||||
.family-personality-tags {
|
||||
display: flex !important;
|
||||
overflow: visible !important;
|
||||
text-overflow: clip !important;
|
||||
flex-wrap: wrap;
|
||||
gap: 2px;
|
||||
margin-top: 2px;
|
||||
white-space: normal !important;
|
||||
overflow: visible;
|
||||
max-height: none;
|
||||
}
|
||||
.family-personality-tags em {
|
||||
display: inline-block;
|
||||
max-width: none;
|
||||
padding: 1px 4px;
|
||||
border-radius: 999px;
|
||||
background: rgba(103,178,44,0.11);
|
||||
border: 1px solid rgba(103,178,44,0.20);
|
||||
color: rgba(64,72,48,0.86);
|
||||
font-size: 7.5px;
|
||||
font-style: normal;
|
||||
line-height: 1.25;
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.family-copy strong { font-size: 11px; }
|
||||
.family-copy span { color: var(--muted); font-size: 8.5px; margin-top: 1px; }
|
||||
.tool[data-tip] { position: relative; }
|
||||
|
||||
|
||||
.colony-card { overflow: visible; min-height: 0; flex: 0 0 auto; }
|
||||
.colony-stats {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.colony-chart {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 188px;
|
||||
min-height: 188px;
|
||||
border: 1px solid rgba(84, 68, 48, 0.14);
|
||||
border-radius: 14px;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,0.70), rgba(246,240,228,0.54));
|
||||
overflow: hidden;
|
||||
}
|
||||
.colony-card h2 { line-height: 1.45; margin-bottom: 10px; }
|
||||
.colony-chart svg { display: block; width: 100%; height: 100%; }
|
||||
.colony-chart-legend {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px 8px;
|
||||
align-items: center;
|
||||
margin: 4px 0 8px;
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
line-height: 1.25;
|
||||
}
|
||||
.colony-chart-legend span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.colony-chart-legend i {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
display: inline-block;
|
||||
box-shadow: 0 0 0 1px rgba(70, 52, 36, 0.10);
|
||||
}
|
||||
|
||||
.relation-row strong { display: inline-flex; align-items: center; justify-content: flex-end; gap: 6px; }
|
||||
.relation-face {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
object-fit: contain;
|
||||
border-radius: 999px;
|
||||
background: rgba(255,255,255,0.66);
|
||||
border: 1px solid rgba(84,68,48,0.12);
|
||||
vertical-align: middle;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
|
||||
.lineage-family {
|
||||
padding: 10px;
|
||||
margin: 0 0 12px;
|
||||
border-radius: 15px;
|
||||
background: rgba(255,253,248,0.62);
|
||||
border: 1px solid rgba(84,68,48,0.12);
|
||||
}
|
||||
.lineage-family h3 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 13px;
|
||||
color: rgba(64,52,38,0.86);
|
||||
}
|
||||
/* Family archive: actual top-down tree diagram. */
|
||||
.lineage-family-tree {
|
||||
overflow-x: auto;
|
||||
overflow-y: visible;
|
||||
padding: 10px 10px 22px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overscroll-behavior-x: contain;
|
||||
}
|
||||
.lineage-family-tree h3 {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 3;
|
||||
width: max-content;
|
||||
padding-right: 12px;
|
||||
background: rgba(255,253,248,0.82);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.lineage-tree-hint {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 3;
|
||||
width: max-content;
|
||||
max-width: 100%;
|
||||
margin: -2px 0 8px;
|
||||
padding: 5px 8px;
|
||||
border-radius: 9px;
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
background: rgba(255,255,255,0.62);
|
||||
border: 1px solid rgba(84,68,48,0.08);
|
||||
}
|
||||
.lineage-family-tree .lineage-tree-panel {
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
|
|
@ -292,7 +206,6 @@ body::before {
|
|||
z-index: 1;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.lineage-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -305,7 +218,6 @@ body::before {
|
|||
border-color: rgba(103,178,44,0.46);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.lineage-render-status {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
|
|
@ -355,9 +267,9 @@ body::before {
|
|||
}
|
||||
.lineage-family-tree .tree-node {
|
||||
position: absolute;
|
||||
width: 132px;
|
||||
height: 84px;
|
||||
min-height: 84px;
|
||||
width: 150px;
|
||||
height: 112px;
|
||||
min-height: 112px;
|
||||
box-sizing: border-box;
|
||||
align-items: flex-start;
|
||||
background: linear-gradient(180deg, #fffdf8, #f8f1e5);
|
||||
|
|
@ -376,11 +288,35 @@ body::before {
|
|||
.lineage-family-tree .tree-node[data-family-tarinai-id]:hover {
|
||||
transform: none;
|
||||
}
|
||||
.lineage-family-tree.family-hover-active .tree-node.family-hover-dim {
|
||||
opacity: 0.28;
|
||||
filter: saturate(0.58);
|
||||
transition: opacity .12s ease, filter .12s ease, box-shadow .12s ease, border-color .12s ease;
|
||||
}
|
||||
.lineage-family-tree.family-hover-active .tree-node.family-hover-focus,
|
||||
.lineage-family-tree.family-hover-active .tree-node.family-hover-parent,
|
||||
.lineage-family-tree.family-hover-active .tree-node.family-hover-child {
|
||||
opacity: 1;
|
||||
filter: none;
|
||||
}
|
||||
.lineage-family-tree.family-hover-active .tree-node.family-hover-focus {
|
||||
border-color: rgba(124, 165, 66, 0.78);
|
||||
box-shadow: 0 0 0 3px rgba(124, 165, 66, 0.16), 0 5px 13px rgba(64,48,32,0.10);
|
||||
}
|
||||
.lineage-family-tree.family-hover-active .tree-node.family-hover-parent {
|
||||
border-color: rgba(80, 132, 198, 0.72);
|
||||
box-shadow: 0 0 0 2px rgba(80, 132, 198, 0.14);
|
||||
}
|
||||
.lineage-family-tree.family-hover-active .tree-node.family-hover-child {
|
||||
border-color: rgba(87, 170, 91, 0.72);
|
||||
box-shadow: 0 0 0 2px rgba(87, 170, 91, 0.14);
|
||||
}
|
||||
.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;
|
||||
|
|
@ -388,7 +324,7 @@ body::before {
|
|||
width: 54px;
|
||||
padding: 3px 0;
|
||||
border-radius: 999px;
|
||||
font-size: 10px;
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
text-align: center;
|
||||
background: rgba(255,255,255,0.70);
|
||||
|
|
@ -403,13 +339,6 @@ body::before {
|
|||
border-radius: 9px;
|
||||
background: rgba(255,255,255,0.70);
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.ecology-grid { grid-template-columns: 1fr; }
|
||||
.ecology-card { grid-template-columns: 68px minmax(0, 1fr); }
|
||||
.ecology-card img { width: 68px; height: 68px; }
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.lineage-main-content {
|
||||
height: 70vh;
|
||||
|
|
@ -418,5 +347,138 @@ body::before {
|
|||
}
|
||||
}
|
||||
|
||||
/* Restored from the original ZIP: fixed colony graph/legend sizing. */
|
||||
.colony-card { overflow: visible; min-height: 0; flex: 0 0 auto; }
|
||||
.colony-stats {
|
||||
grid-template-columns: 1fr;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.colony-chart {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 188px;
|
||||
min-height: 188px;
|
||||
border: 1px solid rgba(84, 68, 48, 0.14);
|
||||
border-radius: 14px;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,0.70), rgba(246,240,228,0.54));
|
||||
overflow: hidden;
|
||||
}
|
||||
.colony-card h2 { line-height: 1.45; margin-bottom: 10px; }
|
||||
.colony-chart svg { display: block; width: 100%; height: 100%; }
|
||||
.colony-chart-legend {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px 8px;
|
||||
align-items: center;
|
||||
margin: 4px 0 8px;
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
line-height: 1.25;
|
||||
}
|
||||
.colony-chart-legend .chart-series-toggle {
|
||||
appearance: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
padding: 2px 4px;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
line-height: inherit;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
transition: opacity 120ms ease, background-color 120ms ease, transform 120ms ease;
|
||||
}
|
||||
.colony-chart-legend .chart-series-toggle:hover { background: rgba(255,255,255,0.56); }
|
||||
.colony-chart-legend .chart-series-toggle:focus-visible { outline: 2px solid rgba(103,178,44,0.58); outline-offset: 1px; }
|
||||
.colony-chart-legend .chart-series-toggle:active { transform: translateY(1px); }
|
||||
.colony-chart-legend .chart-series-toggle i {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
flex: 0 0 10px;
|
||||
border-radius: 999px;
|
||||
display: inline-block;
|
||||
box-shadow: 0 0 0 1px rgba(70, 52, 36, 0.10);
|
||||
}
|
||||
.colony-chart-legend .series-hidden { opacity: 0.42; text-decoration: line-through; }
|
||||
.colony-chart-legend .series-hidden i { background: transparent !important; box-shadow: inset 0 0 0 2px currentColor; }
|
||||
|
||||
|
||||
/* Family tree personality tags: consolidated final cascade. */
|
||||
.family-personality-tags {
|
||||
box-sizing: border-box !important;
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
|
||||
grid-auto-rows: 13px !important;
|
||||
gap: 2px 3px !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
height: 28px !important;
|
||||
max-height: 28px !important;
|
||||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
justify-self: stretch !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
.family-personality-tags em {
|
||||
box-sizing: border-box !important;
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
min-width: 0 !important;
|
||||
max-width: 100% !important;
|
||||
height: 13px !important;
|
||||
padding: 1px 2px !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
white-space: nowrap !important;
|
||||
line-height: 11px !important;
|
||||
font-size: 7.2px !important;
|
||||
font-style: normal;
|
||||
letter-spacing: -0.08em !important;
|
||||
text-align: center !important;
|
||||
border-radius: 999px;
|
||||
background: rgba(103,178,44,0.11);
|
||||
border: 1px solid rgba(103,178,44,0.20);
|
||||
color: rgba(64,72,48,0.86);
|
||||
}
|
||||
.lineage-family-tree .tree-node .family-copy {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
position: static !important;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
gap: 1px !important;
|
||||
}
|
||||
.lineage-family-tree .tree-node .family-copy strong,
|
||||
.lineage-family-tree .tree-node .family-copy > span:not(.family-personality-tags) {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
.lineage-family-tree .tree-node .family-copy > .family-personality-tags {
|
||||
position: static !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
height: 24px !important;
|
||||
max-height: 24px !important;
|
||||
margin: 2px 0 0 !important;
|
||||
grid-auto-rows: 11px !important;
|
||||
gap: 1px 3px !important;
|
||||
z-index: 3 !important;
|
||||
}
|
||||
.lineage-family-tree .tree-node .family-personality-tags em {
|
||||
height: 11px !important;
|
||||
line-height: 9px !important;
|
||||
font-size: 6.8px !important;
|
||||
}
|
||||
|
||||
.colony-chart { position: relative; }
|
||||
.colony-chart-tooltip { position: fixed; z-index: 10020; pointer-events: none; min-width: 112px; max-width: 210px; padding: 7px 8px; border-radius: 9px; border: 1px solid rgba(80,65,48,0.18); background: rgba(255,252,245,0.98); color: #4d443a; box-shadow: 0 4px 14px rgba(64,48,30,0.16); font-size: 10px; line-height: 1.45; transform: none; }
|
||||
.colony-chart-tooltip.hidden { display: none; }
|
||||
.colony-chart-tooltip strong { display: block; margin-bottom: 3px; font-size: 10.5px; }
|
||||
.colony-chart-tooltip-row { display: flex; justify-content: space-between; gap: 10px; white-space: nowrap; }
|
||||
.colony-chart-hover-point { position: absolute; z-index: 5; width: 8px; height: 8px; border: 2px solid #5d8ee6; border-radius: 50%; background: rgba(255,255,255,0.96); box-shadow: 0 0 0 2px rgba(255,255,255,0.7); pointer-events: none; transform: translate(-50%, -50%); }
|
||||
.colony-chart-hover-point.hidden { display: none; }
|
||||
|
||||
.tool[data-tip] { position: relative; }
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
# Architecture
|
||||
|
||||
Build target: static browser page. No bundler. Globals and ordered deferred scripts are intentional. Current inspected version: `15.20.16`.
|
||||
|
||||
## Load order
|
||||
|
||||
`app_manifest.json` owns CSS/JS order. Regenerate `index.html` and `service-worker.js` with `python3 scripts/generate_app_files.py` after version/order changes. `js/version.js` exposes `TARINAI_APP.version`, cache name, and query helper.
|
||||
|
||||
The old `css/styles.css` compatibility shim and `js/ui_family.js` split marker have been removed. The family tree entry point now starts at `ui_family_data.js` and continues through async/layout/path/render modules.
|
||||
|
||||
## Runtime objects
|
||||
|
||||
- `World`: constructor in `js/world.js`; methods split into view, family/social, combat/effects, environment, spatial/budget, ants, update, placement/log, bootstrap.
|
||||
- `Tarinai`: constructor in `js/tarinai.js`; methods split into identity/social, disease/nest, item effects, needs/items, social/move/life, render.
|
||||
- `Item`: item state, lifecycle, scaling, and field rendering in `js/items.js`.
|
||||
- `Effect`: transient visuals in `js/sim_core.js`; combat/weather/food effects are spawned through `World` helpers.
|
||||
- `TarinaiEvents`: global event bus for logs, terrain dirtiness, audio failures, phase/performance changes.
|
||||
- `TarinaiPerformance`: FPS quality manager; provides effect caps, ant stride, terrain redraw cadence, UI intervals.
|
||||
- `TarinaiInputMode`: input classification and mobile-mode state. UI code reads this manager instead of duplicating touch heuristics.
|
||||
- `TarinaiWeatherSystem`: weather transitions and rain-spawned water item creation. Rain overlay drawing remains in `render.js`.
|
||||
|
||||
## Startup path
|
||||
|
||||
`main.js` owns the user-visible boot sequence:
|
||||
|
||||
1. Start the loading showcase and preload the initial sprite/image set through `loadImages`.
|
||||
2. Bind UI, apply the default `garden` field layout, reset the world, and select `observe`.
|
||||
3. Render logs, family archive, first frame, and stats.
|
||||
4. Start deferred background image loading, enter `requestAnimationFrame(loop)`, and hide the loading screen.
|
||||
5. Register `service-worker.js` with `updateViaCache: "none"` after window load.
|
||||
|
||||
## Data ownership
|
||||
|
||||
- `js/data.js`: base item/tool definitions, `TOOL_CATEGORIES`, config, image metrics, field presets.
|
||||
- `js/item_visual_registry.js`: canonical item visual descriptors used by field drawing and generated tool-icon previews.
|
||||
- `js/food_registry.js`: food/medicine semantics such as labels, serving classes, decay multipliers, and hygiene penalties.
|
||||
- `js/disease_registry.js`: disease labels, flags, display metadata, cure-rule notes, and mystery-drug eligibility.
|
||||
- `js/input_mode_manager.js`: touch/mobile classification, mobile mode persistence, and gesture capability policy.
|
||||
- `js/weather_system.js`: weather state transitions and rain-water item spawning.
|
||||
- `js/text_catalog.js`: Japanese UI copy/tooltips. Keep JS strings escaped where existing style uses escapes.
|
||||
- `js/effect_registry.js`: item-effect labels, colors, modifiers, exclusivity/removal metadata, behavior callbacks (`onApply`, `onTick`, `onRemove`), direct eating context, and random-effect candidates.
|
||||
- `js/sound_pack.js`: canonical sound metadata: IDs, categories, gain multipliers, cooldowns, optional sample paths, and ID-to-category mapping.
|
||||
- `FIELD_TYPES` in `js/data.js`: cage/garden/park field labels, responsive heights, world scale, starting population, and initial grass.
|
||||
|
||||
## State loops
|
||||
|
||||
`main.js` starts asset preload, world bootstrap, animation loop, deferred image loading, service-worker registration. `World.update(dt)` advances time, weather, items, ants, Tarinai, effects, budgets, terrain dirtiness. `render()` draws cached background/terrain, visible entities, weather overlay, cards, HUD, and debug overlays.
|
||||
|
||||
World time is `CONFIG.dayLength` seconds per day. Version `15.20.16` uses 120 seconds/day and weather changes every 46-96 seconds. The phase boundary rules live in `World.phaseName()`.
|
||||
|
||||
Domain events are emitted at central mutation points: placement, eating, decay, effect apply/remove, disease infect/cure, damage/death/birth, fight hit/loss/end, ant spawn/grab/carry/death, weather changes, and rain item drops. Logs/audio still use direct calls where they already existed; do not add duplicate log or sound side effects when subscribing to these events.
|
||||
|
||||
Ant behavior remains intentionally conservative. `ANT_EXTENSION_HOOKS` names future ecology slots for nest hunger, nest growth, queen reproduction, worker lifecycle, carrying success, and territorial behavior, but this pass does not rebalance those mechanics.
|
||||
|
||||
## Spatial/performance rules
|
||||
|
||||
Use `World.nearby*` helpers rather than full-array scans in hot paths. Terrain cache uses dirty/version flags; do not reintroduce every-frame full terrain hashing. Performance degradation should be centralized in `performance_manager.js`.
|
||||
|
||||
Family tree rendering is intentionally asynchronous and cached. `ui_family_async.js` chunks component discovery with `requestIdleCallback` when available; `ui_family_render.js` hydrates cached HTML, keeps archive dirty/version state, and exposes `validateFamilyTree()` plus `familyTreeDiagnostics()` for browser-console checks.
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
# Content and visual guide
|
||||
|
||||
## Visual source of truth
|
||||
|
||||
Do not invent tool-UI-only item designs. `js/item_visual_registry.js` is the canonical descriptor table for drift-prone item visuals; field drawing in `js/items.js` and generated previews must consume the same concepts. Tool icons in `assets/ui/tool_*.webp` are generated previews and must match field rendering. Update `scripts/generate_item_icons.py` when changing item helper silhouettes; regenerate icons before shipping.
|
||||
|
||||
Canonical helper mapping:
|
||||
|
||||
- Weather rain overlay: `drawRain` in `render.js`; diagonal blue line streaks only, never item droplets.
|
||||
- Horizontal item droplet: `drawOvalWaterDropSprite`; used by fallen water items, zunda juice, mercury. Same silhouette; recolor only.
|
||||
- Water bowl: `water_bowl` descriptor in `js/item_visual_registry.js`; bowl and inner horizontal water palette are registry-owned.
|
||||
- Sleep drug: `drawSleepTabletSprite`.
|
||||
- Laxative: `drawLaxativeTabletSprite`; zunchi-motif tablet.
|
||||
- Mystery drug: `drawMysteryTabletSprite`; question-mark tablet.
|
||||
- Ammo: `drawBulletSprite`; bullet silhouette.
|
||||
- Protein/niteropu: powder-pile rendering in the parameter-item branch.
|
||||
|
||||
## Tool groups
|
||||
|
||||
`TOOL_CATEGORIES` in `js/data.js` owns the runtime-generated tool palette. CSS colors/icons are tied to `data-tool-category` and `data-tool` values:
|
||||
|
||||
- Operation/observation: observe, delete, poke, pinch, new, water hose.
|
||||
- Food/water/grass: zunda mochi, love mochi, fight mochi, grass, water bowl, zunda juice.
|
||||
- Medicine/effects: sleep drug, laxative, protein, niteropu, ammo, mystery drug, mercury, giant drug, dwarf drug.
|
||||
- Habitat/living things: zunchi, bed, nest box, ant nest, ball.
|
||||
- Hazards/obstacles: stone, genkotsu, firecracker, vertical fence, horizontal fence.
|
||||
|
||||
When adding or renaming a tool, update `TOOL_DEFINITIONS`, `TOOL_CATEGORIES`, `TEXT_CATALOG.toolTips`, CSS icon mapping, generated `assets/ui/tool_*.webp`, and docs. Do not duplicate category membership in `index.html`.
|
||||
|
||||
## Gameplay data registries
|
||||
|
||||
- Item effects: `TarinaiEffectRegistry` owns labels, display colors, modifiers, exclusivity/removal metadata, behavior callbacks, and mystery-drug candidates. Tarinai item-effect methods are the integration layer and should route apply/tick/remove behavior through the registry.
|
||||
- Diseases: `TarinaiDiseaseRegistry` owns disease flags, labels, symptoms/display text, base infection chances, cure-rule notes, and mystery-drug eligibility.
|
||||
- Food: `TarinaiFoodRegistry` owns labels, serving classes, nutrition, hunger relief, decay multipliers, hygiene penalties, and special effect IDs. Serving-food eating paths must read nutrition and hunger relief from this registry.
|
||||
- Weather: `TarinaiWeatherSystem` owns weather transitions and rain-spawned water items. Rain visuals are diagonal line streaks only. Rain-spawned water items, water bowls, zunda juice, and mercury are separate item/object concepts.
|
||||
- Ants: `ANT_EXTENSION_HOOKS` documents future ecology slots only. Do not change worker count, queen founding, carrying, or nest removal balance as part of registry/event cleanup.
|
||||
|
||||
## UI copy and labels
|
||||
|
||||
Most visible UI remains Japanese. Keep user-facing naming consistent: the former lifestyle chart label is now the mental-state label; selected Tarinai combat summary shows fight win rate, not cumulative defeats. The selected Tarinai data panel must show both daily personality-change causes and the rolling `recentChangeCauses` history.
|
||||
|
||||
## Bubble text inventory
|
||||
|
||||
Active Tarinai/world bubble text classes:
|
||||
|
||||
- Symbols: `!`, `?`, `♡`, `...`.
|
||||
- Sleep: `Zzz...`.
|
||||
- Eating/poop: short munching and poop sounds.
|
||||
- Fear/fight: short panic, shout, and breathing sounds.
|
||||
- Disease/recovery: short weak or relief sounds.
|
||||
- Random small voice: short Tarinai voice variants.
|
||||
- Item effects: `???`.
|
||||
|
||||
When adding bubbles, keep them short; every bubble may also trigger `audio.bubble`.
|
||||
|
||||
## Audio
|
||||
|
||||
Sound settings are in the top-right sound menu, not inside Event UI. Categories: Tarinai voice, notification, operation. `js/sound_pack.js` is the canonical metadata source for sound IDs, categories, gains, cooldowns, sample paths, and ID mapping; `js/audio.js` is the playback engine. Missing samples fall back to synthetic audio. Notification and operation sounds are intentionally louder than voice sounds.
|
||||
|
||||
The Event card owns in-page push toast settings for log kinds: death, accident, birth, fight, food, weather, grass, relation. Those settings are separate from audio categories and persist through localStorage.
|
||||
|
||||
## Ecology and credits
|
||||
|
||||
Ecology dialog card copy and images come from `TEXT_CATALOG.ecologyCards`; image paths should use real assets or generated WebP assets. Credits are hardcoded in `index.html`; update them when adding new external media sources.
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
# Maintenance
|
||||
|
||||
## Release process
|
||||
|
||||
1. Edit source files.
|
||||
2. Bump `app_manifest.json` and `js/version.js` to the same version.
|
||||
3. Regenerate static references: `python3 scripts/generate_app_files.py`.
|
||||
4. Regenerate item icons if item visuals changed: `python3 scripts/generate_item_icons.py`.
|
||||
5. Check versions: no stale `v=<old>` in `index.html`, CSS icon URLs, `service-worker.js`.
|
||||
6. Validate syntax: `node --check js/*.js service-worker.js`. If Node is not installed on the local machine, document that explicitly in the handoff and use browser-console smoke checks instead.
|
||||
7. Open `index.html` in a browser and verify startup, reset field selection, one tool placement, observe selection, family tree render, sound menu toggles, and `?debug=1`.
|
||||
8. Zip from project root and run `zip -T`.
|
||||
|
||||
If `scripts/generate_app_files.py` cannot run locally, manually keep these in sync: `app_manifest.json`, `index.html` script/link tags, and `service-worker.js` `coreScriptNames`/CSS cache entries.
|
||||
|
||||
## Cache rules
|
||||
|
||||
`service-worker.js` must remain no-cache/no-store in `.htaccess`; versioned JS/CSS/WebP can be immutable. Register service worker with `updateViaCache: "none"`.
|
||||
|
||||
## Debugging
|
||||
|
||||
Use `?debug=1` for FPS, performance level, counts, cache/SW state, last audio ID, effect registry count, terrain/spatial version, and recent event-bus history.
|
||||
|
||||
Useful browser-console checks:
|
||||
|
||||
- `TARINAI_VERSION` and `TARINAI_APP.cacheName` confirm version/cache naming.
|
||||
- `world.fieldType`, `world.tarinai.length`, `world.items.length`, and `world.ants.length` confirm the active simulation shape.
|
||||
- `TarinaiEffectRegistry.debugSnapshot()` confirms registered item effects.
|
||||
- `TarinaiDiseaseRegistry.debugSnapshot()` confirms disease metadata.
|
||||
- `TarinaiFoodRegistry.debugSnapshot()` confirms food semantics.
|
||||
- `toolCategories().map(c => c.id)` confirms the runtime tool palette registry.
|
||||
- `TarinaiInputMode.snapshot()` confirms mobile classification and mode policy.
|
||||
- `TarinaiEvents.recent(null, 20).map(e => e.type)` confirms domain-event emission.
|
||||
- `validateFamilyTree()` checks reciprocal family links and rendered family tree node/link consistency.
|
||||
- `familyTreeDiagnostics()` reports the latest async archive render state.
|
||||
|
||||
## Mobile input
|
||||
|
||||
The mobile mode controls are owned by `TarinaiInputMode` and are only for compact/no-hover/coarse-pointer touch-first devices. Desktop browsers, including touch-screen PCs, should not show the mobile controls unless the viewport or UA also indicates mobile/tablet.
|
||||
|
||||
The mode state is stored as `tarinai_mobile_input_mode_v1`; sound settings use `tarinai_audio_settings_v2`; log push settings use `tarinai_log_push_settings_v2`.
|
||||
|
||||
## Prompt archive
|
||||
|
||||
`docs/prompt.txt` is external user material. Do not rewrite, translate, trim, or delete it.
|
||||
466
docs/prompt.txt
|
|
@ -1,466 +0,0 @@
|
|||
You are working on the browser game "たりないコロニー" based on the current ZIP/project state, assumed to be v15.20.3.
|
||||
|
||||
Goal: resolve the remaining internal design confusion and underdeveloped systems, while preserving existing gameplay unless explicitly requested below.
|
||||
|
||||
Hard exclusions:
|
||||
|
||||
* Do NOT implement field-size balance tuning. Specifically do not implement the previously proposed "field-specific balance correction" work.
|
||||
* Do NOT implement save/load.
|
||||
* Do NOT implement debug scenario modes.
|
||||
* Do NOT redesign or expand the zunchi system. Keep zunchi mechanics and general design as-is, except where existing code must be connected through registries/events without changing behavior.
|
||||
* Do NOT modify `docs/prompt.txt` if present.
|
||||
* Do not introduce a build toolchain requirement. Plain static HTML/CSS/JS must still run directly.
|
||||
|
||||
Primary requirements:
|
||||
|
||||
1. Make item visuals single-source-of-truth.
|
||||
|
||||
* Current problem: field item rendering, tool UI icons, generated WebP icons, documentation, and item definitions can diverge.
|
||||
* Establish one canonical visual definition per item/tool.
|
||||
* Field rendering must be the primary visual source, or both field rendering and UI preview must derive from the same canonical visual descriptor.
|
||||
* The tool UI must never have an independently invented appearance.
|
||||
* Create or consolidate a visual registry, for example `js/item_visual_registry.js`, with descriptors such as:
|
||||
|
||||
* `renderer`
|
||||
* `shape`
|
||||
* `palette`
|
||||
* `scale`
|
||||
* `uiPreviewScale`
|
||||
* `fieldScale`
|
||||
* `accessibleShapeTraits`
|
||||
* optional `emojiFallback`
|
||||
* Update field rendering and UI icon generation/preview so they share this registry.
|
||||
* Fix visual drift for all known drift-prone items:
|
||||
|
||||
* ammo
|
||||
* mystery drug
|
||||
* laxative
|
||||
* sleeping drug
|
||||
* protein
|
||||
* nietorop
|
||||
* giant drug
|
||||
* dwarf drug
|
||||
* zunda juice
|
||||
* mercury
|
||||
* rain-water item
|
||||
* ball
|
||||
* Keep the rule in docs: UI icon is a preview of the field visual, not a separate design.
|
||||
* Ensure scalable items correctly respond to small/medium/large placement size.
|
||||
|
||||
2. Data-drive tool categories.
|
||||
|
||||
* Current problem: tool category membership/order/color is partly hardcoded in HTML/CSS and not owned by tool data.
|
||||
* Move tool categories into a data registry, for example `js/tool_registry.js` or `js/data.js`.
|
||||
* The registry must define:
|
||||
|
||||
* category id
|
||||
* Japanese label
|
||||
* color/theme class
|
||||
* sort order
|
||||
* tool ids included in that category
|
||||
* Generate/build the tool UI from this registry at runtime.
|
||||
* Keep collapsible category behavior.
|
||||
* Keep simple category color coding.
|
||||
* Remove duplicated static category definitions from `index.html` where possible.
|
||||
* Category changes must require editing only the registry, not HTML.
|
||||
|
||||
3. Convert EffectRegistry from display-only to real behavior ownership.
|
||||
|
||||
* Current problem: `effect_registry.js` mainly handles labels/display/random candidates, while actual behavior remains scattered in Tarinai/item code.
|
||||
* Promote `EffectRegistry` into the canonical owner for item effect metadata and behavior.
|
||||
* Each effect should define, where relevant:
|
||||
|
||||
* `id`
|
||||
* Japanese label
|
||||
* duration or permanent flag
|
||||
* exclusive group
|
||||
* stack behavior
|
||||
* display color
|
||||
* display summary
|
||||
* modifiers
|
||||
* `onApply(tarinai, context)`
|
||||
* `onTick(tarinai, dt, context)`
|
||||
* `onRemove(tarinai, context)`
|
||||
* visual effect id
|
||||
* bubble policy
|
||||
* whether it is eligible for "怪しい薬"
|
||||
* whether it is removed by "ずんだ汁"
|
||||
* Migrate these item effects into the registry:
|
||||
|
||||
* laxative
|
||||
* protein
|
||||
* nietorop
|
||||
* ammo
|
||||
* mystery drug
|
||||
* mercury
|
||||
* giant drug
|
||||
* dwarf drug
|
||||
* zunda juice clearing behavior
|
||||
* heko mochi timed effect
|
||||
* fight mochi timed effect
|
||||
* sleeping drug if it changes behavior
|
||||
* Existing semantics must remain:
|
||||
|
||||
* laxative: emits zunchi every 3 seconds during effect; eating does not reduce hunger because it immediately passes through.
|
||||
* protein: permanent; damage dealt x1.75; disease chance x0.5; red upward particles; mutually exclusive with nietorop; latest wins.
|
||||
* nietorop: permanent; damage dealt x0.5; disease chance x2; purple downward particles; mutually exclusive with protein; latest wins.
|
||||
* ammo: timed; walking speed x10; while moving, collision with Tarinai/ants knocks them away.
|
||||
* mystery drug: grants random two effects from behavior-changing item effects and diseases.
|
||||
* mercury: each consumption multiplicatively increases lifespan by x1.3.
|
||||
* giant drug: permanent; size x3; damage x2; speed x1.5; 1 damage/sec; mutually exclusive with dwarf drug; latest wins.
|
||||
* dwarf drug: permanent; size x0.25; damage x0.25; speed x0.5; 1 damage/sec; mutually exclusive with giant drug; latest wins.
|
||||
* zunda juice: removes all removable item effects.
|
||||
* "たりないデータ" must read active item effects from the registry, not hand-built scattered conditions.
|
||||
|
||||
4. Add DiseaseRegistry and connect disease behavior.
|
||||
|
||||
* Current problem: diseases are individual booleans/branches and are not uniformly represented.
|
||||
* Add `DiseaseRegistry` or equivalent.
|
||||
* It must define:
|
||||
|
||||
* disease id
|
||||
* Japanese label
|
||||
* symptoms/behavior flags
|
||||
* infection chance modifiers
|
||||
* cure rules
|
||||
* display text
|
||||
* visual/bubble policy
|
||||
* eligibility for mystery drug
|
||||
* Migrate these diseases into registry metadata while preserving behavior:
|
||||
|
||||
* zunchi disease
|
||||
* sleep disease
|
||||
* explosion disease
|
||||
* fight disease
|
||||
* Explosion disease infection probability must remain 15% where currently defined, with protein/nietorop disease multipliers applied to that base.
|
||||
* Mystery drug must sample diseases through this registry.
|
||||
* Zunda juice should remove item effects, not necessarily diseases, unless current semantics already remove a given disease; do not silently change disease cure rules.
|
||||
|
||||
5. Add FoodRegistry and centralize food semantics.
|
||||
|
||||
* Current problem: food interest, nutrition, decay, hygiene penalty, cures, and special exceptions are scattered.
|
||||
* Add `FoodRegistry`.
|
||||
* It must define:
|
||||
|
||||
* item id/type
|
||||
* Japanese label
|
||||
* nutrition
|
||||
* palatability/interest class
|
||||
* decay profile
|
||||
* hygiene penalty on decay
|
||||
* special effect id, if eating applies an effect
|
||||
* whether it counts as grass-like interest, zunda-mochi high interest, medicine-like low interest, water, corpse, etc.
|
||||
* Preserve existing behavior:
|
||||
|
||||
* zunda mochi remains highly attractive.
|
||||
* most non-zunda foods remain only slightly more interesting than grass.
|
||||
* all food naturally decays about twice per half-day, not continuously.
|
||||
* decay slightly reduces hygiene stats at those discrete decay ticks.
|
||||
* laxative effect prevents hunger reduction from eating.
|
||||
* `Tarinai` food-seeking logic must consult `FoodRegistry`.
|
||||
* Food decay logic must consult `FoodRegistry`.
|
||||
* Tooltips/descriptions should remain Japanese and current user-facing wording must not regress.
|
||||
|
||||
6. Separate WeatherSystem from water items.
|
||||
|
||||
* Current problem: rain visual effects and rain-spawned items/water-like items are conceptually mixed.
|
||||
* Create or isolate a `WeatherSystem`.
|
||||
* Explicitly separate:
|
||||
|
||||
* `rain_visual`: diagonal blue-ish streaks/lines falling at an angle.
|
||||
* `rain_water_item`: horizontal oval water item dropped by rain.
|
||||
* `water_bowl`: placed water object.
|
||||
* `zunda_juice`: green horizontal oval liquid item.
|
||||
* `mercury`: gray horizontal oval liquid item.
|
||||
* Do not reuse rain visual renderer for item droplets.
|
||||
* Do not render rain effect as oval droplets. Rain effect must be angled falling lines.
|
||||
* Do not render zunda juice or mercury with diagonal lines.
|
||||
* Preserve current rain gameplay behavior.
|
||||
* Add code comments documenting the distinction so future changes do not confuse them.
|
||||
|
||||
7. Apply event bus more broadly.
|
||||
|
||||
* Current problem: logs, sounds, notifications, stats, effects, and debug traces are still partly direct calls.
|
||||
* Use the existing event bus or strengthen it.
|
||||
* Add domain events for:
|
||||
|
||||
* `tool:placed`
|
||||
* `item:eaten`
|
||||
* `item:decayed`
|
||||
* `effect:applied`
|
||||
* `effect:removed`
|
||||
* `disease:infected`
|
||||
* `disease:cured`
|
||||
* `tarinai:damaged`
|
||||
* `tarinai:died`
|
||||
* `tarinai:born`
|
||||
* `fight:hit`
|
||||
* `fight:lost`
|
||||
* `fight:ended`
|
||||
* `ant:spawned`
|
||||
* `ant:grabbed`
|
||||
* `ant:carried`
|
||||
* `ant:died`
|
||||
* `weather:changed`
|
||||
* `rain:itemDropped`
|
||||
* Logs, push notifications, audio, stats, and debug history should subscribe to events where practical.
|
||||
* Do not rewrite everything destructively. Add event emission at central points first, then migrate obvious direct calls.
|
||||
* Avoid duplicate logs/sounds.
|
||||
|
||||
8. Sound system source-of-truth cleanup.
|
||||
|
||||
* Current problem: `sound_pack.js` and `audio.js` both contain overlapping settings.
|
||||
* Make `sound_pack.js` the canonical metadata source for:
|
||||
|
||||
* sound id
|
||||
* category
|
||||
* volume multiplier
|
||||
* cooldown
|
||||
* sample path
|
||||
* fallback synthesis profile
|
||||
* `audio.js` should be the playback engine only:
|
||||
|
||||
* loading samples
|
||||
* fallback synthesis
|
||||
* category enable/disable
|
||||
* master gain
|
||||
* cooldown enforcement
|
||||
* concurrent voice limiting
|
||||
* Keep the current user settings:
|
||||
|
||||
* master effect sound on/off
|
||||
* Tarinai voice
|
||||
* notification
|
||||
* operation
|
||||
* Do not regress volume. The previous issue was that only the first sound was loud and subsequent sounds became too quiet. Preserve the fixed master gain behavior.
|
||||
* Keep `?debug=1` showing recent sound IDs and sound-pack version.
|
||||
|
||||
9. Mobile input manager.
|
||||
|
||||
* Current problem: mobile operation UI has appeared on PC repeatedly.
|
||||
* Add a single `InputModeManager` or equivalent.
|
||||
* It must own:
|
||||
|
||||
* `isTouchDevice`
|
||||
* `isCompactViewport`
|
||||
* `hasHover`
|
||||
* `pointerCoarse`
|
||||
* `userAgentMobile`
|
||||
* `currentMode`
|
||||
* `gestureState`
|
||||
* whether canvas can pan
|
||||
* whether canvas can place tools
|
||||
* whether family tree is allowed to capture touch
|
||||
* Mobile operation UI must appear only when the device is clearly touch-first:
|
||||
|
||||
* coarse pointer
|
||||
* no hover
|
||||
* real touch support
|
||||
* and either compact viewport or mobile/tablet UA
|
||||
* Desktop PCs with touch screens must not automatically show the mobile UI unless the viewport/UA also indicates mobile/tablet.
|
||||
* Add debug display of the input classification under `?debug=1`.
|
||||
* Keep existing modes:
|
||||
|
||||
* auto
|
||||
* camera
|
||||
* tool
|
||||
* family tree
|
||||
|
||||
10. Ant system cleanup and extension hooks, without broad balance changes.
|
||||
|
||||
* Do not create a full new ant ecology balance pass.
|
||||
* Do consolidate ant domain events and state names.
|
||||
* Preserve existing behavior:
|
||||
|
||||
* ants can carry sleeping Tarinai.
|
||||
* sleeping Tarinai do not wake just because ants carry them.
|
||||
* sleeping Tarinai do not counterattack ants while asleep.
|
||||
* ants without nests either join another available nest or wander.
|
||||
* empty nests are removed.
|
||||
* queen ants are visually larger.
|
||||
* Add clear extension hooks or comments for future ant ecology:
|
||||
|
||||
* nest hunger
|
||||
* nest growth
|
||||
* queen reproduction
|
||||
* worker lifecycle
|
||||
* carrying success rate
|
||||
* territorial behavior
|
||||
* Do not implement those future mechanics now unless already present.
|
||||
|
||||
11. Family tree: add personality tags.
|
||||
|
||||
* The family tree currently records lineage but should expose personality more directly.
|
||||
* Add compact personality tags to each Tarinai node/card in the family tree.
|
||||
* Use existing personality axes:
|
||||
|
||||
* aggression
|
||||
* openness
|
||||
* sociability
|
||||
* neuroticism
|
||||
* Tags should be short Japanese labels, for example:
|
||||
|
||||
* aggressive/high aggression
|
||||
* calm/low aggression
|
||||
* curious/high openness
|
||||
* cautious/low openness
|
||||
* social/high sociability
|
||||
* solitary/low sociability
|
||||
* nervous/high neuroticism
|
||||
* stable/low neuroticism
|
||||
* Use at most 2-3 tags per node to avoid clutter.
|
||||
* Make tags visually subtle and compact.
|
||||
* Do not break existing family tree panning/zooming/layout.
|
||||
* If a Tarinai is dead, still show its personality tags if data exists.
|
||||
|
||||
12. Tarinai data panel: show recent change causes.
|
||||
|
||||
* The Tarinai individual data panel must include recent reasons for personality/state changes.
|
||||
* Add a small "recent change causes" section.
|
||||
* Record recent causes when personality or important state changes happen, for example:
|
||||
|
||||
* genkotsu damage: fear increase and openness decrease
|
||||
* fight win/loss
|
||||
* being carried by ants
|
||||
* disease infection/cure
|
||||
* protein/nietorop
|
||||
* giant/dwarf drug
|
||||
* mercury consumption
|
||||
* zunda juice clearing effects
|
||||
* sleep/wake transitions where meaningful
|
||||
* stress/fear significant changes
|
||||
* Store only a short rolling history per Tarinai, for example last 5-8 entries.
|
||||
* Each entry should include:
|
||||
|
||||
* Japanese short reason
|
||||
* affected stat/effect
|
||||
* approximate in-game time or "recent"
|
||||
* This is UI/diagnostic information, not save/load.
|
||||
* Do not add full persistence.
|
||||
|
||||
13. Personality system visibility.
|
||||
|
||||
* The existing personality axes are meaningful but currently not visible enough.
|
||||
* In addition to family tree tags and recent change causes, improve the Tarinai data panel to make personality effects understandable:
|
||||
|
||||
* show current personality values already present, if available
|
||||
* show the most recent cause next to changed values where practical
|
||||
* avoid long prose; use compact labels.
|
||||
* Do not add new personality axes.
|
||||
|
||||
14. Documentation consolidation and English-only high-density docs.
|
||||
|
||||
* Keep `docs/prompt.txt` untouched.
|
||||
* Keep documentation concise, English-only, and high information density.
|
||||
* Update or consolidate docs so the following source-of-truth rules are explicit:
|
||||
|
||||
* item visuals come from the canonical visual registry.
|
||||
* UI icons are previews, not independent art.
|
||||
* tool categories come from the tool registry.
|
||||
* item effects come from EffectRegistry.
|
||||
* diseases come from DiseaseRegistry.
|
||||
* food behavior comes from FoodRegistry.
|
||||
* rain visuals are separate from rain-spawned water items.
|
||||
* zunchi is intentionally unchanged.
|
||||
* no field balance, save/load, or debug scenario implementation in this pass.
|
||||
* Avoid many small docs. Prefer a small set such as:
|
||||
|
||||
* `docs/ARCHITECTURE.md`
|
||||
* `docs/CONTENT_GUIDE.md`
|
||||
* `docs/MAINTENANCE.md`
|
||||
* Remove obsolete docs if safely redundant.
|
||||
* Do not delete `docs/prompt.txt`.
|
||||
|
||||
15. Preserve user-facing Japanese UI text unless intentionally specified.
|
||||
|
||||
* Game UI remains Japanese.
|
||||
* Docs are English.
|
||||
* The Codex implementation may use English comments for architecture, but visible labels/tooltips remain Japanese.
|
||||
|
||||
16. Regression fixes to preserve.
|
||||
Do not regress the following existing fixes:
|
||||
|
||||
* Removed unwanted bubbles:
|
||||
|
||||
* ドン
|
||||
* ぐるる
|
||||
* すっきり
|
||||
* Hg xN
|
||||
* ちまっ
|
||||
* でかっ
|
||||
* へにゃ
|
||||
* ムキッ
|
||||
* mm...
|
||||
* Ball tool UI icon must be soccer ball emoji.
|
||||
* Time periods:
|
||||
|
||||
* morning starts at 05:00
|
||||
* noon/day starts at 10:00
|
||||
* evening starts at 17:00
|
||||
* night starts at 20:00
|
||||
* Genkotsu:
|
||||
|
||||
* stronger knockback for living things
|
||||
* zunchi is knocked away
|
||||
* damaged Tarinai gain fear and lose openness
|
||||
* Rain visual:
|
||||
|
||||
* diagonal angled line rain, not droplets.
|
||||
* Rain-spawned water item / zunda juice / mercury:
|
||||
|
||||
* horizontal oval item visuals.
|
||||
* Sleeping Tarinai:
|
||||
|
||||
* can be carried by ants
|
||||
* do not wake from ant carrying
|
||||
* do not attack ants while sleeping
|
||||
* after waking, remain awake for at least 10 seconds.
|
||||
* Tarinai data:
|
||||
|
||||
* fight win rate instead of cumulative defeats.
|
||||
* Fight:
|
||||
|
||||
* on taking damage, Tarinai may lose/flee probabilistically.
|
||||
* high aggression lowers loss chance.
|
||||
* high damage and low HP increase loss chance.
|
||||
|
||||
17. Versioning and cache.
|
||||
|
||||
* Bump app version to the next patch/minor version consistently.
|
||||
* Update:
|
||||
|
||||
* version file
|
||||
* service worker cache name
|
||||
* asset query versions
|
||||
* manifest if present
|
||||
* Ensure `service-worker.js` itself remains no-cache/no-store.
|
||||
* Ensure generated app manifest validation still passes.
|
||||
|
||||
18. Validation.
|
||||
Run and report:
|
||||
|
||||
* `node --check js/*.js service-worker.js`
|
||||
* manifest validation/generation check if scripts exist
|
||||
* zip integrity check if packaging
|
||||
* basic static grep checks:
|
||||
|
||||
* no stale app version references
|
||||
* no removed bubble strings still emitted as bubbles
|
||||
* mobile UI visibility logic uses the new InputModeManager
|
||||
* tool categories are not duplicated in `index.html`
|
||||
* sound metadata is not duplicated as canonical data in both `sound_pack.js` and `audio.js`
|
||||
* item visual drift-prone items use canonical visual registry
|
||||
* docs are English except `docs/prompt.txt`
|
||||
|
||||
Deliverables:
|
||||
|
||||
* Updated project files.
|
||||
* Updated ZIP.
|
||||
* Brief implementation summary.
|
||||
* Validation results.
|
||||
* Mention any deliberately deferred items:
|
||||
|
||||
* field balance tuning
|
||||
* save/load
|
||||
* debug scenario modes
|
||||
* zunchi expansion
|
||||
BIN
favicon.ico
Normal file
|
After Width: | Height: | Size: 12 KiB |
526
index.html
|
|
@ -4,123 +4,202 @@
|
|||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>たりない観察</title>
|
||||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 64 64%27%3E%3Ctext y=%2752%27 font-size=%2752%27%3E%E3%81%9F%3C/text%3E%3C/svg%3E" />
|
||||
<link rel="stylesheet" href="css/base.css?v=15.24.0" />
|
||||
<link rel="stylesheet" href="css/layout.css?v=15.24.0" />
|
||||
<link rel="stylesheet" href="css/panel.css?v=15.24.0" />
|
||||
<link rel="stylesheet" href="css/components.css?v=15.24.0" />
|
||||
<link rel="stylesheet" href="css/mobile.css?v=15.24.0" />
|
||||
<link rel="icon" type="image/png" href="assets/ui/favicon.png?v=39.18.18" />
|
||||
<link rel="apple-touch-icon" href="assets/ui/apple-touch-icon.png?v=39.18.18" />
|
||||
<link rel="manifest" href="manifest.webmanifest?v=39.18.18" />
|
||||
<meta name="theme-color" content="#244d3a" />
|
||||
<link rel="shortcut icon" href="favicon.ico?v=39.18.18" />
|
||||
<link rel="stylesheet" href="css/base.css?v=39.18.18" />
|
||||
<link rel="stylesheet" href="css/layout.css?v=39.18.18" />
|
||||
<link rel="stylesheet" href="css/panel.css?v=39.18.18" />
|
||||
<link rel="stylesheet" href="css/components.css?v=39.18.18" />
|
||||
<link rel="stylesheet" href="css/mobile.css?v=39.18.18" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="loadingScreen" class="loading-screen" aria-live="polite">
|
||||
<div class="loading-panel">
|
||||
<div class="loading-kicker">たりないコロニー</div>
|
||||
<h1 class="loading-title">読み込み中…</h1>
|
||||
<p id="loadingText" class="loading-text">準備しています</p>
|
||||
<div class="loading-kicker">たりない観察</div>
|
||||
<h1 class="loading-title">読み込み中…</h1>
|
||||
<p id="loadingText" class="loading-text">準備しています。</p>
|
||||
<div id="loadingSpriteStrip" class="loading-sprite-strip" aria-hidden="true"></div>
|
||||
<div class="loading-progress-track"><div id="loadingBar" class="loading-progress-bar" style="width:0%"></div></div>
|
||||
<div class="loading-progress-meta"><span>進行状況</span><strong id="loadingPercent">0%</strong></div>
|
||||
<p class="loading-hint">初期表示に必要な画像だけ先に読み込み、残りは後から順次ロードします。</p>
|
||||
<div class="loading-progress-meta"><span>進行状況</span><strong id="loadingPercent">0%</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="app">
|
||||
<header class="topbar">
|
||||
<div class="top-left">
|
||||
<a class="btn room-link" href="https://host.nishi.boats/~333/">333の部屋に移動</a>
|
||||
<div class="room-links" style="display:flex;flex-direction:column;align-items:stretch;gap:6px;">
|
||||
<a class="btn room-link" style="display:block;text-align:center;" href="https://2012r2.nishi.boats/~333/">333の部屋に移動</a>
|
||||
<a class="btn room-link" style="display:block;text-align:center;" href="https://seesaawiki.jp/informalzunda/d/%a5%b2%a1%bc%a5%e0%3a%20%a4%bf%a4%ea%a4%ca%a4%a4%b4%d1%bb%a1">地下牢に戻る</a>
|
||||
</div>
|
||||
<div class="brand">
|
||||
<h1>たりない観察</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="top-actions">
|
||||
<div id="panelSearchControl" class="panel-search-control top-panel-search" role="search" aria-label="右メニュー検索">
|
||||
<input id="panelSearchInput" class="panel-search-input" type="search" autocomplete="off" spellcheck="false" placeholder="検索" aria-label="右メニューを検索">
|
||||
<span class="panel-search-status" aria-live="polite">0</span>
|
||||
<button class="panel-search-prev" type="button" disabled aria-label="前の該当位置へ移動">↑</button>
|
||||
<button class="panel-search-next" type="button" disabled aria-label="次の該当位置へ移動">↓</button>
|
||||
</div>
|
||||
<button id="pauseBtn" class="btn primary">一時停止</button>
|
||||
<button id="speedBtn" class="btn">速度 x1</button>
|
||||
<button id="ecologyBtn" class="btn">たりないの生態</button>
|
||||
<div id="soundMenu" class="sound-menu">
|
||||
<button id="soundBtn" class="btn sound-menu-btn" type="button" aria-haspopup="true" aria-expanded="false">音設定 ON</button>
|
||||
<div id="soundPanel" class="sound-panel hidden" role="menu" aria-label="効果音設定">
|
||||
<label class="sound-master"><input id="soundMasterToggle" type="checkbox" checked><span>効果音</span></label>
|
||||
<div class="sound-panel-divider"></div>
|
||||
<label><input type="checkbox" data-sound-category="voice"><span>たりないの声</span></label>
|
||||
<label><input type="checkbox" data-sound-category="notify" checked><span>通知</span></label>
|
||||
<label><input type="checkbox" data-sound-category="ops" checked><span>操作</span></label>
|
||||
<div id="speedMenu" class="top-menu speed-menu">
|
||||
<button id="speedMenuBtn" class="btn speed-menu-btn" type="button" aria-haspopup="true" aria-expanded="false">速度 ×1</button>
|
||||
<div id="speedMenuPanel" class="top-menu-panel speed-menu-panel hidden" role="menu" aria-label="速度">
|
||||
<button class="top-menu-item" type="button" role="menuitemradio" data-speed-value="0.5">×0.5</button>
|
||||
<button class="top-menu-item" type="button" role="menuitemradio" data-speed-value="1">×1</button>
|
||||
<button class="top-menu-item" type="button" role="menuitemradio" data-speed-value="2">×2</button>
|
||||
<button class="top-menu-item" type="button" role="menuitemradio" data-speed-value="4">×4</button>
|
||||
<button class="top-menu-item" type="button" role="menuitemradio" data-speed-value="8">×8</button>
|
||||
<button class="top-menu-item" type="button" role="menuitemradio" data-speed-value="16">×16</button>
|
||||
</div>
|
||||
<select id="speedSelect" class="speed-select-native" aria-hidden="true" tabindex="-1">
|
||||
<option value="0.5">0.5</option><option value="1" selected>1</option><option value="2">2</option><option value="4">4</option><option value="8">8</option><option value="16">16</option>
|
||||
</select>
|
||||
</div>
|
||||
<button id="saveBtn" class="btn">セーブ</button>
|
||||
<button id="ecologyBtn" class="btn ecology-top-btn" type="button">たりないの生態</button>
|
||||
<button id="achievementsBtn" class="btn achievement-top-btn" type="button" aria-haspopup="dialog" aria-controls="achievementsDialog">実績 <span id="achievementButtonCount">0/77</span></button>
|
||||
<button id="resetBtn" class="btn danger reset-top-btn">リセット</button>
|
||||
<div id="topMenu" class="top-menu">
|
||||
<button id="topMenuBtn" class="btn top-menu-btn" type="button" aria-haspopup="true" aria-expanded="false" aria-label="メニュー">☰</button>
|
||||
<div id="topMenuPanel" class="top-menu-panel hidden" role="group" aria-label="メニュー">
|
||||
<div id="soundMenu" class="sound-menu top-menu-sound">
|
||||
<button id="soundBtn" class="top-menu-item sound-menu-btn" type="button" aria-haspopup="true" aria-expanded="false">音 ON</button>
|
||||
<div id="soundPanel" class="sound-panel hidden" role="group" aria-label="音">
|
||||
<label class="sound-master"><input id="soundMasterToggle" type="checkbox" checked><span>効果音</span></label>
|
||||
<div class="sound-panel-divider"></div>
|
||||
<label><input type="checkbox" data-sound-category="voice"><span>たりないの声</span></label>
|
||||
<label><input type="checkbox" data-sound-category="notify" checked><span>通知</span></label>
|
||||
<label><input type="checkbox" data-sound-category="ops" checked><span>操作</span></label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="visualSettingsMenu" class="sound-menu top-menu-sound top-menu-visual">
|
||||
<button id="visualSettingsBtn" class="top-menu-item sound-menu-btn" type="button" aria-haspopup="true" aria-expanded="false">表示設定</button>
|
||||
<div id="visualSettingsPanel" class="sound-panel hidden" role="group" aria-label="表示設定">
|
||||
<label><input id="visualEffectsToggle" type="checkbox" data-visual-setting="effects" data-visual-on="high" data-visual-off="off"><span>エフェクト</span></label>
|
||||
<label><input id="visualShadowsToggle" type="checkbox" data-visual-setting="shadows" data-visual-on="high" data-visual-off="off"><span>影</span></label>
|
||||
<div class="sound-panel-divider"></div>
|
||||
<div class="sound-panel-subtitle">描画</div>
|
||||
<label><input type="radio" name="visualDetails" data-visual-setting="details" data-visual-value="auto"><span>自動</span></label>
|
||||
<label><input type="radio" name="visualDetails" data-visual-setting="details" data-visual-value="low"><span>低</span></label>
|
||||
<label><input type="radio" name="visualDetails" data-visual-setting="details" data-visual-value="high"><span>高</span></label>
|
||||
</div>
|
||||
</div>
|
||||
<button id="creditsBtn" class="top-menu-item" type="button">クレジット</button>
|
||||
</div>
|
||||
</div>
|
||||
<button id="creditsBtn" class="btn">クレジット</button>
|
||||
<button id="saveBtn" class="btn">セーブ</button>
|
||||
<button id="resetBtn" class="btn danger">リセット</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="layout">
|
||||
<section class="stage-wrap">
|
||||
<canvas id="gameCanvas" aria-label="たりない観察キャンバス"></canvas>
|
||||
<div id="mobileModeControls" class="mobile-mode-controls" aria-label="スマホ操作モード">
|
||||
<button type="button" data-mobile-mode="auto" class="active">自動</button>
|
||||
<button type="button" data-mobile-mode="camera">カメラ</button>
|
||||
<button type="button" data-mobile-mode="tool">道具</button>
|
||||
<button type="button" data-mobile-mode="family">家系図</button>
|
||||
<div id="mobileModeControls" class="mobile-mode-controls" aria-label="スマホ操作モード">
|
||||
<button type="button" data-mobile-mode="auto" class="active">自動</button>
|
||||
<button type="button" data-mobile-mode="camera">カメラ</button>
|
||||
<button type="button" data-mobile-mode="tool">道具</button>
|
||||
</div>
|
||||
<div id="mobileRotateControls" class="mobile-rotate-controls" hidden aria-label="物理アイテムの回転">
|
||||
<button type="button" data-mobile-rotate="left">左回転</button>
|
||||
<button type="button" data-mobile-rotate="right">右回転</button>
|
||||
</div>
|
||||
<div id="toast" class="toast hidden"></div>
|
||||
</section>
|
||||
|
||||
<aside class="panel">
|
||||
<nav id="panelQuickTabs" class="panel-quick-tabs" aria-label="右メニューのタブ">
|
||||
<button type="button" data-panel-tab="game">本体</button>
|
||||
<button type="button" data-panel-tab="selected">たりない<br>データ</button>
|
||||
<button type="button" data-panel-tab="tools">道具</button>
|
||||
<button type="button" data-panel-tab="colony">コロニー</button>
|
||||
<button type="button" data-panel-tab="events">イベント</button>
|
||||
<button type="button" data-panel-tab="family">家系図</button>
|
||||
</nav>
|
||||
<section id="selectedCard" class="card selected-card hidden">
|
||||
<div class="selected-head">
|
||||
<h2>たりないデータ</h2>
|
||||
<button id="selectedCloseBtn" class="selected-close" type="button" aria-label="観察をやめる">×</button>
|
||||
<button id="selectedCloseBtn" class="selected-close" type="button" aria-label="たりないデータを閉じる">×</button>
|
||||
</div>
|
||||
<div id="selectedInfo" class="selected-info empty">未選択</div>
|
||||
<div id="selectedInfo" class="selected-info empty">たりないを選択してください</div>
|
||||
</section>
|
||||
|
||||
<section id="toolCard" class="card tool-card collapsible-card" data-card-key="tools">
|
||||
<section id="toolCard" class="card tool-card collapsible-card">
|
||||
<button class="panel-card-toggle" type="button" aria-expanded="true"><span class="panel-card-title">道具</span><span class="panel-card-state">折り畳み</span></button>
|
||||
<div class="panel-card-body">
|
||||
<div class="tool-body">
|
||||
<div id="toolPalette" class="tool-palette" aria-live="polite"></div>
|
||||
<p class="hint">同じ道具ボタンをもう一度押すと、対応道具の大きさが小・中・大で切り替わります。</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card colony-card collapsible-card" data-card-key="colony">
|
||||
<section class="card colony-card collapsible-card">
|
||||
<button class="panel-card-toggle" type="button" aria-expanded="true"><span class="panel-card-title">コロニー</span><span class="panel-card-state">折り畳み</span></button>
|
||||
<div class="panel-card-body">
|
||||
<div class="stats colony-stats">
|
||||
<div class="stat"><span>個体数</span><strong id="statPop">0</strong></div>
|
||||
<div class="stat"><span>累計死亡数</span><strong id="statDead">0</strong></div>
|
||||
<div class="stat"><span>雰囲気</span><strong id="statColonyMood">のんびり</strong></div>
|
||||
<div class="stat"><span>状態</span><strong id="statColonyMood">安定</strong></div>
|
||||
</div>
|
||||
<div id="colonyChartTabs" class="chart-tabs"><button data-chart="population" class="active">人口</button><button data-chart="life">精神</button><button data-chart="environment">環境</button><button data-chart="objects">オブジェクト</button></div>
|
||||
<details class="colony-setting-fold colony-settings-fold">
|
||||
<summary><span>コロニー設定</span></summary>
|
||||
<div class="colony-settings-body">
|
||||
<section class="colony-setting-section ground-setting-section">
|
||||
<div class="colony-setting-section-head"><span>地面</span></div>
|
||||
<button id="groundTypeBtn" class="ground-type-btn colony-setting-action" type="button" data-ground-toggle aria-label="地面をきりかえる"><strong id="statGroundType">砂</strong><span>切り替え</span></button>
|
||||
</section>
|
||||
<section class="colony-setting-section temperature-section">
|
||||
<div class="colony-setting-section-head"><span>気温</span><strong id="temperatureValue">10℃</strong></div>
|
||||
<div class="temperature-control" aria-label="気温調整">
|
||||
<label class="temperature-auto"><input id="temperatureAutoToggle" type="checkbox" checked> 自動</label>
|
||||
<input id="temperatureSlider" class="temperature-slider" type="range" aria-label="気温" min="-100" max="120" step="1" value="10">
|
||||
</div>
|
||||
</section>
|
||||
<section class="colony-setting-section colony-limit-section">
|
||||
<div class="colony-setting-section-head"><span>上限設定</span><small>300の次は∞</small></div>
|
||||
<div class="colony-limit-control" aria-label="コロニー上限設定">
|
||||
<div class="colony-limit-grid">
|
||||
<label class="colony-limit-row"><span>たりないの数</span><input id="tarinaiPopulationLimitSlider" class="colony-limit-slider" type="range" min="1" max="301" step="1" value="301"><input id="tarinaiPopulationLimitInput" class="colony-limit-number" type="number" min="1" max="301" step="1" inputmode="numeric" placeholder="∞"></label>
|
||||
<label class="colony-limit-row"><span>物体数</span><input id="objectLimitSlider" class="colony-limit-slider" type="range" min="1" max="301" step="1" value="301"><input id="objectLimitInput" class="colony-limit-number" type="number" min="1" max="301" step="1" inputmode="numeric" placeholder="∞"></label>
|
||||
</div>
|
||||
<div id="colonyLimitStatus" class="colony-limit-status">たりないの数 0 / ∞ 物体数 0 / ∞</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</details>
|
||||
<div id="colonyChartTabs" class="chart-tabs"><button data-chart="population" class="active">個体数</button><button data-chart="life">欲求</button><button data-chart="objects">環境</button></div>
|
||||
<div id="colonyChartRangeTabs" class="chart-range-tabs" aria-label="統計の表示期間"><button data-chart-range="day">1日</button><button data-chart-range="season" class="active">季節</button><button data-chart-range="year">年</button></div>
|
||||
<div id="colonyChartLegend" class="colony-chart-legend"></div>
|
||||
<div id="colonyChart" class="colony-chart" aria-label="コロニーの推移グラフ" role="img"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card log-card collapsible-card" data-card-key="events">
|
||||
<section class="card log-card collapsible-card">
|
||||
<button class="panel-card-toggle" type="button" aria-expanded="true"><span class="panel-card-title">イベント</span><span class="panel-card-state">折り畳み</span></button>
|
||||
<div class="panel-card-body">
|
||||
<div id="log" class="log"></div>
|
||||
<div class="log-push-controls" id="logPushControls" aria-label="プッシュ通知設定">
|
||||
<label class="push-master"><input id="logPushEnabled" type="checkbox"> プッシュ通知</label>
|
||||
<div class="log-push-controls" id="logPushControls" aria-label="イベント表示・プッシュ通知設定">
|
||||
<div class="event-category-heading">表示カテゴリ</div>
|
||||
<div class="push-kind-grid">
|
||||
<label><input type="checkbox" data-push-kind="death" checked> 死亡</label>
|
||||
<label><input type="checkbox" data-push-kind="accident" checked> 事故</label>
|
||||
<label><input type="checkbox" data-push-kind="birth" checked> 誕生</label>
|
||||
<label><input type="checkbox" data-push-kind="fight"> 喧嘩</label>
|
||||
<label><input type="checkbox" data-push-kind="food"> 食事</label>
|
||||
<label><input type="checkbox" data-push-kind="weather"> 天気</label>
|
||||
<label><input type="checkbox" data-push-kind="grass"> 草</label>
|
||||
<label><input type="checkbox" data-push-kind="relation"> 関係</label>
|
||||
<label><input type="checkbox" data-event-category="life" checked> 生命</label>
|
||||
<label><input type="checkbox" data-event-category="danger" checked> 危険</label>
|
||||
<label><input type="checkbox" data-event-category="social" checked> 交流</label>
|
||||
<label><input type="checkbox" data-event-category="living" checked> 生活</label>
|
||||
<label><input type="checkbox" data-event-category="environment" checked> 環境</label>
|
||||
<label><input type="checkbox" data-event-category="observe" checked> 観察</label>
|
||||
</div>
|
||||
<label class="push-master"><input id="logPushEnabled" type="checkbox"> プッシュ通知</label>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</aside>
|
||||
</main>
|
||||
|
||||
|
||||
|
||||
<section class="card lineage-main-card">
|
||||
<div class="lineage-head">
|
||||
<h2>家系図</h2>
|
||||
<h2>家系図<span class="lineage-title-note">重い場合があるので注意</span></h2>
|
||||
<div class="lineage-actions">
|
||||
<button id="archiveUpdateToggleBtn" class="btn" type="button" aria-pressed="false">更新 OFF</button>
|
||||
<button id="archiveResetBtn" class="btn danger" type="button">家系図をリセット</button>
|
||||
|
|
@ -128,18 +207,67 @@
|
|||
</div>
|
||||
<div id="archiveContent" class="archive-content lineage-main-content"></div>
|
||||
</section>
|
||||
|
||||
|
||||
<button id="backToGameBtn" class="back-to-game-btn" type="button" hidden aria-label="ゲーム本体へ戻る">▲ 本体</button>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div id="achievementsDialog" class="field-dialog hidden" role="dialog" aria-modal="true" aria-labelledby="achievementsDialogTitle">
|
||||
<div class="field-dialog-panel achievement-dialog-panel">
|
||||
<div class="achievement-dialog-head">
|
||||
<div>
|
||||
<div class="achievement-dialog-kicker">たりない観察</div>
|
||||
<h2 id="achievementsDialogTitle">実績 <span id="achievementDialogCount">0 / 77</span></h2>
|
||||
</div>
|
||||
<button id="achievementsCloseIconBtn" class="achievement-close-icon" type="button" aria-label="実績を閉じる">×</button>
|
||||
</div>
|
||||
<p class="field-dialog-lead">一部の条件は未達成でも公開されます。実績はこの端末に保存され、背景の棒グラフは全体達成率を示します。</p>
|
||||
<div id="achievementSharedStatus" class="achievement-shared-status" aria-live="polite">共有達成率を確認中…</div>
|
||||
<div id="achievementList" class="achievement-list"></div>
|
||||
<div class="field-dialog-actions achievement-dialog-actions">
|
||||
<button id="achievementResetBtn" class="btn danger achievement-reset-btn" type="button">実績をリセット</button>
|
||||
<button id="achievementUnlockAllBtn" class="btn" type="button" hidden>全実績を獲得</button>
|
||||
<button id="achievementRefreshBtn" class="btn" type="button">共有達成率を更新</button>
|
||||
<button id="achievementsCloseBtn" class="btn primary" type="button">閉じる</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="achievementToast" class="achievement-toast hidden" role="status" aria-live="polite">
|
||||
<div class="achievement-toast-icon" aria-hidden="true">★</div>
|
||||
<div class="achievement-toast-copy">
|
||||
<small>実績を解除しました</small>
|
||||
<strong id="achievementToastTitle"></strong>
|
||||
<span id="achievementToastCondition" class="achievement-toast-condition"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="fieldDialog" class="field-dialog hidden" role="dialog" aria-modal="true" aria-labelledby="fieldDialogTitle">
|
||||
<div class="field-dialog-panel">
|
||||
<h2 id="fieldDialogTitle">フィールドを選択</h2>
|
||||
<div class="field-dialog-panel reset-dialog-panel">
|
||||
<h2 id="fieldDialogTitle">リセット設定</h2>
|
||||
<p class="field-dialog-lead">サイズとプリセットを組み合わせて観察環境を生成します。</p>
|
||||
<div class="field-dialog-section-title">サイズ</div>
|
||||
<div class="field-choice-grid">
|
||||
<button class="field-choice" data-field-type="park" type="button"><strong>公園</strong><span>大</span></button>
|
||||
<button class="field-choice" data-field-type="garden" type="button"><strong>庭</strong><span>中</span></button>
|
||||
<button class="field-choice" data-field-type="cage" type="button"><strong>ケージ</strong><span>小</span></button>
|
||||
<button class="field-choice" data-field-type="park" type="button" aria-pressed="false"><strong>公園</strong><span>大</span></button>
|
||||
<button class="field-choice" data-field-type="garden" type="button" aria-pressed="true"><strong>庭</strong><span>中</span></button>
|
||||
<button class="field-choice" data-field-type="cage" type="button" aria-pressed="false"><strong>ケージ</strong><span>小</span></button>
|
||||
</div>
|
||||
<div class="field-dialog-section-title">プリセット</div>
|
||||
<div class="field-choice-grid reset-preset-choice-grid">
|
||||
<button class="field-choice" data-reset-preset="empty" type="button" aria-pressed="false"><strong>空白</strong><span>何も無い。</span></button>
|
||||
<button class="field-choice" data-reset-preset="default" type="button" aria-pressed="true"><strong>デフォルト</strong><span>初期生成。</span></button>
|
||||
<button class="field-choice" data-reset-preset="athletic" type="button" aria-pressed="false"><strong>アスレチック</strong><span>柵とボール。</span></button>
|
||||
<button class="field-choice" data-reset-preset="crowded" type="button" aria-pressed="false"><strong>過密</strong><span>大量のたりない。</span></button>
|
||||
<button class="field-choice" data-reset-preset="family" type="button" aria-pressed="false"><strong>家族</strong><span>2匹とへこ餅。</span></button>
|
||||
<button class="field-choice" data-reset-preset="war" type="button" aria-pressed="false"><strong>戦争</strong><span>けんかと柵。</span></button>
|
||||
<button class="field-choice" data-reset-preset="happy" type="button" aria-pressed="false"><strong>幸福</strong><span>掃除機と巣箱。</span></button>
|
||||
<button class="field-choice" data-reset-preset="hair_trigger" type="button" aria-pressed="false"><strong>一触即発</strong><span>中央のボール柵。</span></button>
|
||||
</div>
|
||||
<div class="field-dialog-actions">
|
||||
<button id="fieldCancelBtn" class="btn" type="button">キャンセル</button>
|
||||
<button id="fieldConfirmBtn" class="btn danger" type="button">再生成</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -147,15 +275,14 @@
|
|||
|
||||
|
||||
<div id="creditsDialog" class="field-dialog hidden" role="dialog" aria-modal="true" aria-labelledby="creditsDialogTitle">
|
||||
<div class="field-dialog-panel credits-dialog-panel">
|
||||
<div class="field-dialog-panel">
|
||||
<h2 id="creditsDialogTitle">クレジット</h2>
|
||||
<div class="credits-content">
|
||||
<div>
|
||||
<p><strong>製作:</strong> 333</p>
|
||||
<p><strong>たりない:</strong> むらさき氏</p>
|
||||
<p><strong>イラスト:</strong> むらさき氏 / いらすとや: <a href="https://www.irasutoya.com/" target="_blank" rel="noopener">https://www.irasutoya.com/</a></p>
|
||||
<p><strong>効果音:</strong> VOICEVOX: ずんだもん 効果音ラボ: <a href="https://soundeffect-lab.info/" target="_blank" rel="noopener">https://soundeffect-lab.info/</a></p>
|
||||
<p><strong>アリ素材:</strong> いらすとや: <a href="https://www.irasutoya.com/" target="_blank" rel="noopener">https://www.irasutoya.com/</a></p>
|
||||
</div>
|
||||
<div class="field-dialog-actions credits-actions">
|
||||
<div class="field-dialog-actions">
|
||||
<button id="creditsCloseBtn" class="btn" type="button">閉じる</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -183,66 +310,221 @@
|
|||
</div>
|
||||
|
||||
<div id="logPushOverlay" class="log-push-overlay" aria-live="polite" aria-atomic="false"></div>
|
||||
<script src="js/version.js?v=15.24.0" defer></script>
|
||||
<script src="js/event_bus.js?v=15.24.0" defer></script>
|
||||
<script src="js/performance_manager.js?v=15.24.0" defer></script>
|
||||
<script src="js/registry_base.js?v=15.24.0" defer></script>
|
||||
<script src="js/disease_registry.js?v=15.24.0" defer></script>
|
||||
<script src="js/effect_registry.js?v=15.24.0" defer></script>
|
||||
<script src="js/sound_pack.js?v=15.24.0" defer></script>
|
||||
<script src="js/data.js?v=15.24.0" defer></script>
|
||||
<script src="js/item_visual_registry.js?v=15.24.0" defer></script>
|
||||
<script src="js/food_registry.js?v=15.24.0" defer></script>
|
||||
<script src="js/input_mode_manager.js?v=15.24.0" defer></script>
|
||||
<script src="js/math.js?v=15.24.0" defer></script>
|
||||
<script src="js/physics_helpers.js?v=15.24.0" defer></script>
|
||||
<script src="js/assets.js?v=15.24.0" defer></script>
|
||||
<script src="js/audio.js?v=15.24.0" defer></script>
|
||||
<script src="js/render.js?v=15.24.0" defer></script>
|
||||
<script src="js/sim_core.js?v=15.24.0" defer></script>
|
||||
<script src="js/items.js?v=15.24.0" defer></script>
|
||||
<script src="js/ants.js?v=15.24.0" defer></script>
|
||||
<script src="js/health.js?v=15.24.0" defer></script>
|
||||
<script src="js/tarinai.js?v=15.24.0" defer></script>
|
||||
<script src="js/tarinai_identity_social.js?v=15.24.0" defer></script>
|
||||
<script src="js/tarinai_action_state.js?v=15.24.0" defer></script>
|
||||
<script src="js/tarinai_disease_nest.js?v=15.24.0" defer></script>
|
||||
<script src="js/tarinai_item_effects.js?v=15.24.0" defer></script>
|
||||
<script src="js/tarinai_needs_items.js?v=15.24.0" defer></script>
|
||||
<script src="js/tarinai_social_move_life.js?v=15.24.0" defer></script>
|
||||
<script src="js/tarinai_render.js?v=15.24.0" defer></script>
|
||||
<script src="js/world.js?v=15.24.0" defer></script>
|
||||
<script src="js/world_view.js?v=15.24.0" defer></script>
|
||||
<script src="js/family_graph.js?v=15.24.0" defer></script>
|
||||
<script src="js/world_family_social.js?v=15.24.0" defer></script>
|
||||
<script src="js/world_combat_effects.js?v=15.24.0" defer></script>
|
||||
<script src="js/world_environment.js?v=15.24.0" defer></script>
|
||||
<script src="js/world_spatial_budget.js?v=15.24.0" defer></script>
|
||||
<script src="js/world_ants_system.js?v=15.24.0" defer></script>
|
||||
<script src="js/weather_system.js?v=15.24.0" defer></script>
|
||||
<script src="js/world_update.js?v=15.24.0" defer></script>
|
||||
<script src="js/world_placement_log.js?v=15.24.0" defer></script>
|
||||
<script src="js/world_bootstrap.js?v=15.24.0" defer></script>
|
||||
<script src="js/text_catalog.js?v=15.24.0" defer></script>
|
||||
<script src="js/ui.js?v=15.24.0" defer></script>
|
||||
<script src="js/ui_log.js?v=15.24.0" defer></script>
|
||||
<script src="js/ui_helpers.js?v=15.24.0" defer></script>
|
||||
<script src="js/ui_selected.js?v=15.24.0" defer></script>
|
||||
<script src="js/snapshot_system.js?v=15.24.0" defer></script>
|
||||
<script src="js/save_system.js?v=15.24.0" defer></script>
|
||||
<script src="js/patch_helpers.js?v=15.24.0" defer></script>
|
||||
<script src="js/freeze_system.js?v=15.24.0" defer></script>
|
||||
<script src="js/relation_event_system.js?v=15.24.0" defer></script>
|
||||
<script src="js/ui_layout_dialogs.js?v=15.24.0" defer></script>
|
||||
<script src="js/ui_tools.js?v=15.24.0" defer></script>
|
||||
<script src="js/ui_bind.js?v=15.24.0" defer></script>
|
||||
<script src="js/ui_charts.js?v=15.24.0" defer></script>
|
||||
<script src="js/ui_family_data.js?v=15.24.0" defer></script>
|
||||
<script src="js/ui_family_async.js?v=15.24.0" defer></script>
|
||||
<script src="js/ui_family_layout.js?v=15.24.0" defer></script>
|
||||
<script src="js/ui_family_paths.js?v=15.24.0" defer></script>
|
||||
<script src="js/ui_family_render.js?v=15.24.0" defer></script>
|
||||
<script src="js/main.js?v=15.24.0" defer></script>
|
||||
<script src="js/debug_tools.js?v=15.24.0" defer></script>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
const replacements = [
|
||||
["端末報告ベース", "達成率"],
|
||||
["報告プレイヤー", "達成人数"],
|
||||
];
|
||||
const replaceText = value => replacements.reduce((text, [from, to]) => text.replaceAll(from, to), String(value || ""));
|
||||
const normalizeNode = node => {
|
||||
if (!node) return;
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
const next = replaceText(node.nodeValue);
|
||||
if (next !== node.nodeValue) node.nodeValue = next;
|
||||
return;
|
||||
}
|
||||
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
||||
if (node.hasAttribute("aria-label")) {
|
||||
const current = node.getAttribute("aria-label");
|
||||
const next = replaceText(current);
|
||||
if (next !== current) node.setAttribute("aria-label", next);
|
||||
}
|
||||
const walker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT);
|
||||
while (walker.nextNode()) {
|
||||
const textNode = walker.currentNode;
|
||||
const next = replaceText(textNode.nodeValue);
|
||||
if (next !== textNode.nodeValue) textNode.nodeValue = next;
|
||||
}
|
||||
node.querySelectorAll?.("[aria-label]").forEach(element => {
|
||||
const current = element.getAttribute("aria-label");
|
||||
const next = replaceText(current);
|
||||
if (next !== current) element.setAttribute("aria-label", next);
|
||||
});
|
||||
};
|
||||
const list = document.getElementById("achievementList");
|
||||
if (!list) return;
|
||||
normalizeNode(list);
|
||||
new MutationObserver(records => {
|
||||
for (const record of records) {
|
||||
if (record.type === "characterData") normalizeNode(record.target);
|
||||
for (const node of record.addedNodes || []) normalizeNode(node);
|
||||
}
|
||||
}).observe(list, { childList: true, subtree: true, characterData: true });
|
||||
})();
|
||||
</script>
|
||||
<script src="js/version.js?v=39.18.18" defer></script>
|
||||
<script src="js/event_bus.js?v=39.18.18" defer></script>
|
||||
<script src="js/domain_ids.js?v=39.18.18" defer></script>
|
||||
<script src="js/registry_base.js?v=39.18.18" defer></script>
|
||||
<script src="js/disease_registry.js?v=39.18.18" defer></script>
|
||||
<script src="js/sound_pack.js?v=39.18.18" defer></script>
|
||||
<script src="js/deterministic_helpers.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_tool_metadata.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_tool_definitions.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_visual_definitions.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_food_definitions.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_effect_definitions.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_registry.js?v=39.18.18" defer></script>
|
||||
<script src="js/data.js?v=39.18.18" defer></script>
|
||||
<script src="js/ground_types.js?v=39.18.18" defer></script>
|
||||
<script src="js/input_mode_manager.js?v=39.18.18" defer></script>
|
||||
<script src="js/math.js?v=39.18.18" defer></script>
|
||||
<script src="js/geometry_helpers.js?v=39.18.18" defer></script>
|
||||
<script src="js/collision_footprint_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/physics_helpers.js?v=39.18.18" defer></script>
|
||||
<script src="js/placement_preview_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/pin_attachment_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/physics_shape_editor_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/mechanical_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/mechanical_shape_bridge.js?v=39.18.18" defer></script>
|
||||
<script src="js/constraint_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/physics_world_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/physics_projection_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/circuit_board_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/signal_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/assets.js?v=39.18.18" defer></script>
|
||||
<script src="js/audio.js?v=39.18.18" defer></script>
|
||||
<script src="js/perf_profiler.js?v=39.18.18" defer></script>
|
||||
<script src="js/display_helpers.js?v=39.18.18" defer></script>
|
||||
<script src="js/render.js?v=39.18.18" defer></script>
|
||||
<script src="js/sim_core.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_seed_factory.js?v=39.18.18" defer></script>
|
||||
<script src="js/structures.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_type_catalog.js?v=39.18.18" defer></script>
|
||||
<script src="js/items.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_type_initializers.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_lifecycle_support.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_update_policy.js?v=39.18.18" defer></script>
|
||||
<script src="js/fire_runtime_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/robot_cleaner_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_dynamic_tool_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_dynamic_ball_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_dynamic_duplicator_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_dynamic_pin_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_dynamic_zunchi_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_environment_hazard_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_dynamic_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_lifecycle_decay_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_lifecycle_growth_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_lifecycle_step_frame.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_lifecycle_step_decay.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_lifecycle_step_dynamic.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_lifecycle_step_growth.js?v=39.18.18" defer></script>
|
||||
<script src="js/update_step_pipeline_runner.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_lifecycle_pipeline.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_runtime.js?v=39.18.18" defer></script>
|
||||
<script src="js/structure_lifecycle.js?v=39.18.18" defer></script>
|
||||
<script src="js/memorial_bell_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_render_helpers.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_render_runtime.js?v=39.18.18" defer></script>
|
||||
<script src="js/burn_motion_util.js?v=39.18.18" defer></script>
|
||||
<script src="js/ants.js?v=39.18.18" defer></script>
|
||||
<script src="js/health.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai.js?v=39.18.18" defer></script>
|
||||
<script src="js/mamekusaredake_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_action_spec.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_behavior_state.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_identity_social.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_action_state.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_disease_nest.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_item_effects.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_needs_core.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_behavior_text.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_forced_behavior.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_nest_sleep_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_item_targeting.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_consumable_behavior.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_social_action_runtime.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_building_behavior.js?v=39.18.18" defer></script>
|
||||
<script src="js/seesaw_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_action_definitions.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_needs_items.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_food_prototype_mixin.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_direct_feeding_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_need_planner_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_item_interaction_context.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_food_interaction_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_contact_item_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_item_interaction_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_sunbath_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_cursor_care_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_local_environment_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_social_move_life.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_update_step_frame.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_update_step_ai.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_update_step_environment.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_update_step_movement.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_update_step_health.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_update_pipeline.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_runtime.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_render.js?v=39.18.18" defer></script>
|
||||
<script src="js/world.js?v=39.18.18" defer></script>
|
||||
<script src="js/world_view.js?v=39.18.18" defer></script>
|
||||
<script src="js/family_graph.js?v=39.18.18" defer></script>
|
||||
<script src="js/world_reset_presets.js?v=39.18.18" defer></script>
|
||||
<script src="js/world_family_social.js?v=39.18.18" defer></script>
|
||||
<script src="js/impact_core_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/impact_response_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/collision_response_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/world_combat_effects.js?v=39.18.18" defer></script>
|
||||
<script src="js/world_environment.js?v=39.18.18" defer></script>
|
||||
<script src="js/world_temperature_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/world_pathfinding_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/world_grass_placement_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/world_spatial_budget.js?v=39.18.18" defer></script>
|
||||
<script src="js/world_ants_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/weather_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/item_update_scheduler.js?v=39.18.18" defer></script>
|
||||
<script src="js/simulation_runtime_helpers.js?v=39.18.18" defer></script>
|
||||
<script src="js/tarinai_update_policy.js?v=39.18.18" defer></script>
|
||||
<script src="js/simulation_environment_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/simulation_item_ant_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/simulation_effects_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/simulation_creature_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/simulation_maintenance_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/simulation_ambient_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/simulation_systems.js?v=39.18.18" defer></script>
|
||||
<script src="js/system_order.js?v=39.18.18" defer></script>
|
||||
<script src="js/simulation.js?v=39.18.18" defer></script>
|
||||
<script src="js/colony_situation_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/world_update.js?v=39.18.18" defer></script>
|
||||
<script src="js/world_tool_actions.js?v=39.18.18" defer></script>
|
||||
<script src="js/world_placement_log.js?v=39.18.18" defer></script>
|
||||
<script src="js/world_event_effects.js?v=39.18.18" defer></script>
|
||||
<script src="js/command_dispatcher.js?v=39.18.18" defer></script>
|
||||
<script src="js/text_catalog.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui.js?v=39.18.18" defer></script>
|
||||
<script src="js/game_dialogs.js?v=39.18.18" defer></script>
|
||||
<script src="js/achievement_catalog.js?v=39.18.18" defer></script>
|
||||
<script src="js/achievements.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_helpers.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_log.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_selected.js?v=39.18.18" defer></script>
|
||||
<script src="js/save_schema.js?v=39.18.18" defer></script>
|
||||
<script src="js/snapshot_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/restore_coordinator.js?v=39.18.18" defer></script>
|
||||
<script src="js/history_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/save_codec.js?v=39.18.18" defer></script>
|
||||
<script src="js/save_storage.js?v=39.18.18" defer></script>
|
||||
<script src="js/save_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_tooltips.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_layout_dialogs.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_ground.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_tools.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_input_shared.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_pointer_action_system.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_input_touch.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_input_mouse.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_bind.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_charts.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_family_data.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_family_async.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_family_layout.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_family_paths.js?v=39.18.18" defer></script>
|
||||
<script src="js/ui_family_render.js?v=39.18.18" defer></script>
|
||||
<script src="js/main.js?v=39.18.18" defer></script>
|
||||
<script src="js/debug_tools.js?v=39.18.18" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
731
js/achievement_catalog.js
Normal file
|
|
@ -0,0 +1,731 @@
|
|||
window.TARINAI_ACHIEVEMENT_CATALOG = {
|
||||
"revision": "2026-07-25-achievement-cleanup-v1",
|
||||
"completionistId": "true_tarinai_observer",
|
||||
"categories": [
|
||||
{
|
||||
"id": "ecology",
|
||||
"title": "生態"
|
||||
},
|
||||
{
|
||||
"id": "population",
|
||||
"title": "人口"
|
||||
},
|
||||
{
|
||||
"id": "experiment",
|
||||
"title": "実験"
|
||||
},
|
||||
{
|
||||
"id": "construction",
|
||||
"title": "建築"
|
||||
},
|
||||
{
|
||||
"id": "operation",
|
||||
"title": "操作"
|
||||
},
|
||||
{
|
||||
"id": "other",
|
||||
"title": "その他"
|
||||
}
|
||||
],
|
||||
"medicineLedgerTypes": [
|
||||
"first_aid",
|
||||
"sedative",
|
||||
"sleep_drug",
|
||||
"laxative",
|
||||
"protein",
|
||||
"niteropu",
|
||||
"ammo",
|
||||
"mystery_drug",
|
||||
"mercury",
|
||||
"giant_drug",
|
||||
"dwarf_drug"
|
||||
],
|
||||
"linkProgressTypes": [
|
||||
"rope",
|
||||
"rod",
|
||||
"spring"
|
||||
],
|
||||
"additiveProgressCaps": {
|
||||
"placementCount": 100,
|
||||
"antKills": 100,
|
||||
"birthCount": 3333,
|
||||
"quickDeleteCount": 30,
|
||||
"undoCount": 20,
|
||||
"redoCount": 20,
|
||||
"directFeedCount": 33,
|
||||
"firstAidHeals": 50,
|
||||
"fightMochiFightCount": 666,
|
||||
"shotCount": 333,
|
||||
"loveMochiBirthCount": 721,
|
||||
"statsButtonPressCount": 30,
|
||||
"totalDeathCount": 10000,
|
||||
"manualTarinaiAddedCount": 100,
|
||||
"robotCleanCount": 200
|
||||
},
|
||||
"definitions": [
|
||||
{
|
||||
"id": "first_birth",
|
||||
"title": "はじめての繁殖",
|
||||
"description": "たりないの繁殖を1回成功させる。",
|
||||
"category": "population",
|
||||
"showLockedDescription": true,
|
||||
"progress": {
|
||||
"type": "counter",
|
||||
"key": "birthCount",
|
||||
"target": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "natural_zunchi_slave",
|
||||
"title": "ずんちどれい",
|
||||
"description": "ずんちどれいが出現する。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "ochibi_natural_slave",
|
||||
"title": "たりないより「たりない」",
|
||||
"description": "おちびが鎖無しでずんちどれいになる。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "natural_tarinai_king",
|
||||
"title": "たりない王",
|
||||
"description": "たりない王が出現する。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "self_zunchi_death",
|
||||
"title": "あぁ^~ たまらねぇぜ",
|
||||
"description": "おしり鋲を抜いた時、自分のずんちで自爆する。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "zunchi_collision_death",
|
||||
"title": "くちゃいのだ!",
|
||||
"description": "ずんちとの衝突で死亡する個体が出現する。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "natural_zunchi_slave_5_generations",
|
||||
"title": "末代までの恥",
|
||||
"description": "5代連続でずんちどれい(鎖なし)",
|
||||
"category": "population",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "natural_tarinai_king_3_generations",
|
||||
"title": "華麗なる王族",
|
||||
"description": "3代連続でたりない王(王冠なし)",
|
||||
"category": "population",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "death_50_in_10_seconds",
|
||||
"title": "6番目の大量絶滅",
|
||||
"description": "10秒以内に50体のたりないが死亡する。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "birth_50_in_60_seconds",
|
||||
"title": "ベビーブーム",
|
||||
"description": "1分以内に30体のたりないが誕生する。",
|
||||
"category": "population",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "great_mother_1000_births",
|
||||
"title": "母なる大地",
|
||||
"description": "たりないを繁殖で累計3333体誕生させる。",
|
||||
"category": "population",
|
||||
"showLockedDescription": true,
|
||||
"progress": {
|
||||
"type": "counter",
|
||||
"key": "birthCount",
|
||||
"target": 3333,
|
||||
"display": "percent",
|
||||
"unit": "体"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "lifespan_completed",
|
||||
"title": "天寿",
|
||||
"description": "たりない1体が天寿を全うする。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "soccer_ball_death",
|
||||
"title": "豆野、殺ッカーやろうぜ!",
|
||||
"description": "サッカーボールとの衝突で、たりない1体が死亡する。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "fight_pair_danger_kill",
|
||||
"title": "喧嘩両成敗",
|
||||
"description": "喧嘩中のたりないを、両方とも危険カテゴリのアイテムで倒す。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "all_non_sleep_diseased_25",
|
||||
"title": "緊急事態宣言",
|
||||
"description": "25体以上が存命し、フィールド上の全員がねむり病以外の病気にかかる。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "colony_happy",
|
||||
"title": "管理された楽園",
|
||||
"description": "コロニー状態をしばらく「幸福」にする。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "direct_feed_33",
|
||||
"title": "餌やりじいさん",
|
||||
"description": "手から直接33回餌を与える。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": false,
|
||||
"progress": {
|
||||
"type": "counter",
|
||||
"key": "directFeedCount",
|
||||
"target": 33
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ignite_during_birth_ritual",
|
||||
"title": "燃え上がる恋",
|
||||
"description": "たりないがヘコヘコ中に着火する。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "laxative_starvation",
|
||||
"title": "内臓全部出た",
|
||||
"description": "下剤の効果中に餓死する。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "idle_observer_5_minutes",
|
||||
"title": "監視カメラ",
|
||||
"description": "3ゲーム日、操作せず画面を見るだけ",
|
||||
"category": "operation",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "placed_objects_100",
|
||||
"title": "物でいっぱい",
|
||||
"description": "配置物を累計100個置く。",
|
||||
"category": "construction",
|
||||
"showLockedDescription": false,
|
||||
"progress": {
|
||||
"type": "counter",
|
||||
"key": "placementCount",
|
||||
"target": 100
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mechanized_industry",
|
||||
"title": "機械化産業",
|
||||
"description": "検知器の信号で接続先を作動させ、ロープ・棒・ばねをそれぞれ1本以上設置する。",
|
||||
"category": "construction",
|
||||
"showLockedDescription": false,
|
||||
"progress": {
|
||||
"type": "mechanized"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "safe_colony_25_5_minutes",
|
||||
"title": "安全第一",
|
||||
"description": "25体以上いる状態をゲーム内5分間維持し、その間寿命以外で死亡者を出さない。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "ants_alive_25",
|
||||
"title": "アリの惑星",
|
||||
"description": "生存しているアリが25体に到達する。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "ants_killed_100",
|
||||
"title": "害虫駆除",
|
||||
"description": "アリを累計100体倒す。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": false,
|
||||
"progress": {
|
||||
"type": "counter",
|
||||
"key": "antKills",
|
||||
"target": 100
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "secret_collection_9_slots",
|
||||
"title": "秘密のコレクション",
|
||||
"description": "セーブスロット9つをすべて埋める。",
|
||||
"category": "operation",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "pause_spam_4_in_1_second",
|
||||
"title": "何やってるの?",
|
||||
"description": "何もない部分を1秒間に4回連打する。",
|
||||
"category": "operation",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "continuous_play_1_hour",
|
||||
"title": "めっちゃたりない観察",
|
||||
"description": "1時間連続プレイする。",
|
||||
"category": "other",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "below_absolute_zero_item",
|
||||
"title": "物理法則を下側に超越",
|
||||
"description": "温度計算されるアイテムを1つ絶対零度を下回る温度にする。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "undo_mass_revival",
|
||||
"title": "見なかったことにしよう",
|
||||
"description": "一度のUndoで10体以上の死亡をなかったことにする。",
|
||||
"category": "operation",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "robot_cleaner_100",
|
||||
"title": "清掃業者",
|
||||
"description": "ロボ掃除機にずんち・死骸・汚れを累計200個処理させる。",
|
||||
"category": "construction",
|
||||
"showLockedDescription": true,
|
||||
"progress": {
|
||||
"type": "counter",
|
||||
"key": "robotCleanCount",
|
||||
"target": 200,
|
||||
"display": "count",
|
||||
"unit": "個"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "held_30_seconds",
|
||||
"title": "空中生活",
|
||||
"description": "同じたりないを30秒間つまみ続ける。",
|
||||
"category": "operation",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "minimalist_happy",
|
||||
"title": "ミニマリスト",
|
||||
"description": "プレイ開始6日目以降、配置物を10個以下に抑えてコロニー状態を「幸福」にする。",
|
||||
"category": "construction",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "overprotective",
|
||||
"title": "過保護",
|
||||
"description": "同じたりないに手から10回餌を与え、3回以上薬で治療する。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "self_sufficient",
|
||||
"title": "自給自足",
|
||||
"description": "複製機がない状態で、20体以上を直接給餌せず10分間維持する。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "unplanned_city_30",
|
||||
"title": "無計画都市",
|
||||
"description": "配置後30秒以内に削除する操作を累計30回行う。",
|
||||
"category": "construction",
|
||||
"showLockedDescription": false,
|
||||
"progress": {
|
||||
"type": "counter",
|
||||
"key": "quickDeleteCount",
|
||||
"target": 30
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "sauna_cold_plunge",
|
||||
"title": "サウナ水風呂",
|
||||
"description": "暑すぎる状態になったたりないを、15秒以内に寒すぎる状態にする。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "rain_shelter_all",
|
||||
"title": "雨宿り完了",
|
||||
"description": "雨天時、20体以上の全員をパラソル・巣箱・土管で雨宿りさせる。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "medicine_ledger_all",
|
||||
"title": "おくすり手帳全埋め",
|
||||
"description": "全種類の薬を少なくとも1回使用する。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": false,
|
||||
"progress": {
|
||||
"type": "set",
|
||||
"key": "medicineTypes",
|
||||
"required": "medicineLedgerTypes"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mercury_lifespan",
|
||||
"title": "不老不死まであと一歩",
|
||||
"description": "水銀を使用したたりないが天寿を全うする。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "enemy_enemy_friend",
|
||||
"title": "敵の敵は味方",
|
||||
"description": "たりない30体以上を無傷で保ちながら、危険アイテムでアリを10体倒す。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "revolution",
|
||||
"title": "革命",
|
||||
"description": "ずんちどれいが、たりない王を喧嘩で死亡させる。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "fuel_to_fire",
|
||||
"title": "火に油",
|
||||
"description": "けんか餅を与えたたりないが30秒以内に喧嘩を始める。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "undo_20",
|
||||
"title": "やっぱなし",
|
||||
"description": "Undoを累計20回成功させる。",
|
||||
"category": "operation",
|
||||
"showLockedDescription": false,
|
||||
"progress": {
|
||||
"type": "counter",
|
||||
"key": "undoCount",
|
||||
"target": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "redo_20",
|
||||
"title": "やっぱあり",
|
||||
"description": "Redoを累計20回成功させる。",
|
||||
"category": "operation",
|
||||
"showLockedDescription": false,
|
||||
"progress": {
|
||||
"type": "counter",
|
||||
"key": "redoCount",
|
||||
"target": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "eternal_history_generation_10",
|
||||
"title": "悠久の歴史",
|
||||
"description": "世代が10に到達する。",
|
||||
"category": "population",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "king_full_satisfaction",
|
||||
"title": "五体満足",
|
||||
"description": "たりない王1個体にプロテインと巨大薬の効果を同時に付与する。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "slave_zero_satisfaction",
|
||||
"title": "零体満足",
|
||||
"description": "ずんちどれい1個体にンイテロプと矮小薬の効果を同時に付与する。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "town_doctor_50",
|
||||
"title": "町のお医者さん",
|
||||
"description": "救急薬を累計50回使用し、体力を回復させる。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": true,
|
||||
"progress": {
|
||||
"type": "counter",
|
||||
"key": "firstAidHeals",
|
||||
"target": 50,
|
||||
"display": "count",
|
||||
"unit": "回"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "poke_plushie_fling",
|
||||
"title": "割とふわふわ",
|
||||
"description": "ぬいぐるみを「つつく」で弾き飛ばす。",
|
||||
"category": "operation",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "chaos_seeker_666_fights",
|
||||
"title": "混沌ヲ希求スル者",
|
||||
"description": "けんか餅の影響下で喧嘩を累計666回発生させる。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": true,
|
||||
"progress": {
|
||||
"type": "counter",
|
||||
"key": "fightMochiFightCount",
|
||||
"target": 666,
|
||||
"display": "percent",
|
||||
"unit": "回"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "clean_freak_robot_only",
|
||||
"title": "潔癖症",
|
||||
"description": "ロボット掃除機の設定全項目にチェックを入れて稼働させ、フィールド上にロボット掃除機以外を存在させない。",
|
||||
"category": "construction",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "true_tarinai_observer",
|
||||
"title": "真・たりない観察",
|
||||
"description": "これ以外の全ての実績を達成する。",
|
||||
"category": "other",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "sniper_333_shots",
|
||||
"title": "狙撃手",
|
||||
"description": "累計333発撃つ。",
|
||||
"category": "operation",
|
||||
"showLockedDescription": false,
|
||||
"progress": {
|
||||
"type": "counter",
|
||||
"key": "shotCount",
|
||||
"target": 333
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "megalopolis",
|
||||
"title": "メガロポリス",
|
||||
"description": "複製機を10個以上、干草寝床・巣箱・土管を合計15個以上設置し、たりないを100体以上生存させる。",
|
||||
"category": "construction",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "fertility_seeker_721_love_births",
|
||||
"title": "豊穣ヲ希求スル者",
|
||||
"description": "へこ餅の影響下で繁殖を721回成功させる。",
|
||||
"category": "population",
|
||||
"showLockedDescription": true,
|
||||
"progress": {
|
||||
"type": "counter",
|
||||
"key": "loveMochiBirthCount",
|
||||
"target": 721,
|
||||
"display": "percent",
|
||||
"unit": "回"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "low_fps_single_digit",
|
||||
"title": "スペックがたりない",
|
||||
"description": "fpsを1桁にする。",
|
||||
"category": "other",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "continuous_play_24_hours",
|
||||
"title": "寝ろ",
|
||||
"description": "24時間連続でプレイする。",
|
||||
"category": "other",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "sandbox_five_toilets",
|
||||
"title": "砂場",
|
||||
"description": "トイレの砂をフィールドに5個設置する。",
|
||||
"category": "construction",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "park_ground_changed",
|
||||
"title": "そこ公共空間だよ?",
|
||||
"description": "公園の地面を勝手に変更する。",
|
||||
"category": "construction",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "ground_change_4_in_1_second",
|
||||
"title": "破格の工事費用",
|
||||
"description": "地面を変えすぎて破産する。",
|
||||
"category": "construction",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "ant_nest_without_tarinai",
|
||||
"title": "アリ観察",
|
||||
"description": "アリの巣があるが、たりないがいない。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "sticky_bomb_15_passes",
|
||||
"title": "命のバトン",
|
||||
"description": "粘着ボムを爆発までに15回以上パスする。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "daily_play_7_days",
|
||||
"title": "毎日たりない観察",
|
||||
"description": "7日連続でプレイする。",
|
||||
"category": "other",
|
||||
"showLockedDescription": true,
|
||||
"progress": {
|
||||
"type": "value",
|
||||
"key": "dailyPlayStreak",
|
||||
"target": 7,
|
||||
"display": "percent",
|
||||
"unit": "日"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "wire_shock_7_tarinai",
|
||||
"title": "抵抗器じゃない",
|
||||
"description": "電流が流れている1本の電線で7体のたりないを感電させる。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "information_industry",
|
||||
"title": "情報化産業",
|
||||
"description": "基盤同士を電線類でつなぎ、信号を流す。",
|
||||
"category": "construction",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "strength_in_numbers",
|
||||
"title": "数こそ力",
|
||||
"description": "生存しているたりないが100体に到達する。",
|
||||
"category": "population",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "elite_few",
|
||||
"title": "少数精鋭",
|
||||
"description": "たりないが25体以下の状態を5日間維持する。",
|
||||
"category": "population",
|
||||
"showLockedDescription": true
|
||||
},
|
||||
{
|
||||
"id": "zunchi_overflow",
|
||||
"title": "踩屎感",
|
||||
"description": "たりないが20体以上いる状態で、ずんちの数がたりないの数を上回る。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "comfortable_beds",
|
||||
"title": "快適な寝床",
|
||||
"description": "生存している全てのたりないに行き渡る数の寝床を用意する。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "stone_pillow",
|
||||
"title": "石に枕す",
|
||||
"description": "たりないが30体以上いる状態で、寝床充足率を0%にする。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "across_seasons",
|
||||
"title": "季節を越えて",
|
||||
"description": "春夏秋冬の四季を一巡する。",
|
||||
"category": "other",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "favorite_one",
|
||||
"title": "推し",
|
||||
"description": "ある個体をお気に入りに登録する。",
|
||||
"category": "ecology",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "statistician",
|
||||
"title": "統計学者",
|
||||
"description": "統計の個体数・欲求・環境・グラフ表示切替ボタンを累計30回押す。",
|
||||
"category": "operation",
|
||||
"showLockedDescription": false,
|
||||
"progress": {
|
||||
"type": "counter",
|
||||
"key": "statsButtonPressCount",
|
||||
"target": 30,
|
||||
"display": "count",
|
||||
"unit": "回"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "well_informed",
|
||||
"title": "情報通",
|
||||
"description": "プッシュ通知をONにする。",
|
||||
"category": "operation",
|
||||
"showLockedDescription": false
|
||||
},
|
||||
{
|
||||
"id": "lively_making",
|
||||
"title": "賑わいづくり",
|
||||
"description": "「たりないを追加」で100体追加する。",
|
||||
"category": "population",
|
||||
"showLockedDescription": true,
|
||||
"progress": {
|
||||
"type": "counter",
|
||||
"key": "manualTarinaiAddedCount",
|
||||
"target": 100,
|
||||
"display": "count",
|
||||
"unit": "体"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "memento_mori",
|
||||
"title": "多数の死は統計上の数字でしかない",
|
||||
"description": "たりないが合計10000体死亡する。",
|
||||
"category": "population",
|
||||
"showLockedDescription": true,
|
||||
"progress": {
|
||||
"type": "counter",
|
||||
"key": "totalDeathCount",
|
||||
"target": 10000,
|
||||
"display": "percent",
|
||||
"unit": "体"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mad_scientist",
|
||||
"title": "マッドサイエンティスト",
|
||||
"description": "異なる10匹のたりないに怪しい薬を投与する。",
|
||||
"category": "experiment",
|
||||
"showLockedDescription": false,
|
||||
"progress": {
|
||||
"type": "unique",
|
||||
"key": "mysteryDrugTarinaiIds",
|
||||
"target": 10,
|
||||
"display": "count",
|
||||
"unit": "匹"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||