Commit Graph
28 Commits
Author SHA1 Message Date
jpmschweitzerandClaude Opus 5 e5224b1a44 chore(meta): 0.4.8 — the T-1242 gate's first false positive, paid not dodged
A test-only edit to composition.rs tripped the canvas-generation gate, which is path-based and cannot tell an assertion fix from a generator change. Bumped rather than excepted: the ruling is that a false positive costs one round of cache misses and a false negative costs a week. Second no-op bump in two days, noted in project.yaml so the rate is visible if it becomes noise.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 13:03:09 +02:00
jpmschweitzerandClaude Opus 5 48fee8a0b6 feat(config): make the canvas-generation/version pairing a gate, not a habit (T-1242)
project.yaml's version is the Atlas disk cache's only invalidation signal, and
nothing enforced that changing canvas GENERATION also moved it. It broke five
times -- 0.4.2 lake_margin_q, 0.4.3 coast_warp_px, 0.4.4 the extent inversion,
0.4.5 the Global sentinel, 0.4.6 one-course-per-river -- each bumped only after
someone noticed a wrong map. The failure is invisible to its author: it needs a
warm cache to reproduce, so a cold checkout looks fine. T-1239 is the last one,
and it took eight days.

tooling/canvas_sources.py is the path registry; tooling/check-canvas-version
rejects a push that touches those paths without moving project.yaml's version
line. Wired into the pre-push hook, `make check-canvas-version`, and, for the
parsing units, `make test-tooling`.

