sprint A cleanup: accessibility, signals, race, debris

G: large_text scales global theme font (14→20 at 1.4×) via new
GameState.get_font_scale + EventBus.settings_changed. reduce_motion
gates ResumeToast fade (HintOverlay already gated).

I: InspectTooltip long-press wired (500ms hold, 12px drift cancel,
tap-to-clear pin). Stale Phase 19 TODO replaced with accurate doc.

H: Pawn.arrived_at_destination now also emitted on
EventBus.pawn_arrived_at_destination; DirtinessSystem subscribes and
bumps indoor traffic dirt (BUMP_INDOOR_TRAFFIC = 0.2). Outdoor-tracked
bump needs Pawn.prev_tile — flagged for Phase 20.

P: CraftingProvider caches ingredient item ref on Job.ingredient_item;
JobRunner._tick_pickup validates is_instance_valid + not being_carried
before the tile scan, cancels cleanly if another pawn grabbed it.

J: rest_provider.gd deleted. Removed @onready + register call from
world.gd, ext_resource + node from world.tscn. Provider count comment
updated to 9.

M: DIRTY_THRESHOLD extracted — cleaning_provider and job_runner now
reference DirtinessSystem.DIRT_DIRTY_THRESHOLD.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
megaproxy 2026-05-16 18:38:14 +01:00
parent 2f76ae1639
commit fd6f958344
15 changed files with 157 additions and 62 deletions

View file

@ -1,24 +1,34 @@
extends CanvasLayer
## InspectTooltip — hover (or long-press) on a world tile to see what's there.
## InspectTooltip — hover (mouse) or long-press (touch) on a world tile to see
## what's there.
##
## Samples the mouse position every frame, maps to a tile, looks for an entity
## or zone at that tile, builds a short description, and renders a small panel
## offset from the cursor. Empty tile or off-map → tooltip hides.
## Mouse path: samples position every frame, maps to a tile, builds a short
## description, renders a small panel offset from the cursor.
## Empty tile or off-map → tooltip hides.
##
## Touch fallback: long-press on a tile triggers the same lookup and pins the
## tooltip until the next click anywhere (Phase 17 long-press inspect from the
## design doc). Desktop hovering is the primary path on PC; the long-press
## path is wired via Selection's long-press detector (TODO Phase 19).
## Touch path: finger held for LONG_PRESS_MS without drifting more than
## LONG_PRESS_DRIFT_PX pins the tooltip at the held tile until the next tap
## anywhere. Touch-down cancels a previous pin. This self-contained path does
## not depend on Selection; it reads the same tile + description helpers.
const TILE_SIZE_PX: int = 16
const CURSOR_OFFSET: Vector2 = Vector2(14, 14)
const EDGE_MARGIN_PX: int = 8
const LONG_PRESS_MS: int = 500 # ms finger must be held to trigger inspect
const LONG_PRESS_DRIFT_PX: float = 12.0 # cancel long-press if finger moves more than this
var _panel: PanelContainer = null
var _label: RichTextLabel = null
var _last_tile: Vector2i = Vector2i(-9999, -9999)
var _last_text: String = ""
# Touch long-press state.
var _touch_pressing: bool = false
var _touch_screen_pos: Vector2 = Vector2.ZERO
var _touch_start_ms: int = 0
var _touch_pinned: bool = false # true while a long-press pin is held open
var _touch_pin_tile: Vector2i = Vector2i(-9999, -9999)
func _ready() -> void:
layer = 50 # below modals (which are 100+), above world
@ -58,10 +68,65 @@ func _ready() -> void:
_panel.add_child(_label)
## Touch-input handler for long-press inspect.
## InputEventScreenTouch press starts the long-press clock; release or excess
## drift cancels it. A subsequent tap anywhere while pinned dismisses the panel.
func _input(event: InputEvent) -> void:
if event is InputEventScreenTouch:
if event.pressed:
if _touch_pinned:
# A new tap clears the pin.
_touch_pinned = false
_panel.visible = false
_last_text = ""
get_viewport().set_input_as_handled()
return
_touch_pressing = true
_touch_screen_pos = event.position
_touch_start_ms = Time.get_ticks_msec()
else:
# Finger lifted — cancel the pending long-press (pin only fires from _process).
_touch_pressing = false
elif event is InputEventScreenDrag and _touch_pressing:
var drift: float = event.position.distance_to(_touch_screen_pos)
if drift > LONG_PRESS_DRIFT_PX:
_touch_pressing = false
func _process(_dt: float) -> void:
var vp := get_viewport()
if vp == null:
return
# ── Touch long-press poll ─────────────────────────────────────────────────
if _touch_pressing and not _touch_pinned:
var held_ms: int = Time.get_ticks_msec() - _touch_start_ms
if held_ms >= LONG_PRESS_MS:
_touch_pressing = false
_touch_pinned = true
# Describe tile under held finger.
var world_touch: Vector2 = vp.get_canvas_transform().affine_inverse() * _touch_screen_pos
_touch_pin_tile = Vector2i(
floori(world_touch.x / float(TILE_SIZE_PX)),
floori(world_touch.y / float(TILE_SIZE_PX)),
)
if _tile_in_map(_touch_pin_tile):
var pin_text: String = _describe_tile(_touch_pin_tile)
if pin_text != "":
_label.text = pin_text
_last_text = pin_text
_panel.reset_size()
_panel.visible = true
_position_near(_touch_screen_pos)
return
# Nothing to show — cancel pin.
_touch_pinned = false
# Pinned: keep the panel up; skip the mouse-hover update.
if _touch_pinned:
return
# ── Mouse hover path ──────────────────────────────────────────────────────
var mouse_screen: Vector2 = vp.get_mouse_position()
# Convert screen → world via canvas transform (selection.gd does the same).
var world_pos: Vector2 = vp.get_canvas_transform().affine_inverse() * mouse_screen

View file

@ -97,6 +97,11 @@ func _on_load_finished(_slot: StringName, ok: bool, real_seconds_away: int) -> v
func _start_fade() -> void:
if bool(GameState.settings.get("accessibility_reduce_motion", false)):
# Snap to hidden immediately — no fade animation.
_root.visible = false
_root.modulate = Color.WHITE
return
_fading = true
_fade_time = FADE_DURATION_SEC

View file

@ -302,6 +302,7 @@ func _unhandled_input(event: InputEvent) -> void:
func _on_save_pressed() -> void:
GameState.apply_settings(_collect_to_dict())
EventBus.settings_changed.emit()
Audit.log("settings_menu", "settings saved")
_set_visible(false)