diff --git a/AI_MAP.md b/AI_MAP.md new file mode 100644 index 0000000..78a68e1 --- /dev/null +++ b/AI_MAP.md @@ -0,0 +1,58 @@ +# tarinai_ AI map + +Purpose: static Japanese browser simulation/game. Optimize context by reading this file only after `README.md`, then open the relevant subsystem files. For exact file expansion, use `scripts/ai_inventory.ps1` or `scripts/ai_inventory.py`. + +## Architecture + +- Entrypoint: `index.html` defines the app shell, canvas, dialogs, right panels, and exact script order. +- Runtime style: plain browser JS, global IIFEs, exports on `window`, no bundler/module loader. +- Version/cache: `app_manifest.json`, `js/version.js`, and generated `service-worker.js` share `39.15.47`. +- Main loop: `js/main.js` initializes assets/audio/world/UI/save, then drives update/render. +- Update order: `js/system_order.js` calls phase facades from `js/world_update_phases.js` / `js/simulation_systems.js`. +- Command path: UI/input emits command objects; `js/command_dispatcher.js` routes tool, pointer, selection, save, reset, and UI intents. +- Persistence: save files are schema/codec/storage/coordinator modules; snapshot/history modules keep world restoration and stats consistent. + +## Subsystem Map + +- Core/bootstrap: `js/version.js`, `js/event_bus.js`, registries, `js/data.js`, `js/ground_types.js`, `js/health.js`, `js/main.js`, `js/debug_tools.js`, `js/perf_profiler.js`. +- Rendering/assets/audio: `js/assets.js`, `js/audio.js`, `js/render.js`, `js/tarinai_render.js`, `js/item_render_runtime.js`, `js/sound_pack.js`. +- Simulation phases: `js/sim_core.js`, `js/simulation*.js`, `js/world_update*.js`, `js/system_order.js`. +- World domain: `js/world*.js`, `js/weather_system.js`; covers state, camera/view, environment, combat/effects, placement/history, tools, family/social, ants, spatial budgets. +- Creature domain: `js/tarinai*.js`; covers state, identity, needs, actions, forced behavior, social life, item effects, disease/nests, movement/update pipeline, rendering. +- Item/structure domain: `js/item*.js`, `js/items.js`, `js/structures.js`, `js/structure_lifecycle.js`; covers definitions, spawning defaults, lifecycle, dynamic behavior, rendering. +- Physics/mechanics: `js/physics*.js`, `js/mechanical_system.js`, `js/constraint_system.js`, `js/collision_footprint_system.js`. +- UI/input: `js/ui*.js`, `js/text_catalog.js`, `js/command_dispatcher.js`; covers panels, tools, selected creature, logs, charts, family tree, mouse/touch, dialogs, labels. +- Family tree: `js/family_graph.js`, `js/ui_family_*.js`; data transform, layout, paths, async rendering, validation. +- Save/history: `js/save*.js`, `js/snapshot_system.js`, `js/restore_coordinator.js`, `js/history_system.js`. +- Scripts: `scripts/generate_app_files.py`, `scripts/generate_item_icons.py`, `scripts/regression_check.py`, `scripts/ai_inventory.py`, `scripts/ai_inventory.ps1`. + +## File Pattern Semantics + +- `*_pipeline.js`: ordered runner/facade for update or lifecycle work. +- `*_step_*.js`: narrow pipeline step wrapper. +- `*_system.js`: domain service, phase service, or compatibility facade. +- `*_runtime.js`: stable runtime-facing facade around split modules. +- `*_registry.js`: definition registry and lookup/normalization helpers. +- `world_*`: world-owned behavior; `tarinai_*`: creature-owned behavior; `item_*`: item-owned behavior; `ui_*`: DOM/input/panel behavior. +- `assets/sprites/tarinai_*.webp`: creature visual state sprites; numeric prefix is stable asset ID/order. +- `assets/ui/tool_*`: tool palette icons; `assets/ui/favicon*`, `apple-touch-icon`, `ecology_*` are app/help icons. +- `assets/objects/*`: field object sprites for ants, zunchi, pushpin, oshibyo, genkotsu, plushie. +- `assets/sounds/voice_*`: creature voice samples; `shoot_*`, `major_damage_*`, `firecracker_*` are SFX. + +## Task-Oriented Read Sets + +- UI/layout bug: `index.html`, relevant `css/*.css`, `js/ui_bind.js`, `js/ui_layout_dialogs.js`, matching `js/ui_*.js`. +- Tool behavior: `js/command_dispatcher.js`, `js/world_tool_actions.js`, `js/item_registry.js`, relevant `js/item_*` or `js/tarinai_item_*`. +- Creature behavior: `js/tarinai.js`, `js/tarinai_update_pipeline.js`, relevant `js/tarinai_*`, `js/system_order.js`. +- World/environment/combat: matching `js/world_*`, `js/simulation_*`, `js/system_order.js`. +- Save/load: `js/save_schema.js`, `js/save_codec.js`, `js/save_system.js`, `js/snapshot_system.js`, `js/restore_coordinator.js`. +- Family tree: `js/world_family_social.js`, `js/family_graph.js`, `js/ui_family_*.js`. +- Static app/cache/version: `app_manifest.json`, `scripts/generate_app_files.py`, `service-worker.js`, `js/version.js`, `index.html`. + +## Invariants + +- Preserve `index.html` script order unless all dependent manifests/generated files are updated together. +- Keep `app_manifest.json`, `js/version.js`, `service-worker.js`, and cache query versions synchronized. +- Keep global exports for compatibility with regression checks and browser runtime. +- Prefer focused edits in the owning subsystem; split modules are intentional compatibility boundaries. +- Run `node --check js/*.js` and `python scripts/regression_check.py` for behavior changes. diff --git a/FILES.json b/FILES.json new file mode 100644 index 0000000..89f458d --- /dev/null +++ b/FILES.json @@ -0,0 +1,175 @@ +{ + "purpose": "Token-efficient machine-readable routing for tarinai_. Use scripts/ai_inventory.py for exact current file expansion.", + "version": "39.15.77", + "entrypoints": { + "app": "index.html", + "runtime": "js/main.js", + "update_order": "js/system_order.js", + "manifest": "app_manifest.json", + "service_worker": "service-worker.js" + }, + "read_strategy": { + "default": [ + "README.md" + ], + "architecture": [ + "README.md", + "AI_MAP.md" + ], + "exact_inventory": [ + "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/ai_inventory.ps1 -All", + "python scripts/ai_inventory.py --all" + ], + "machine_routing": [ + "FILES.json" + ] + }, + "groups": [ + { + "id": "root", + "patterns": [ + ".gitignore", + ".htaccess", + "app_manifest.json", + "favicon.ico", + "index.html", + "README.md", + "AI_MAP.md", + "GAME_FEATURES.md", + "FILES.json", + "service-worker.js" + ], + "meaning": "Repo/app shell metadata, server/static cache config, docs, manifest, entrypoint." + }, + { + "id": "css", + "patterns": [ + "css/*.css" + ], + "meaning": "Base, layout, panel, component, and mobile responsive styling." + }, + { + "id": "scripts", + "patterns": [ + "scripts/*.py", + "scripts/*.ps1" + ], + "meaning": "Generators, regression checks, and token-aware AI inventory tooling." + }, + { + "id": "core", + "patterns": [ + "js/version.js", + "js/event_bus.js", + "js/registry_base.js", + "js/disease_registry.js", + "js/sound_pack.js", + "js/data.js", + "js/ground_types.js", + "js/health.js", + "js/input_mode_manager.js", + "js/main.js", + "js/math.js", + "js/perf_profiler.js", + "js/debug_tools.js" + ], + "meaning": "Bootstrap, shared constants/helpers, registries, config, profiling/debug." + }, + { + "id": "assets_runtime", + "patterns": [ + "js/assets.js", + "js/audio.js", + "js/render.js", + "js/tarinai_render.js", + "js/item_render_runtime.js" + ], + "meaning": "Image/audio loading and canvas rendering." + }, + { + "id": "simulation", + "patterns": [ + "js/sim_core.js", + "js/simulation*.js", + "js/system_order.js" + ], + "meaning": "Simulation primitives, concrete systems, phase ordering." + }, + { + "id": "world", + "patterns": [ + "js/world*.js", + "js/weather_system.js" + ], + "meaning": "World state/view/update/tools/environment/combat/family/social/ants." + }, + { + "id": "physics", + "patterns": [ + "js/physics*.js", + "js/mechanical_system.js", + "js/constraint_system.js", + "js/collision_footprint_system.js" + ], + "meaning": "Geometry, collisions, constraints, mechanical item behavior." + }, + { + "id": "items", + "patterns": [ + "js/item*.js", + "js/items.js", + "js/structures.js", + "js/structure_lifecycle.js" + ], + "meaning": "Item/structure registry, lifecycle, dynamic behavior, rendering." + }, + { + "id": "tarinai", + "patterns": [ + "js/tarinai*.js", + "js/ants.js" + ], + "meaning": "Creature state/actions/needs/social life/item effects/disease/rendering plus ant actor model." + }, + { + "id": "save", + "patterns": [ + "js/save*.js", + "js/snapshot_system.js", + "js/restore_coordinator.js", + "js/history_system.js" + ], + "meaning": "Snapshot schema/codec/storage/restore/history." + }, + { + "id": "ui", + "patterns": [ + "js/ui*.js", + "js/text_catalog.js", + "js/command_dispatcher.js", + "js/family_graph.js" + ], + "meaning": "DOM UI, input, tools, selected panel, logs, charts, family tree, text labels, command routing." + }, + { + "id": "assets", + "patterns": [ + "assets/sprites/tarinai_*.webp", + "assets/ui/tool_*", + "assets/ui/favicon*", + "assets/ui/apple-touch-icon.png", + "assets/ui/ecology_*.webp", + "assets/objects/*", + "assets/sounds/*" + ], + "meaning": "Creature sprites, UI/tool icons, field object images, voice/SFX samples." + } + ], + "conventions": { + "*_pipeline.js": "ordered runner/facade", + "*_step_*.js": "pipeline stage wrapper", + "*_system.js": "domain service or compatibility facade", + "*_runtime.js": "stable runtime-facing facade", + "*_registry.js": "definition lookup/normalization" + } +} diff --git a/GAME_FEATURES.md b/GAME_FEATURES.md new file mode 100644 index 0000000..a3d0acb --- /dev/null +++ b/GAME_FEATURES.md @@ -0,0 +1,171 @@ +# tarinai_ Game Features + +This document describes the playable features of `tarinai_`: what the player sees, what tools do, and how tarinai behave. It is intentionally player/design-facing, not an implementation map. + +## Game Overview + +`tarinai_` is an observation sandbox about small creatures called tarinai. The player places food, furniture, hazards, machines, and environment tools, then watches how individuals live, move, eat, sleep, panic, fight, reproduce, get sick, recover, and leave family records. + +The core loop is: + +1. Choose a field and observe the colony. +2. Place tools or objects that change the environment. +3. Select individual tarinai to inspect health, needs, personality, relations, diseases, records, and current behavior. +4. Watch the event log, colony graphs, and family tree to understand long-term outcomes. + +## Main UI + +- **Canvas / field**: the main simulation area. Tarinai, items, effects, ants, weather, and terrain are drawn here. +- **Top buttons**: pause/resume, speed control, ecology guide, sound settings, credits, save, and reset. +- **Mobile mode controls**: auto, camera, and tool modes for touch screens. +- **Right panel tabs**: jump between main game, selected tarinai data, tools, colony, events, and family tree. +- **Tarinai data panel**: appears when a tarinai is selected. Shows health, needs, personality, active effects, relations, inventory, behavior reason, and records. +- **Tool palette**: grouped buttons for operation, food, medicine, habitat, physics, and danger tools. +- **Colony panel**: shows population, deaths, colony mood, ground type, and charts for population, needs, and objects. +- **Event panel**: logs births, deaths, accidents, fights, food events, weather, grass, and relation changes. Optional push-style notifications can be enabled by event type. +- **Family tree**: shows parent-child generations, inherited stats, personality tags, family links, and archived lineage records. +- **Ecology dialog**: in-game guide explaining tarinai needs, stress, food, sleep, personality, family, disease, accidents, tools, and colony reading. +- **Sound menu**: master sound plus voice, notification, and operation categories. + +## Tool Categories + +### Operation + +- **解除 / clear**: cancels the selected tool and returns to normal observe mode. +- **たりないデータ / observe**: select a tarinai or inspect compatible objects such as signboards. +- **追加 / new**: calls a new tarinai into the field. +- **つつく / poke**: pokes tarinai and can push balls. +- **つまむ / pinch**: drags tarinai and some placed objects. +- **コピー / copy**: copies a placed item and pastes a new instance with its settings. +- **戻す / undo** and **進む / redo**: reverse or reapply placement/edit actions. +- **削除 / delete**: removes placed tools, objects, and stains. +- **範囲削除 / area delete**: removes many nearby objects or stains around the cursor. +- **洗浄 / water hose**: drag to clean dirt, zunchi, corpses, and stains. + +### Food + +- **ずんだ餅 / sweet**: favored food; can cure some illness. +- **へこ餅 / love mochi**: makes reproduction behavior more likely for a while. +- **けんか餅 / fight mochi**: makes fighting behavior more likely for a while. +- **草 / grass**: food and comfort source; can grow and be used for simple beds. +- **水滴 / water**: drinkable water, also appears during rain and can be set in duplicators. +- **ずんだ汁 / zunda juice**: clears item effects from a tarinai. +- **複製機 / duplicator**: can be loaded with compatible food, medicine, grass, water, pushpins, or oshibyo and then produces or applies that content. + +### Medicine + +- **ねむり薬 / sleep drug**: causes sleepiness or sleep disease; damage can cure the disease. +- **下剤 / laxative**: causes repeated zunchi and reduces food benefit. +- **プロテイン / protein**: permanent power mode; more damage dealt, lower disease chance. +- **ンイテロプ / niteropu**: permanent weak mode; less damage dealt, higher disease chance. +- **弾薬 / ammo**: temporary high-speed movement and strong collision impact. +- **怪しい薬 / mystery drug**: randomly applies an eligible item effect or disease. +- **水銀 / mercury**: increases lifespan multiplier on each use. +- **巨大薬 / giant drug**: greatly increases size, attack, and speed, but deals ongoing damage. +- **矮小薬 / dwarf drug**: greatly decreases size, attack, and speed, and also deals ongoing damage. + +### Habitat + +- **ずんち / zunchi**: tarinai waste; affects disease and can fertilize grass. +- **ロボ掃除機 / robot_cleaner**: moves through the field at fixed normal speed 60, or 120 in high-speed mode. It checks the nearest matching cleanup target every 2 seconds, keeps pursuing it between scans, and cleans every matching object it touches. The 死骸 target also covers blood traces; clicking the item opens its target settings. +- **干草寝床 / bed**: placeable sleeping spot; helps HP recovery. +- **巣箱 / nest box**: reduces stress nearby; improves sleep quality when used. +- **ボール / ball**: toy that tarinai can play with and the player can poke. +- **看板 / signboard**: editable text object; inspect with observe after placing. + +### Physics + +- **ヒモ / rope**: connect two targets with a flexible link. +- **棒 / rod**: connect two targets with a rigid link. +- **柵 / fence**: blocks tarinai movement; Q/E rotate by 45 degrees, Shift+Q/E by 5 degrees. +- **バウンス柵 / bounce fence**: fence that rebounds things that hit it. +- **ゲート柵 / gate fence**: click to open or close. +- **回転体 / rotator**: custom mechanical shape that rotates; click to edit shape and speed. +- **毒ブロック / poison block**: custom hazard block; touching it damages skin; click to edit shape. +- **往復体 / reciprocator**: custom mechanical shape that moves back and forth; click to edit shape and speed. + +### Danger + +- **石 / stone**: heavy object; dangerous when dropped or moved into others. +- **げんこつ / genkotsu**: large fist impact on the ground. +- **爆竹 / firecracker**: creates an explosion. +- **画鋲 / pushpin**: rolls when dropped; can lodge in tarinai and cause panic plus continuous damage. +- **おしり鋲 / oshibyo**: blocks zunchi when lodged; pulling it out releases stored zunchi. +- **射撃 / shoot**: attacks with a gun effect. +- **アリの巣 / ant nest**: spawns ants that search for tarinai and carry them to the nest. + +## Tarinai Needs And Stats + +Tarinai behavior is shaped by health, energy, needs, stress, personality, family, disease, and nearby objects. + +Key needs include: + +- **Food / hunger**: hungry tarinai seek grass, sweets, food, water-related options, or other edible targets. +- **Sleep**: sleepy tarinai seek beds, nest boxes, grass beds, or eventually sleep anywhere. +- **Safety**: danger, pain, hazards, ants, fights, and panic increase defensive behavior. +- **Social / relations**: tarinai can seek friends, follow parents, avoid enemies, fight rivals, or approach mates. +- **Fulfillment / comfort**: toys, ground, sun, shelter, and environment affect mood and stress. +- **Health**: damage, disease, stress, and recovery decide whether tarinai live, weaken, or die. + +Stress changes with fear, crowding, dirt, disease, fights, ground type, and environment. High stress makes fleeing, intimidation, sickness, and poor behavior more likely. + +Personality and genetics affect tendencies such as aggression, openness, sociability, nervousness, body size, lifespan, damage, and speed. Some traits are inherited by children and shown in the family tree. + +## Movement And Behavior + +Tarinai are autonomous. Their movement is usually a result of current state, target choice, and local environment. + +Common behavior states: + +- **Idle / wander**: waits, watches, or moves lightly around the field. +- **Seek food**: moves toward edible items when hungry or affected by disease. +- **Eat / drink**: consumes food, grass, water, medicine, or special items. +- **Seek bed / sleep**: finds a bed, nest box, grass bed, or sleeps in place. +- **Play ball**: approaches and interacts with balls. +- **Seek material / build**: gathers material and creates simple structures such as grass beds. +- **Seek friend / follow parent**: moves toward social targets or parents. +- **Fight / seek enemy**: approaches and attacks rivals, especially under fight effects or aggression. +- **Panic / flee**: runs from danger, pain, destroyed owned objects, hazards, or fear triggers. +- **Intimidate**: threatens another tarinai or ants instead of directly fleeing or attacking. +- **Birth ritual**: pre-birth/reproduction interaction with a partner. +- **Sunbath**: rests in good sun to reduce stress. +- **Disease states**: zunchi sickness, sleep disease, explosion disease, and injury sickness can override normal priorities. +- **Frozen**: outside-field preservation state. + +Tarinai can be pushed by physics objects, carried or affected by ants, dragged by the player, attached with ropes/rods, blocked by fences, and damaged by hazards. Some objects become owned or emotionally important, so losing them can trigger panic. + +## Environment + +- **Field types**: park, garden, and cage provide different field sizes. +- **Ground types**: sand, dirt, concrete, blanket, foot massage, ice, and laboratory-style ground affect stress, grass, and movement. +- **Weather**: rain and other weather influence water, mood, and environment effects. +- **Grass ecology**: grass grows, feeds tarinai, comforts them, and can become bed material. +- **Dirt and corpses**: zunchi, splats, corpses, and ant corpses affect hygiene, disease, and observation results. +- **Day/light conditions**: lighting and sun influence rendering and sunbath behavior. + +## Disease And Recovery + +Diseases and item effects can strongly redirect behavior. + +- **Zunchi disease**: changes food/water priorities and makes the tarinai feel unwell. +- **Sleep disease**: causes sleep-focused behavior. +- **Explosion disease**: creates dangerous explosive outcomes and water-seeking pressure. +- **Fight disease / injury sickness**: causes unstable movement or fighting-related behavior. +- **Item effects**: mochi, medicine, size drugs, mercury, ammo, and power modes create temporary or permanent modifiers. + +Recovery depends on the disease or effect. Water, rest, food, medicine, zunda juice, damage, environment cleanup, or time can all matter depending on the case. + +## Family, Records, And Colony Observation + +- Tarinai can form parent-child records and partner links. +- Children inherit parts of their parents' tendencies and are grouped by generation. +- Family tree nodes show portraits, inherited stats, personality tags, and relationship structure. +- Event logs record important simulation events such as birth, death, accidents, fights, weather, grass, and relation changes. +- Colony charts summarize population, needs, and object counts over time. +- Selected tarinai records help explain why an individual is acting a certain way. + +## Save, Reset, And Persistence + +- The app supports manual save and restore through browser storage. +- Reset opens field selection and starts a new field state. +- Service worker caching allows the static app shell to remain available after load, depending on browser support. diff --git a/README.md b/README.md new file mode 100644 index 0000000..51fe40e --- /dev/null +++ b/README.md @@ -0,0 +1,87 @@ +# tarinai_ + +AI-first entrypoint for a static browser simulation/game. Keep this file small; read deeper docs only when needed. + +## v39.15.70 robot cleaner tuning +- Tuned ロボ掃除機 targeting: normal speed is 60, high-speed mode is 120, and speed no longer changes based on the selected cleanup targets. +- The cleaner checks the nearest matching target every 2 seconds, then keeps trying to clean that target between scans. Contact cleanup still removes every matching object it touches. +- The 死骸 target also covers blood traces. When たりない is enabled, nearby Tarinai panic while chased and are cleaned on contact. + +## Read Strategy + +- Start here for architecture, script order constraints, and where to look next. +- For game mechanics, UI, items, and tarinai behavior, read `GAME_FEATURES.md`. +- For subsystem work, read `AI_MAP.md` plus only the matching files. +- For exact file inventory, run `powershell -NoProfile -ExecutionPolicy Bypass -File scripts/ai_inventory.ps1 -All` or narrow it: `powershell -NoProfile -ExecutionPolicy Bypass -File scripts/ai_inventory.ps1 ui`. Python equivalent: `python scripts/ai_inventory.py --all`. +- For machine-readable routing, read `FILES.json`. +- Avoid loading all assets into context; asset filenames are mostly self-describing and grouped by glob. + +## Runtime Shape + +- No bundler; `index.html` loads ordered global-IIFE scripts with `?v=39.15.47`. +- Mutable runtime lives on `window`; primary entities are `World`, `Tarinai`, items, structures, ants, family graph, weather, and save snapshots. +- Update loop: `main.js` creates the world/UI loop; `World.update` delegates to `TarinaiSystemOrder.update`; `system_order.js` runs named phases. +- UI loop: pointer/tool/UI intents go through `command_dispatcher.js`; canvas drawing is in render modules; side panels are `ui_*.js`. +- Generated/static app shell: `app_manifest.json`, `service-worker.js`, and `js/version.js` must stay version-aligned. + +## File Routing + +- `index.html`: DOM shell, CSS/JS load order, panels/dialogs/canvas. +- `css/*.css`: base/layout/panel/components/mobile styling. +- `js/world*.js`: world state, view, update phases, tools, placement, combat, environment, family/social, ants. +- `js/tarinai*.js`: creature state, needs, actions, behavior, social life, item effects, update pipeline, rendering. +- `js/item*.js`, `js/structures*.js`: item registry/runtime/lifecycle/dynamic behavior/rendering plus structures. +- `js/simulation*.js`, `js/system_order.js`, `js/sim_core.js`: concrete update systems and phase ordering. +- `js/ui*.js`, `js/text_catalog.js`, `js/command_dispatcher.js`: DOM UI, input, selected panel, logs, charts, family tree, tools, labels. +- `js/save*.js`, `js/snapshot_system.js`, `js/restore_coordinator.js`, `js/history_system.js`: persistence and history. +- `js/physics*.js`, `js/mechanical_system.js`, `js/constraint_system.js`, `js/collision_footprint_system.js`: geometry, mechanics, constraints, collisions. +- `assets/sprites/tarinai_*.webp`: creature state sprites. +- `assets/ui/tool_*`: tool palette icons; other `assets/ui/*` are app/help icons. +- `assets/objects/*`: field object images. +- `assets/sounds/*`: voice and SFX samples. +- `scripts/*`: generators, regression checks, and AI inventory. + +## Conventions + +- `*_pipeline` = ordered runner/facade. +- `*_step_*` = pipeline stage wrapper. +- `*_system` = domain service or compatibility facade. +- `*_runtime` = stable runtime-facing facade. +- `*_registry` = definition lookup/normalization. +- Many split files preserve regression contracts; do not collapse them casually. + +## Checks + +- Syntax: `node --check js/*.js` +- Regression: `python scripts/regression_check.py` +- Inventory sanity: `powershell -NoProfile -ExecutionPolicy Bypass -File scripts/ai_inventory.ps1 -All` + + +## v39.15.47 UI media isolation / scroll repair + +- PC / smartphone UI override rules are separated into `@media (min-width: 981px)` and `@media (max-width: 980px)`. +- PC family tree card is restored below the main game layout, not inside the right menu. +- Smartphone manual scroll no longer uses internal vertical scroll traps for event log / family tree content. +- Auto-scroll does not open collapsed cards. + + +## v39.15.47 event/family/oshibyo UI fixes + +- Event UI hides direct player item placement records while keeping those records in the internal log. +- Collapsed Event UI no longer leaves a large blank area from the log-card min-height. +- Family tree nodes show inherited stats in a 2x2 grid and move the tarinai portrait to the upper-right. +- Oshibyo lodged position is anchored to the left side of the tarinai sprite. + + +## v39.15.47 family polish + +- Family tree generation row spacing is tightened. +- Family tree personality tags also use a 2x2 grid (up to 4 visible tags). +- Sprinkler effective cleaning radius is increased to 3x the previous value and still filters targets by actual distance. + + +## v39.15.47 owned object panic / shoot effect + +- Tarinai panic when their own plushie or simple grass bed is destroyed. +- The selected Tarinai data behavior text explicitly shows the owned-object panic reason. +- Shooting impact effects are larger and remain visible for slightly longer. diff --git a/app_manifest.json b/app_manifest.json index caa7202..0e1a0fe 100644 --- a/app_manifest.json +++ b/app_manifest.json @@ -1,5 +1,5 @@ { - "version": "39.15.18", + "version": "39.15.90", "css": [ "css/base.css", "css/layout.css", @@ -13,16 +13,26 @@ "js/registry_base.js", "js/disease_registry.js", "js/sound_pack.js", + "js/deterministic_helpers.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/ground_types.js", "js/input_mode_manager.js", "js/math.js", - "js/physics_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/assets.js", "js/audio.js", "js/perf_profiler.js", @@ -33,7 +43,6 @@ "js/items.js", "js/item_type_initializers.js", "js/item_lifecycle_support.js", - "js/item_lifecycle_runtime.js", "js/item_update_policy.js", "js/item_dynamic_tool_system.js", "js/item_dynamic_ball_system.js", @@ -63,14 +72,13 @@ "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/tarinai_action_definitions.js", "js/tarinai_needs_items.js", - "js/tarinai_forced_action_planner_system.js", - "js/tarinai_action_planner_system.js", "js/tarinai_need_planner_system.js", "js/tarinai_item_interaction_context.js", "js/tarinai_food_interaction_system.js", @@ -92,6 +100,9 @@ "js/world_view.js", "js/family_graph.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_spatial_budget.js", @@ -107,9 +118,9 @@ "js/simulation_maintenance_system.js", "js/simulation_ambient_system.js", "js/simulation_systems.js", - "js/world_update_phases.js", "js/system_order.js", "js/simulation.js", + "js/colony_situation_system.js", "js/world_update.js", "js/world_tool_actions.js", "js/world_placement_log.js", @@ -127,12 +138,12 @@ "js/save_codec.js", "js/save_storage.js", "js/save_system.js", - "js/patch_helpers.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", diff --git a/assets/sounds/major_damage_01.wav b/assets/sounds/major_damage_01.wav new file mode 100644 index 0000000..8892aab Binary files /dev/null and b/assets/sounds/major_damage_01.wav differ diff --git a/assets/sounds/major_damage_02.wav b/assets/sounds/major_damage_02.wav new file mode 100644 index 0000000..fb88720 Binary files /dev/null and b/assets/sounds/major_damage_02.wav differ diff --git a/assets/sounds/major_damage_03.wav b/assets/sounds/major_damage_03.wav new file mode 100644 index 0000000..8c40633 Binary files /dev/null and b/assets/sounds/major_damage_03.wav differ diff --git a/assets/sounds/major_damage_04.wav b/assets/sounds/major_damage_04.wav new file mode 100644 index 0000000..aa0fb39 Binary files /dev/null and b/assets/sounds/major_damage_04.wav differ diff --git a/assets/sounds/shoot_bolt_action.mp3 b/assets/sounds/shoot_bolt_action.mp3 new file mode 100644 index 0000000..d611e74 Binary files /dev/null and b/assets/sounds/shoot_bolt_action.mp3 differ diff --git a/assets/sounds/shoot_pistol.mp3 b/assets/sounds/shoot_pistol.mp3 new file mode 100644 index 0000000..77db291 Binary files /dev/null and b/assets/sounds/shoot_pistol.mp3 differ diff --git a/assets/sounds/voice_008_temperature_buruburu.wav b/assets/sounds/voice_008_temperature_buruburu.wav new file mode 100644 index 0000000..fbf4666 Binary files /dev/null and b/assets/sounds/voice_008_temperature_buruburu.wav differ diff --git a/assets/sounds/voice_009_temperature_achui.wav b/assets/sounds/voice_009_temperature_achui.wav new file mode 100644 index 0000000..6870f41 Binary files /dev/null and b/assets/sounds/voice_009_temperature_achui.wav differ diff --git a/assets/sprites/tarinai_34_hot_01.webp b/assets/sprites/tarinai_34_hot_01.webp new file mode 100644 index 0000000..2c8eebd Binary files /dev/null and b/assets/sprites/tarinai_34_hot_01.webp differ diff --git a/assets/sprites/tarinai_35_hot_02.webp b/assets/sprites/tarinai_35_hot_02.webp new file mode 100644 index 0000000..68d341f Binary files /dev/null and b/assets/sprites/tarinai_35_hot_02.webp differ diff --git a/assets/sprites/tarinai_36_hot_03.webp b/assets/sprites/tarinai_36_hot_03.webp new file mode 100644 index 0000000..ab1d668 Binary files /dev/null and b/assets/sprites/tarinai_36_hot_03.webp differ diff --git a/assets/sprites/tarinai_37_cold_01.webp b/assets/sprites/tarinai_37_cold_01.webp new file mode 100644 index 0000000..7ce7782 Binary files /dev/null and b/assets/sprites/tarinai_37_cold_01.webp differ diff --git a/assets/sprites/tarinai_38_cold_02.webp b/assets/sprites/tarinai_38_cold_02.webp new file mode 100644 index 0000000..f412a10 Binary files /dev/null and b/assets/sprites/tarinai_38_cold_02.webp differ diff --git a/assets/sprites/tarinai_39_cold_03.webp b/assets/sprites/tarinai_39_cold_03.webp new file mode 100644 index 0000000..b747187 Binary files /dev/null and b/assets/sprites/tarinai_39_cold_03.webp differ diff --git a/assets/ui/favicon-64.png b/assets/ui/favicon-64.png deleted file mode 100644 index 92594d7..0000000 Binary files a/assets/ui/favicon-64.png and /dev/null differ diff --git a/assets/ui/tool_shoot.png b/assets/ui/tool_shoot.png new file mode 100644 index 0000000..50b5f7f Binary files /dev/null and b/assets/ui/tool_shoot.png differ diff --git a/assets/ui/tool_water_bowl.webp b/assets/ui/tool_water_bowl.webp deleted file mode 100644 index 3dc7384..0000000 Binary files a/assets/ui/tool_water_bowl.webp and /dev/null differ diff --git a/css/base.css b/css/base.css index 5a72a27..ed57595 100644 --- a/css/base.css +++ b/css/base.css @@ -74,25 +74,6 @@ button { 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); @@ -137,13 +118,6 @@ h1 { white-space: nowrap; } -.subtitle { - margin: 0; - color: var(--muted); - font-size: clamp(11px, 1.1vw, 13px); - line-height: 1.35; - white-space: nowrap; -} .top-actions { display: flex; @@ -240,9 +214,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; @@ -429,7 +424,6 @@ h1 { z-index: 1; filter: drop-shadow(0 2px 3px rgba(52,40,26,0.12)); } -.tool-sprite-watermark { display: none !important; } .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; } @@ -441,6 +435,7 @@ h1 { .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="shoot"] { --tool-icon: url("../assets/ui/tool_shoot.png"); --tool-icon-zoom: 1.48; } .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"); } @@ -550,13 +545,38 @@ h1 { 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(4, minmax(0, 1fr)); + grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 6px; padding: 8px 4px 9px; margin: -2px 0 2px; diff --git a/css/components.css b/css/components.css index d5fdc33..49fbfb5 100644 --- a/css/components.css +++ b/css/components.css @@ -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: normal; + overflow-wrap: anywhere; + word-break: break-word; } .tool-tip-popover.hidden { display: none; } .selected-actions { @@ -241,12 +246,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 +277,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; } @@ -419,6 +418,7 @@ .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.leaving { animation: logPushOut .18s ease both; } @@ -684,60 +684,6 @@ outline: 2px solid rgba(103,178,44,0.58); outline-offset: 2px; } -.save-hash-profile { - display: grid; - gap: 8px; - padding: 9px 10px; - border: 1px solid rgba(115, 91, 62, 0.18); - border-radius: 12px; - background: rgba(255,255,255,0.56); - color: #4a3929; - font-size: 12px; -} -.save-hash-profile:empty { - display: none; -} -.save-profile-summary { - display: flex; - flex-wrap: wrap; - gap: 6px 10px; -} -.save-profile-summary span { - padding: 3px 7px; - border-radius: 999px; - background: rgba(115, 91, 62, 0.08); -} -.save-profile-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); - gap: 5px; -} -.save-profile-grid > div { - display: grid; - grid-template-columns: 1fr auto auto; - gap: 6px; - align-items: center; - padding: 4px 6px; - border-radius: 8px; - background: rgba(255,255,255,0.54); -} -.save-profile-grid em { - opacity: 0.62; - font-style: normal; -} -.save-profile-items { - display: flex; - flex-wrap: wrap; - gap: 5px 7px; - align-items: center; -} -.save-profile-items span { - padding: 2px 6px; - border-radius: 999px; - background: rgba(111, 153, 102, 0.13); -} - - /* v36: pin only the operation tool category under the quick-scroll tabs. */ .tool-category[data-tool-category="operate"]:not(.collapsed) { position: sticky; @@ -745,8 +691,8 @@ z-index: 11; overflow: hidden; background: linear-gradient(180deg, rgba(238, 247, 255, 0.98), rgba(221, 237, 255, 0.98)); - border-color: rgba(82,128,190,0.36); - box-shadow: 0 8px 18px rgba(58, 82, 112, 0.13), inset 0 1px 0 rgba(255,255,255,0.82); + border: 3px solid rgba(52,112,188,0.72); + box-shadow: 0 9px 22px rgba(58, 82, 112, 0.18), inset 0 0 0 1px rgba(255,255,255,0.86); } .tool-category[data-tool-category="operate"] .tool-category-toggle { background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(231, 243, 255, 0.98)); @@ -756,7 +702,7 @@ background: rgba(238, 247, 255, 0.98); } .tool-category[data-tool-category="operate"] .tool-category-body { - grid-template-columns: repeat(5, minmax(0, 1fr)); + grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 5px; } .tool-category[data-tool-category="operate"] .tool { @@ -785,3 +731,49 @@ @media (max-width: 980px) { .tool-category[data-tool-category="operate"]:not(.collapsed) { top: 58px; } } + +@media (max-width: 980px) { + body.touch-mobile-ui .tool-tip-popover { + max-width: min(280px, calc(100vw - 24px)); + } +} + +/* v39.15.47: collapsed event card must shrink to its header. + layout.css gives .log-card a min-height, which left a large blank panel when folded. */ +.collapsible-card.collapsed { min-height: 0 !important; } +.log-card.collapsed { min-height: 0 !important; height: auto !important; flex: 0 0 auto !important; flex-basis: auto !important; } +.log-card.collapsed .panel-card-body { display: none !important; } + + +.temperature-control { + margin: 10px 0 2px; + padding: 10px 11px 11px; + border: 1px solid rgba(118, 92, 61, 0.16); + border-radius: 14px; + background: rgba(255, 251, 244, 0.56); +} +.temperature-control-head { + display: flex; + align-items: center; + gap: 8px; + font-size: 12px; + color: rgba(68, 55, 41, 0.84); +} +.temperature-control-head strong { + margin-left: auto; + font-size: 15px; +} +.temperature-auto { + display: inline-flex; + align-items: center; + gap: 4px; + margin-left: 4px; + user-select: none; +} +.temperature-slider { + width: 100%; + margin-top: 8px; +} +.temperature-slider:disabled { + opacity: 0.52; +} diff --git a/css/layout.css b/css/layout.css index c7b4fec..ed5e7b7 100644 --- a/css/layout.css +++ b/css/layout.css @@ -6,17 +6,19 @@ .tool[data-tool="delete"] { --tool-icon: url("../assets/ui/tool_delete.webp"); } .tool[data-tool="area_delete"] { --tool-icon: url("../assets/ui/tool_delete.webp"); } .tool[data-tool="new"] { --tool-icon: url("../assets/ui/tool_new.webp"); } +.tool[data-tool="shoot"] { --tool-icon: url("../assets/ui/tool_shoot.png"); --tool-icon-zoom: 1.48; } .tool[data-tool="poke"] { --tool-emoji: "\1F448"; } .tool[data-tool="pinch"] { --tool-emoji: "\1F90F"; } .tool[data-tool="water_hose"] { --tool-emoji: "\1F6BF"; } -.tool[data-tool="copy"] { --tool-emoji: "⧉"; } -.tool[data-tool="area_delete"] { --tool-emoji: "✕"; } -.tool[data-tool="rope"] { --tool-emoji: "╰"; } -.tool[data-tool="rod"] { --tool-emoji: "━"; } -.tool[data-tool="reciprocator"] { --tool-emoji: "⇄"; } -.tool[data-tool="undo"] { --tool-emoji: "↶"; } -.tool[data-tool="redo"] { --tool-emoji: "↷"; } +.tool[data-tool="clear_tool"] { --tool-emoji: "\2715"; } +.tool[data-tool="copy"] { --tool-emoji: "\29c9 "; } +.tool[data-tool="area_delete"] { --tool-emoji: "\d7 "; } +.tool[data-tool="rope"] { --tool-emoji: "\2570 "; } +.tool[data-tool="rod"] { --tool-emoji: "\2501 "; } +.tool[data-tool="reciprocator"] { --tool-emoji: "\21c4 "; } +.tool[data-tool="undo"] { --tool-emoji: "\21b6 "; } +.tool[data-tool="redo"] { --tool-emoji: "\21b7 "; } .tool.tool-emoji-icon { --tool-icon: none; } .tool.tool-emoji-icon::before { content: var(--tool-emoji); @@ -41,10 +43,16 @@ .tool[data-tool="area_delete"] { --tool-icon-zoom: 1.42; } .tool[data-tool="pinch"].tool-emoji-icon::before { font-size: 22px; } .tool[data-tool="area_delete"].tool-emoji-icon::before { - font-size: 30px; + font-size: 31px; + line-height: 1; color: #f3a7aa; text-shadow: 0 1px 0 rgba(255,255,255,0.65), 0 2px 5px rgba(146,54,62,0.16); 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="ball"] { --tool-icon-zoom: 1.36; } @@ -66,8 +74,6 @@ 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: 10px; } .tool-palette { display: flex; flex-direction: column; gap: 8px; } @@ -160,14 +166,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; @@ -315,6 +313,7 @@ meter { .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); } +.log-event { border-left-color: rgba(132, 120, 210, 0.62); } @media (max-width: 980px) { #app { padding: 10px; } @@ -326,21 +325,12 @@ meter { #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; } + .reset-preset-choice-grid { grid-template-columns: repeat(2, minmax(0, 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); -} /* Clearer tool category grouping. */ @@ -355,4 +345,3 @@ meter { @media (min-width: 921px) { .mobile-mode-controls { display: none !important; } } - diff --git a/css/mobile.css b/css/mobile.css index eacaddb..dd123e7 100644 --- a/css/mobile.css +++ b/css/mobile.css @@ -1,40 +1,3 @@ -.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); } } - - /* v15.12.1: top-right sound settings panel */ .sound-menu { position: relative; display: inline-block; } .sound-menu-btn.active { background: linear-gradient(180deg, #f3ffe9, #e7f8db); border-color: rgba(103,178,44,.46); } @@ -59,7 +22,7 @@ .sound-panel input:disabled + span { opacity: .55; } .sound-master { font-weight: 700; } .sound-panel-divider { height: 1px; background: rgba(84,68,48,.12); margin: 2px 0; } -@media (max-width: 760px) { .sound-panel { right: auto; left: 0; } } +@media (max-width: 980px) { .sound-panel { right: auto; left: 0; } } /* Collapsed card sizing is shared by the base panel rules; no event-only shrink override. */ @@ -70,9 +33,6 @@ } /* 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 */ #app { overflow-x: clip; } @@ -164,9 +124,48 @@ } 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 { + display: none; + position: absolute; + right: 10px; + bottom: 58px; + z-index: 24; + gap: 6px; + padding: 6px; + border-radius: 14px; + 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-rotate-controls button { + border: 0; + border-radius: 10px; + padding: 7px 10px; + font-size: 12px; + font-weight: 800; + color: #65533f; + background: rgba(255,255,255,.82); + box-shadow: inset 0 -1px 0 rgba(0,0,0,.05); +} +.mobile-rotate-controls button:active { transform: translateY(1px); } +body.touch-mobile-ui .mobile-rotate-controls:not([hidden]) { display: flex; } +body:not(.touch-mobile-ui) .mobile-rotate-controls { display: none !important; } +@media (max-width: 980px) { + 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-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. */ @@ -174,3 +173,515 @@ body.touch-mobile-ui.mobile-mode-family .lineage-main-card { z-index: 9; } .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); } + + +/* v39.15.35: mobile controls and scroll affordances */ +@media (max-width: 980px) { + body.touch-mobile-ui .mobile-mode-controls:not([hidden]) { + display: flex !important; + } + .panel-quick-tabs { + grid-template-columns: repeat(5, minmax(0, 1fr)); + position: sticky; + top: 0; + } + .panel-quick-tabs button { + padding-left: 3px; + padding-right: 3px; + font-size: 10.5px; + } +} +body.touch-mobile-ui .tool-tip-popover { + max-width: min(520px, calc(100vw - 24px)); +} + +/* v39.15.36: mobile navigation redesign without family input mode */ +@media (max-width: 980px) { + body.touch-mobile-ui .mobile-mode-controls:not([hidden]) { + position: fixed; + left: 50%; + right: auto; + bottom: calc(env(safe-area-inset-bottom, 0px) + 10px); + transform: translateX(-50%); + z-index: 12001; + max-width: calc(100vw - 22px); + } + .panel-quick-tabs { + grid-template-columns: repeat(6, minmax(0, 1fr)); + } + .lineage-main-card { + margin: 10px 0 0; + contain: content; + } + .lineage-main-card .lineage-head { + gap: 8px; + } + .lineage-main-card .lineage-actions { + flex-wrap: wrap; + } + body.touch-mobile-ui .tool-category:not([data-tool-category="operate"]).collapsed .tool-category-body { + display: none; + } + body.touch-mobile-ui .log { + max-height: 240px; + } +} +.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) { + .back-to-game-btn { display: none !important; } +} + +/* v39.15.38: keep desktop layout close to v39.15.36; only hide the PC quick-scroll buttons that duplicate the canvas/tool card. */ +@media (min-width: 981px) { + .panel-quick-tabs { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + .panel-quick-tabs [data-panel-tab="game"] { + display: none !important; + } +} + +/* v39.15.38: mobile navigation/footer room and family-graph reachability. */ +@media (max-width: 980px) { + html, + body { + max-width: 100%; + overflow-x: hidden; + } + #app { + width: 100%; + max-width: 100%; + box-sizing: border-box; + padding-left: max(10px, env(safe-area-inset-left, 0px)); + padding-right: max(10px, env(safe-area-inset-right, 0px)); + padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 92px); + overflow-x: clip; + } + .topbar, + .top-left, + .brand, + .layout, + .stage-wrap, + .panel, + .card, + .lineage-main-card { + min-width: 0; + max-width: 100%; + box-sizing: border-box; + } + .top-actions { + width: 100%; + max-width: 100%; + display: flex; + flex-wrap: nowrap; + justify-content: flex-start; + gap: 5px; + overflow-x: auto; + overflow-y: hidden; + padding: 1px 0 5px; + -webkit-overflow-scrolling: touch; + scrollbar-width: thin; + } + .top-actions > .btn, + .top-actions > .sound-menu, + .top-actions .sound-menu-btn { + flex: 0 0 auto; + white-space: nowrap; + } + .top-actions .btn { + padding: 7px 9px; + font-size: 11px; + } + .room-link { + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + } + body.touch-mobile-ui .mobile-mode-controls:not([hidden]) { + position: absolute; + left: 50%; + right: auto; + bottom: 10px; + transform: translateX(-50%); + z-index: 24; + max-width: calc(100% - 18px); + } + body.touch-mobile-ui .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; + width: auto; + max-width: none; + grid-template-columns: repeat(6, minmax(0, 1fr)); + gap: 4px; + 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); + } + body.touch-mobile-ui .panel-quick-tabs button { + min-height: 34px; + padding: 5px 2px; + font-size: 10px; + } + body.touch-mobile-ui .selected-card, + body.touch-mobile-ui .tool-card, + body.touch-mobile-ui .colony-card, + body.touch-mobile-ui .log-card, + body.touch-mobile-ui .lineage-main-card { + scroll-margin-top: 10px; + scroll-margin-bottom: calc(env(safe-area-inset-bottom, 0px) + 88px); + } + body.touch-mobile-ui .lineage-main-card { + margin-bottom: calc(env(safe-area-inset-bottom, 0px) + 78px); + } + .back-to-game-btn { + bottom: calc(env(safe-area-inset-bottom, 0px) + 62px); + right: max(10px, env(safe-area-inset-right, 0px)); + } +} + + +/* v39.15.47: desktop rollback guard. Keep the normal PC layout; only remove duplicated quick-scroll buttons. */ +@media (min-width: 981px) { + .topbar { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 14px 18px; + } + .top-left { + flex: 1 0 auto; + min-width: max-content; + max-width: 100%; + } + .brand, + .brand h1, + h1 { + flex: 0 0 auto; + min-width: max-content; + max-width: none !important; + overflow: visible !important; + text-overflow: clip !important; + } + .top-actions { + flex: 1 1 560px; + margin-left: auto; + justify-content: flex-end; + align-items: center; + gap: 10px; + } + .top-actions .btn, + .top-actions .sound-menu-btn, + .room-link { + min-height: 42px; + padding: 10px 16px; + font-size: 13px; + line-height: 1.15; + } + .panel-quick-tabs { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + .panel-quick-tabs [data-panel-tab="game"] { + display: none !important; + } +} + +/* v39.15.47: PC / smartphone UI isolation. All override UI placement is scoped by width. */ +@media (min-width: 981px) { + html, + body { + overflow-x: hidden; + overflow-y: auto; + } + #app { + width: min(1480px, 100vw); + padding: 18px; + overflow: visible; + } + .topbar { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 14px 18px; + } + .top-left { + flex: 1 0 auto; + min-width: max-content; + max-width: 100%; + } + .brand, + .brand h1, + h1 { + flex: 0 0 auto; + min-width: max-content; + max-width: none !important; + overflow: visible !important; + text-overflow: clip !important; + } + .top-actions { + flex: 1 1 560px; + margin-left: auto; + justify-content: flex-end; + align-items: center; + gap: 10px; + } + .top-actions .btn, + .top-actions .sound-menu-btn, + .room-link { + min-height: 42px; + padding: 10px 16px; + font-size: 13px; + line-height: 1.15; + } + .layout { + display: grid; + grid-template-columns: minmax(680px, 1fr) minmax(320px, 390px); + align-items: start; + } + .panel { + position: sticky; + top: 12px; + align-self: start; + max-height: calc(100vh - 24px); + overflow-y: auto; + overflow-x: hidden; + } + .panel-quick-tabs { + position: sticky; + top: 0; + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + .panel-quick-tabs [data-panel-tab="game"] { + display: none !important; + } + .lineage-main-card { + display: block; + visibility: visible; + position: relative; + z-index: 1; + width: auto; + margin: 16px 0 0; + overflow: hidden; + contain: layout paint; + } + .lineage-main-content { + height: min(78vh, 980px); + min-height: 520px; + max-height: calc(100vh - 120px); + overflow: auto; + overscroll-behavior: auto; + } +} + +@media (max-width: 980px) { + html, + body { + width: 100%; + max-width: 100%; + min-height: 100%; + overflow-x: hidden; + overflow-y: auto; + overscroll-behavior-y: auto; + } + #app { + width: 100%; + max-width: 100%; + padding: 10px max(10px, env(safe-area-inset-right, 0px)) calc(env(safe-area-inset-bottom, 0px) + 104px) max(10px, env(safe-area-inset-left, 0px)); + overflow-x: clip; + overflow-y: visible; + } + .topbar, + .top-left, + .brand, + .layout, + .stage-wrap, + .panel, + .card, + .lineage-main-card { + min-width: 0; + max-width: 100%; + box-sizing: border-box; + } + .topbar { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 8px; + } + .top-left { + display: flex; + align-items: center; + gap: 8px; + } + .brand h1, + h1 { + max-width: 100%; + overflow: visible; + text-overflow: clip; + white-space: nowrap; + font-size: clamp(20px, 7vw, 30px); + } + .top-actions { + display: flex; + flex-wrap: nowrap; + justify-content: flex-start; + gap: 5px; + width: 100%; + overflow-x: auto; + overflow-y: hidden; + padding: 1px 0 5px; + -webkit-overflow-scrolling: touch; + } + .top-actions > .btn, + .top-actions > .sound-menu, + .top-actions .sound-menu-btn { + flex: 0 0 auto; + white-space: nowrap; + } + .top-actions .btn, + .top-actions .sound-menu-btn { + min-height: 34px; + padding: 7px 9px; + font-size: 11px; + } + .layout { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 10px; + position: relative; + z-index: 2; + overflow: visible; + } + .stage-wrap { + position: relative; + width: 100%; + overflow: hidden; + } + .panel { + position: relative !important; + top: auto !important; + display: flex; + flex-direction: column; + gap: 10px; + width: 100%; + max-height: none !important; + height: auto !important; + overflow: visible !important; + isolation: auto; + } + body.touch-mobile-ui .panel-quick-tabs, + .panel-quick-tabs { + position: fixed; + left: max(8px, env(safe-area-inset-left, 0px)); + right: max(8px, env(safe-area-inset-right, 0px)); + bottom: calc(env(safe-area-inset-bottom, 0px) + 8px); + top: auto; + z-index: 12002; + display: grid; + grid-template-columns: repeat(6, minmax(0, 1fr)); + gap: 4px; + width: auto; + max-width: none; + margin: 0; + padding: 6px; + border: 1px solid rgba(102, 169, 215, 0.34); + border-radius: 16px; + background: rgba(244, 250, 247, 0.95); + box-shadow: 0 12px 28px rgba(42, 54, 64, 0.18); + } + .panel-quick-tabs button { + min-height: 34px; + padding: 5px 2px; + font-size: 10px; + } + body.touch-mobile-ui .mobile-mode-controls:not([hidden]) { + display: flex !important; + position: absolute; + left: 50%; + right: auto; + bottom: 10px; + transform: translateX(-50%); + z-index: 24; + max-width: calc(100% - 18px); + } + body.touch-mobile-ui.mobile-mode-auto #gameCanvas { + touch-action: pan-y pinch-zoom !important; + } + body.touch-mobile-ui.mobile-mode-camera #gameCanvas, + body.touch-mobile-ui.mobile-mode-tool #gameCanvas { + touch-action: none !important; + } + .log { + max-height: none !important; + height: auto !important; + overflow: visible !important; + overscroll-behavior-y: auto !important; + } + .lineage-main-card { + display: block !important; + visibility: visible !important; + position: relative; + z-index: 1; + width: 100%; + margin: 10px 0 calc(env(safe-area-inset-bottom, 0px) + 132px); + overflow: visible !important; + contain: none !important; + clear: both; + } + .lineage-main-content { + display: block !important; + height: auto !important; + min-height: 360px; + max-height: none !important; + overflow-x: hidden !important; + overflow-y: visible !important; + overscroll-behavior-y: auto !important; + -webkit-overflow-scrolling: auto; + touch-action: pan-y pinch-zoom; + } + .lineage-family-tree { + max-width: 100%; + overflow-x: auto !important; + overflow-y: visible !important; + overscroll-behavior-x: contain; + overscroll-behavior-y: auto; + -webkit-overflow-scrolling: touch; + touch-action: pan-x pan-y pinch-zoom; + } + .lineage-family-tree .lineage-tree-panel { + max-width: none; + } + .selected-card, + .tool-card, + .colony-card, + .log-card, + .lineage-main-card, + .stage-wrap { + scroll-margin-top: 0 !important; + scroll-margin-bottom: calc(env(safe-area-inset-bottom, 0px) + 104px); + } +} diff --git a/css/panel.css b/css/panel.css index 98037d2..80cfa3e 100644 --- a/css/panel.css +++ b/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 { @@ -119,6 +117,56 @@ body::before { z-index: 0; pointer-events: none; } + +.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, +.family-fight-stat b { + font-weight: 800; + color: rgba(120, 86, 30, 0.92); +} +.family-fight-stat { + display: block; + margin-top: 1px; + max-width: 82px; + font-size: 9.7px; + line-height: 1.13; + color: rgba(88, 70, 48, 0.82); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.lineage-family-tree .tree-node.champion { + border-color: rgba(202, 146, 33, 0.58); + box-shadow: inset 0 0 0 1px rgba(247, 200, 64, 0.30), 0 3px 8px rgba(120, 78, 12, 0.09); +} +.lineage-family-tree .tree-node.champion::before { + content: "♛"; + 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 +182,44 @@ 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; + display: grid !important; + grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-auto-rows: minmax(13px, auto); + align-items: stretch; + gap: 2px 4px; margin-top: 2px; + max-width: 84px; + width: 84px; + max-height: 34px; + overflow: hidden !important; + text-overflow: clip !important; white-space: normal !important; - overflow: visible; - max-height: none; } .family-personality-tags em { - display: inline-block; - max-width: none; - padding: 1px 4px; + display: flex; + align-items: center; + justify-content: center; + min-width: 0; + max-width: 100%; + padding: 1px 3px; 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-size: 8.2px; 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; } - - -.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)); + line-height: 1.18; 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; + text-overflow: ellipsis; 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); -} +.tool[data-tip] { position: relative; } + + + .lineage-family-tree .lineage-tree-panel { position: relative; overflow: visible; @@ -292,7 +243,6 @@ body::before { z-index: 1; isolation: isolate; } - .lineage-actions { display: flex; align-items: center; @@ -305,7 +255,6 @@ body::before { border-color: rgba(103,178,44,0.46); font-weight: 700; } - .lineage-render-status { position: sticky; top: 0; @@ -355,9 +304,9 @@ body::before { } .lineage-family-tree .tree-node { position: absolute; - width: 132px; - height: 84px; - min-height: 84px; + width: 150px; + height: 104px; + min-height: 104px; box-sizing: border-box; align-items: flex-start; background: linear-gradient(180deg, #fffdf8, #f8f1e5); @@ -377,10 +326,11 @@ body::before { transform: none; } .lineage-family-tree .tree-node .family-portrait { - right: 0; - bottom: -6px; - width: 58px; - height: 58px; + right: 4px; + top: 4px; + bottom: auto; + width: 48px; + height: 48px; } .lineage-family-tree .tree-generation-label { position: absolute; @@ -388,7 +338,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 +353,23 @@ 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; } +.lineage-family-tree .tree-node .family-copy { + max-width: calc(100% - 46px); + width: auto; +} +.lineage-family-tree .tree-node .family-copy strong, +.lineage-family-tree .tree-node .family-copy > span:not(.family-genetic-stats):not(.family-fight-stat):not(.family-personality-tags) { + max-width: 100%; +} +.lineage-family-tree .tree-node .family-personality-tags { + max-width: 84px; + width: 84px; +} +.lineage-family-tree .tree-node .family-personality-tags em { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } - @media (max-width: 720px) { .lineage-main-content { height: 70vh; @@ -418,5 +378,113 @@ 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: 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); +} -.tool[data-tip] { position: relative; } + +/* v9: keep 2x2 personality tags inside fixed family-tree nodes. */ +.lineage-family-tree .tree-node .family-personality-tags, +.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: 86px !important; + max-width: 86px !important; + height: 28px !important; + max-height: 28px !important; + overflow: hidden !important; +} +.lineage-family-tree .tree-node .family-personality-tags em, +.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; + letter-spacing: -0.08em !important; + text-align: center !important; +} + +/* v39.15.85: push family-tree personality tags to the node's right edge. */ +.lineage-family-tree .tree-node .family-personality-tags, +.family-personality-tags { + margin-left: auto !important; + margin-right: 0 !important; + justify-self: end !important; +} +.lineage-family-tree .tree-node .family-copy { + width: 100% !important; + max-width: 100% !important; +} +.lineage-family-tree .tree-node .family-copy strong, +.lineage-family-tree .tree-node .family-copy > span:not(.family-personality-tags) { + max-width: calc(100% - 46px) !important; +} +.lineage-family-tree .tree-node .family-copy > .family-personality-tags { + max-width: 86px !important; + width: 86px !important; +} + +/* v39.15.87: let family personality tags use the full node width. */ +.lineage-family-tree .tree-node .family-copy > .family-personality-tags, +.lineage-family-tree .tree-node .family-personality-tags, +.family-personality-tags { + width: 100% !important; + max-width: 100% !important; + margin-left: 0 !important; + margin-right: 0 !important; + justify-self: stretch !important; + grid-template-columns: repeat(2, minmax(0, 1fr)) !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; +} diff --git a/index.html b/index.html index 11c5b1f..30ac200 100644 --- a/index.html +++ b/index.html @@ -3,20 +3,20 @@
-