Verified against real history rather than a synthetic branch: run over
4e503c356 -- the commit that actually caused T-1239 -- the gate rejects and names
the three files. Run over the commits that DID bump (bdea71953, 39f0fd8c5, and
T-1239's own fix), it passes.

The registry is globbed, not hand-listed. step_canvas.rs imports ten sibling
modules and those import more, so a traced closure would be stale within a month,
and stale here is silent. It over-includes on purpose: a false positive costs one
bump and one round of cache misses, a false negative costs another week of a
wrong map -- the ticket's own ruling.

Two deliberate calls worth naming. The registry includes ITSELF, which closes the
narrowing hole: remove a path and change that same path in one push, and the gate
still fires because the registry file is in the set. And there is no override
flag -- it would be reached for exactly when someone is certain their change is
harmless, which is the reasoning behind all five regressions.

Version bumped 0.4.6 -> 0.4.7 with NO canvas-generation change: self-inclusion
means adding the registry trips its own rule. Spent rather than special-cased,
because the first exception is how a rule like this dies.

The units cover the property no branch run can show -- that editing project.yaml's
comment block, which quotes old version NUMBERS directly above the field, is not
a bump -- plus a registry-coverage test naming the files each of the five known
regressions touched, so a future narrowing past them fails loudly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 00:06:32 +02:00
jpmschweitzerandClaude Opus 5 086d9ed56e fix(client): bake the version into the build, so an export can invalidate its cache (T-1241)
current_schema_version() line-scanned res://../project.yaml at runtime. That
resolves to the repo root in a dev run and to nothing in an exported build, so a
shipped game got the "?.?.?" fallback every time. Since that tag is the Atlas
disk cache's ONLY invalidation signal, every exported build stamped and compared
the same sentinel: a canvas cached by one build would be served by every later
build, forever. T-1239 is what that failure looks like once it happens.

loading_screen.gd carried a byte-for-byte copy of the same function, so the
version shown to the player was "?.?.?" in exactly the builds where a version
string is worth showing. Both call sites now share client/scripts/build_version.gd,
which reads application/config/version out of ProjectSettings — a value Godot
bakes into the PCK, identical in the editor and in an export by construction
rather than by luck. No file IO, no fallback branch.

project.yaml stays the source of truth (CLAUDE.md); client/project.godot mirrors
it. A mirror nobody checks would be worse than the bug it replaces -- the old
code failed loudly everywhere, a stale mirror fails silently -- so
tooling/check-client-version compares the two and the pre-push hook runs it
unconditionally. Not gated on "were those files in this push": drift persists on
main once introduced, and gating would let an existing drift ride along.

The test this replaces asserted that current_schema_version() did not return its
fallback, and passed -- in the one environment where the code under test worked.
Three tests now pin the property that actually matters: a real version, sourced
from the baked setting, matching project.yaml.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 23:35:33 +02:00
jpmschweitzerandClaude 21e263d0a4 feat(ui): stretch the UI to the window aspect — support ultrawide properly
Jeroen on a 3440x1440 panel: 'it does not fit the viewport', then 'widescreen
users are fully supported so we add stretching ui'.

client/project.godot declares a 1920x1080 base viewport with
stretch/mode=canvas_items, and stretch/aspect was unset — which defaults to
'keep'. On a 21:9 display Godot letterboxes the entire 16:9 UI inside it, so
every screen in the game was pillarboxed, not just the Atlas. Measured: the
window came out 2432x1368, exactly 16:9; with aspect=expand it becomes
3440x1368, the full panel width.

Worth being explicit that the Atlas was innocent here. It was filling its
logical viewport correctly the whole time; that viewport was being boxed
inside the monitor. The three genuine Atlas sizing bugs fixed today (the
zeroed extent, the legend-column mismatch, the integer-ratio floor) were all
real and all separate from this.

expand gives every screen more space on a wide display instead of bars, which
is the right default for a UI-heavy game with a HUD and implant panels. The
tradeoff is that layouts must tolerate a variable aspect ratio rather than
assuming 16:9 — accepted deliberately, since widescreen is a supported target.

Verified: full client suite 1833 / 1807 passed / 0 failed, cold-parse clean,
parse sweep clean across 226 scripts.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-27 21:39:10 +02:00
jpmschweitzerandClaude Fable 5 34dbac9bf6 feat(ui): AtlasAgentInterface — observe/act named-intent control channel (D-226, T-971)
D-226 layer 4, rebuilt against the post-D-255 stepped Atlas after the
Phase-1 reconciliation (the original intent list targeted the retired
continuous-zoom viewer). Eleven intents, each backed by the exact
production handler a click calls — select/open for systems and bodies
(extracted shared by-id tails so click and intent paths are one code
path), scroll_rung, reset_view, back, open/close_atlas, set_overlay —
plus two new first-class capabilities: jump_to_center (the fixed-center
revisit pattern proven by five eyeball drivers, via a new
StepCanvasViewer.jump_to seam that reuses _scroll_rung's exact request
tail — same extent cap, same cache keys) and get_current_canvas_summary
(allocation-light reads off the raw wire dict, courses-by-class,
draw-matched settlement dedup — no PNG decode). Contract shape: dumb
AtlasAgentBridge autoload holding the app handle (untyped per the
parse-order rule), all logic in the static AtlasAgentInterface class.
observe() is side-effect-free: current state + a generic Control-walk
affordance tree. In-process consumers only this ticket (documented);
the committed reference driver (atlas_agent_driver.gd, InputSwallower +
settle-until-ready from the T-1157 inventory) replaces the scratch
eyeball drivers as the sanctioned headless-drive pattern.
select_city/open_regional dropped with recorded rationale (no
settlement hit-test affordance exists post-D-255) — diff on the ticket.
33 new tests across three suites.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 18:51:45 +02:00
jpmschweitzerandClaude Opus 4.6 3f5b4258ba feat(client): sprint 38 — free camera viewer, archetype strip, test fixes
- Add free camera mode (F4 toggle): WASD pan, scroll zoom, decoupled
  from player position (#898)
- Strip archetype-driven code: remove character_archetype, lattice_profile,
  and lattice color palettes from client (#882)
- Fix confrontation_monologue signal not firing in headless test mode (#867)
- Revive fog state behavioral tests: EXP_EXPLORED persistence, grow-only
  bounds, texture-resize copy, BoundaryWall handling (#879)
- Triage pre-existing test failures: fix examine_display dismiss timing,
  fog test position fragility, rendering snapshot assertions,
  time_display format (#871)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-02 10:19:11 +02:00
jpmschweitzerandClaude Opus 4.6 84105916cd feat(ui): introduce MetaScreen pattern foundation (#618, #680)
Establishes the faux-game-menu base pattern for non-diegetic UI,
analogous to ImplantApp but for pre-gameplay and meta-overlay
screens (main menu, character creation, settings, debug console,
bug report, loading screen). Workstream 1 of the MetaScreen
refactor — foundation only, no screen migrations yet.

- client/ui/meta/meta_screen.gd: base class (Control) with
  HIDDEN/OPENING/OPEN/CLOSING phase tracking, three orthogonal
  policy booleans (pauses_sim, closable_by_escape, captures_input),
  open/close lifecycle, on_escape contract, closed + escape_pressed
  signals, subclass hooks (on_open, on_close).
- client/ui/meta/meta_stack.gd: autoload coordinator. Overlay stack
  with push/pop/top/is_active; handle_escape chain; sim-pause
  coordination via SimBridge when pauses_sim=true; meta_active_changed
  signal. All class references kept inside method bodies — no
  top-level class_name refs, matching HudGroups / GameState
  autoload parse-order discipline.
- client/scripts/character_profile.gd: Resource wrapping the
  visual descriptor with bookmark_id and start_location_id.
  Target of the creation_confirmed signal once the character
  creation flow migrates.
- client/project.godot: MetaStack registered as autoload after
  HudGroups, before ImplantRegistry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-19 20:18:29 +02:00
jpmschweitzerandClaude Sonnet 4.6 df29d26d1e feat(ui): introduce ImplantApp pattern — base class, registry, atlas + economics refactor (#844, #824, #836)
Establishes the ImplantApp faux-mobile-OS architecture (D-191):

Foundation:
- ImplantAppManifest (Resource): @export vars for app_path, scene_path, default_key, default_mode, preserves_state
- ImplantNavStack (Node): push/pop/replace/reset_to_default, synchronous screen_changed signal
- ImplantApp (Control base class): absorbs HudGroups boilerplate; on_install/on_open/on_close/on_insert_deactivated lifecycle hooks
- ImplantRegistry (autoload): lazy-scans res://ui/implant/apps/*/app.tres; avoids autoload parse-order trap

Atlas app (replaces atlas_panel + atlas_reach_map + atlas_system_map + atlas_planet_map + root viewer files):
- apps/atlas/app.tres — manifest (implant/map, FULLSCREEN, key=M)
- apps/atlas/atlas_app.gd — coordinator; 4-screen nav via ImplantNavStack
- apps/atlas/screens/{reach,system,planet,regional}_screen.gd — enter/leave interface
- apps/atlas/{atlas_viewer,atlas_marker_overlay,atlas_overlay_bar}.gd — moved from root

Economics app (replaces economics_panel):
- apps/economics/app.tres — manifest (implant/economics, INSERT, key=N)
- apps/economics/economics_app.gd — thin shell delegating to OverviewScreen
- apps/economics/screens/overview_screen.gd — full panel logic, enter/leave interface

Wiring:
- project.godot: add ImplantRegistry autoload after HudGroups
- main.gd: registry-driven key toggle loop; rename atlas_panel→atlas_app, economics_panel→economics_app
- hud.tscn: swap to new scene paths; remove legacy StarMap node
- snapshot_consumers.gd: on_insert_deactivated() uniformly; rename vars
- hud_groups.gd: remove stale starchart compat comment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 13:16:56 +02:00
jpmschweitzerandClaude Opus 4.6 f037bcaa81 feat(ui): HUD visibility groups — gameplay vs implant layers (D-170)
New HudGroups autoload manages show/hide of related HUD elements as
groups. Exclusive groups (gameplay, implant) are mutually exclusive —
opening the star map hides stance indicator, minimap, interaction
prompts, etc. Closing it restores them.

Gameplay nodes registered in main.gd _ready(). Star map registers as
implant group and uses toggle_group() instead of direct visibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:01:28 +02:00
jpmschweitzerandClaude Opus 4.6 ff3a8e95e4 feat(client): port character shaders, skin tones, skeleton, and animations (#702, #706)
Port proven shaders (toon, toon_masked, outline) and skin tone textures
from quaternius-aesthetic spike to production client. Import 65-bone
armature and Universal Animation Libraries (UAL1 + UAL2). Set
gltf/embedded_image_handling=3 in project.godot (critical import setting).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 07:48:37 +01:00
jpmschweitzerandClaude Sonnet 4.6 f51366b3f5 fix(ui): address PR #90 review — 8 items (W1–W4, S5–S6, A3–A4)
W1/A2: add SimBridge.CONNECTED guard before send_input in toggle handler
W2/A1: assert PlatformInfo != null in HardwareDetector._ready();
       add ordering comment in project.godot [autoload] section
W3: replace FileAccess.open() with get_file_as_string() in
    read_benchmark_cache() — auto-closes, no leak
W4/A3: add _extract_bool_setting() helper in game_state.gd that handles
       both {"Bool": true} and plain bool; push_warning on type mismatch
S5: sync _pre_battery_pref from GameState after load_ai_pref() in
    HardwareDetector._ready() — closes race if system starts on battery
S6: separate "red" TPT status into its own arm with STATUS_RED dot and
    label colour — three-colour mapping: green/yellow/red now distinct
A4: replace DebugConsole.PREFS_PATH compile-time dependency in
    _save_ai_pref() with local SETTINGS_CFG_PATH constant

S7 (battery CI guard) already applied by Hoshe in test file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 11:39:47 +01:00
jpmschweitzerandClaude Sonnet 4.6 d7a4e3268a feat(ui): PlatformInfo abstraction + battery detection + fixes (#646 D-138)
Platform abstraction:
- New PlatformInfo autoload (extends Node, registered first in project.godot).
  Owns all OS queries: power state monitoring (30s poll), memory (on demand),
  file path resolution (user_data_dir, config_dir, benchmark_cache_path,
  install_dir, executable_path, cache_dir, model_dir).
- PowerProfile enum (FULL/BATTERY/POWER_SAVER), power_profile_changed signal.
  Uses OS.callv("get_power_info") to defer resolution to runtime — avoids
  compile errors on Godot 4.6 headless builds without power API.

HardwareDetector refactor:
- check_ram() now delegates to PlatformInfo.refresh_memory() + free_memory_mb.
  All direct OS.get_memory_info() calls removed.
- classify_power_state(int) and should_suspend_inference(int) delegate to
  PlatformInfo for single source of truth.
- check_power_state() → {power_state, classification, should_suspend}.
- Battery suspend/resume: on PowerProfile.BATTERY, stores pre-battery pref and
  sends ChangeSettings(false) to server. On FULL restore, sends ChangeSettings
  with saved pref if it was enabled. inference_suspended member tracks state.

Settings dialog:
- is_ai_inference_suspended() / set_ai_inference_suspended(bool) — testable API
  per Hoshe's test contract. Reads HardwareDetector.inference_suspended on open.
- Toggle now disabled when battery-suspended OR hardware fails (not only fail).
- get_ai_dialogue_label_text() wired to UIStrings ("settings.ai_dialogue_toggle")
  instead of hardcoded string.

Cleanup:
- Deleted ai_dialogue_detector.gd and .uid (orphaned duplicate, dead code).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 10:41:07 +01:00
jpmschweitzerandClaude Sonnet 4.6 01b0583265 feat(ui): AI-Enhanced Dialogue toggle + hardware detection (#646, D-138)
Implements the full AI-Enhanced Dialogue feature for Sprint 26:

- HardwareDetector autoload (extends Node): three-layer detection —
  Layer 1 RAM classification (pass/marginal/fail, thresholds 2GB/1.6GB),
  Layer 2 TPT benchmark cache (green/yellow/red, thresholds 6/3 t/s),
  Layer 3 degradation monitoring (>40% drop → yellow). load_ai_pref()
  restores toggle state from user://settings.cfg on startup.

- Settings dialog AI section: toggle, colored status dot, status message
  label, testable API (get_ai_dialogue_label_text, set_ai_dialogue_
  hardware_status, is_ai_dialogue_toggle_enabled). Only RAM "fail" greys
  out toggle — player always overrides yellow/red recommendations (D-138).

- GameState.ai_enhanced_dialogue_enabled (default true, opt-out model).
  GameState.settings_response (v20 one-shot settings dump from server).
  apply_snapshot() hydrates ai_enhanced_dialogue_enabled from full dump.

- Protocol v19→v20, settings_response decoding in decode_snapshot().
  Protocol converted to extends Node autoload (enables test has_method).
  encode_change_settings() helper for test inspection.

- InputMapper: CHANGE_SETTINGS, REQUEST_ALL_SETTINGS, DELETE_SETTING.
  SimBridge: wire mappings for all three. RequestAllSettings queued after
  handshake to hydrate client state from server SQLite on connect.
  settings_response carry-forward in receive_bytes().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 10:34:48 +01:00
jpmschweitzerandClaude Opus 4.6 e1ea07e746 feat(client): save/load client UI — F5/F6 quicksave/quickload (#554)
Wire SaveGame/LoadGame player actions through the full client stack:
protocol v15 decode, InputMapper F5/F6 bindings, SimBridge wire mapping
with one-shot carry-forward, GameState save_result field, and HUD
notification via monologue display. Quit-to-menu triggers quicksave
before scene change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 13:15:05 +01:00
jpmschweitzerandClaude Opus 4.6 ef135830f2 feat(client): game session management (#258, D-085)
Per-game save directories under user://saves/<timestamp>-<seed>/.
SessionManager autoload handles new_game(), resume_game(), quit flow.
Main menu scene with New Game / Continue / Quit buttons. Game-id
passed to server subprocess via --game-id flag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 11:45:46 +01:00
jpmschweitzerandClaude Opus 4.6 86ff83d927 feat(ui): knowledge/journal display panel (#264)
Right-side insert panel toggled with J key. Facts grouped by entity
with confidence, source, and state metadata. Contradicted entries
in amber with strikethrough (THE FRIEND arc surface). Stale entries
dimmed. Mutual exclusion with dialogue box. All labels via UIStrings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 02:30:04 +01:00
jpmschweitzerandClaude Opus 4.6 9b53096b0b feat(ui): F3 debug overlay — real-time game state display (#511)
Toggle with F3. Shows tick, fps, position, facing, stance, zone,
entity/tile counts, interaction/recognition counts, dialogue status,
stationary ticks, insert state, time/tick_rate, mode, gauntlet.
Two-column layout, click-through, hidden by default.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 20:10:30 +01:00
jpmschweitzerandClaude Opus 4.6 0e6cc4eef8 feat(audio): Sprint 13 — AudioManager, zone crossfade, dip profiles, murmur wiring (#528, #529, #530, #533)
AudioManager: volume slider persistence (ConfigFile), settings UI with
5 teal-bordered sliders, default_bus_layout.tres for editor. Zone
crossfade: defensive zone_id read from snapshot tiles, 1.5-2s ambient
tween, auto-activates when server ships OQ-09. Dip profiles: dialogue
dip in show/hide_dialogue, ListeningFocus 30-tick gate via
stationary_ticks in game_state.gd. NPC murmur: client plumbing for
event-driven World SFX playback, no-ops until audio asset arrives.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 18:19:25 +01:00
jpmschweitzerandClaude Opus 4.6 7274151671 feat(client): hub teleport UX — Home key, fade transition (#501)
Home key sends TeleportToHub in Gauntlet mode. Camera snaps to hub
spawn with 0.3s fade-from-black. Clears dialogue/monologue buffers
on teleport. Teleport detection uses distance threshold (>5 tiles)
so future teleport types get the transition for free.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 08:46:02 +01:00
jpmschweitzerandClaude Opus 4.6 80d5a57b5d feat(client): introduce Michroma font and implant UI theme (#517)
Add Michroma-Regular.ttf (OFL license) as the game font. Create
FontVariation with +1px tracking and a global Theme resource with
cyan-white (#E0F7FA) implant text color. Wire theme into project.godot
so all Label/RichTextLabel/Button/LineEdit nodes inherit automatically.
Add IMPLANT_TEXT_COLOR/DIM/PULSE constants to constants.gd.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 12:53:08 +01:00
jpmschweitzerandClaude Opus 4.6 d7755698b2 feat(client): add WRONG button F12 bug report capture (#495)
F12 pauses simulation, shows modal LineEdit prompt, saves three files
to user://bug-reports/gauntlet-t{tick}-{timestamp}/: snapshot.json
(full ObserverSnapshot), render.txt (simplified client-side text
render), description.txt (tester notes + tick/room/stance metadata).
Esc cancels without saving. Double-activation guard prevents stacking.

BUG_REPORT action added to InputMapper with wire guard in SimBridge
(client-only, never sent to server). Dialog on ModalLayer (CL 30).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:53:15 +01:00
jpmschweitzerandClaude Opus 4.6 9e6b859cdf feat(audio): add AudioManager autoload with 5-bus architecture
D-068 bus layout (Music, Ambient, WorldSFX, PlayerActions, UISounds),
directory-scan asset registry, spatial/non-spatial playback, D-069
audio dip profiles (dialogue, confrontation, listening_focus) with
low-pass filter sweep, and D-073 zone crossfade stub. No-op fallback
when audio assets absent. Implements #255.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 16:34:42 +01:00
jpmschweitzerandClaude Opus 4.6 1fcf08d21f feat(client): Sprint 6 Touch — z-layer pipeline, cursor, fog, interactions, inventory, stance, radial
Three-scope z-layer rendering pipeline (D-049): world z:0-900 inside
CanvasGroup, insert overlay CanvasLayer 10, UI CanvasLayer 20, modal
CanvasLayer 30. Y-sort contract enforced (entities z_index=0). Reserved
ranges for VFX, airborne, lower floors documented in constants.gd.

Sprint 6 client tickets:
- #429: Cursor state machine — 4 states, 150ms transitions (D-056)
- #430: Fog shader rebuild — 5-layer fragment shader, animated noise (D-059)
- #432: Entity interaction list — vertical multi-verb, insert-styled (D-057)
- #433: World radial menu — 2 spokes, drag-release + click-click (D-058)
- #438: Inventory UI — 3x3 grid, 40x40px, 1-9 hotkeys (D-065)
- #439: Stance indicator — color-coded HUD, C/X keybinds (D-053)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:06:03 +01:00
jpmschweitzerandClaude Opus 4.6 5a04656781 feat(ui): add YAML-based UI string loading system (#409)
Adds UIStrings autoload that loads display text from a YAML file,
replacing hardcoded strings in HUD and interaction prompt. Copy team
can now author UI microcopy in client/data/ui-strings.yaml without
touching GDScript. Includes 38 strings across 5 categories and 16
gdUnit4 tests for the parser and lookup API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 17:18:01 +01:00
jpmschweitzerandClaude Opus 4.6 dcf887cb49 feat(client): add tile rendering, fog overlay, and camera smoothing
Sprint 2 rendering pipeline: tiles (#129), fog (#131), camera (#116).

- tile_renderer.gd: programmatic TileSet with floor/wall/door/object
  placeholder tiles, renders from snapshot tile data
- fog_renderer.gd: TileMapLayer overlay with three visibility states
  (visible/fog-edge/hidden), computed from visible_positions data
- Camera2D: smoothing enabled (speed 6.0), 2x zoom, locked to player
- game_state.gd: stores visible_tiles and visible_positions from snapshots
- sim_bridge.gd: test data with 8x8 room, corridor, and Manhattan
  distance visibility for development without server
- Scene render order: Tiles -> FogOverlay -> Entities
- Background clear color set to near-black for unexplored areas

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:48:02 +01:00
jpmschweitzerandClaude Opus 4.6 2ecafff2ed feat(client): integrate LocalBridge transport and 8-directional movement
Wire SimBridge to use LocalBridge for TCP transport in non-test mode:
_process() polls for incoming snapshots and flushes outbound inputs.
Add 4 diagonal movement variants (NE, SE, SW, NW) to InputMapper and
wire mapping, ordered clockwise. Register diagonal input actions in
project.godot.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 20:59:40 +01:00
jpmschweitzerandClaude Opus 4.6 3368231396 fix(client): address PR review — input bug, bounds checks, fixture tests
- Fix input_mapper double-check bug (redundant InputEventKey + pressed filter)
- Add bounds checking for position arrays in entity_renderer and game_state
- Make test snapshot deterministic (incrementing counter, not wall clock)
- Fix tween overlap in monologue_display (cancel active tween before new one)
- Extract TILE_SIZE constant from magic number 32
- Add 5 D-030 Layer 1 fixture tests for snapshot parsing (7/7 total passing)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 17:53:24 +01:00
jpmschweitzerandClaude Opus 4.6 603c24e177 feat(client): initialize Godot 4 project boilerplate (epic #277)
Set up the complete Godot 4.6 client foundation as a pure renderer (D-020):
- project.godot with 2D rendering, autoloads, input actions
- Main scene: Game > World (TileMapLayer, Entities, FogOverlay) + Camera2D + UILayer
- Autoloads: SimBridge (connection state machine + test mode), GameState, InputMapper
- Rendering stubs: WorldRenderer, EntityRenderer, FogRenderer
- UI stubs: HUD (health/perception/time), Minimap, MonologueDisplay
- Input mapping: WASD/arrows, E (interact), Tab (perception), Esc (menu), Space (pause)
- gdUnit4 smoke tests: scene loads, autoloads registered (2/2 passing)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 16:52:27 +01:00