Add PLAN verbs and fix dead incident recording
Full build / Import + headless tests (push) Successful in 26s
Full build / Full desktop build + optional release (push) Failing after 3m31s

Verbs (PLAN levels 1-3)
- Eye contact: own Level 1 radial slice, logged as "eye contact".
- Recognition is a player decision: "Recognize" sets NPCController.player_recognized,
  writes access evidence, and GameManager only then appends a RECOGNITION artifact.
  It is no longer implied by NPC type.
- Body camera: real flag (off by default) driving the REC overlay; B toggles it and
  a formal warning arms it automatically, writing a body_camera incident event.
- Blocking stations the guard 0.9 m from the person toward the entrance and turns
  to face them, instead of only emitting the escalation level.
- Request reason: Level 3 ask answered from the NPC's seeded dialogue_lines.
- Radial grows to 12 slices (174 px outer radius, centered labels).

Incident recording fix
- IncidentRecorder.start_incident() was never called, so add_event /
  add_access_evidence / add_escalation silently no-opped and the shift review
  always reported zero incidents. Incidents now open on focus (guarded by
  has_open_incident), verbs call _ensure_incident, and ShiftCoordinator closes
  leftovers as "unresolved" before building the review.

Copy and docs
- 9 new string keys (eye contact, body camera, recognition, reason, station).
- README controls, 26-step smoke checklist, test count, status lists.
- TODO section moved to DONE; MEMORY records the verb design and the recorder bug.

Verification: headless 98/98, smoke_plan_verbs.gd + smoke_session_shell.gd +
windowed smoke_focus_radial.gd all pass. Version bumped to 0.2.3.
This commit is contained in:
zwitschi committed 2026-10-31 20:29:50 +01:00
1 parent 028f608298
commit f5ad1642b5
16 files changed
+500 -37

No files matched your search

+9 -3
View File
@@ -10,6 +10,7 @@ const Strings := preload("res://src/core/strings.gd")
const SLICE_DEFS := [
{"id": "raise_hand", "label_key": "radial.raise_hand", "level": Rules.Level.SIMPLE_DETERRENCE, "action": "raise hand"},
{"id": "eye_contact", "label_key": "radial.eye_contact", "level": Rules.Level.SIMPLE_DETERRENCE, "action": "eye contact"},
{"id": "point", "label_key": "radial.point", "level": Rules.Level.SIMPLE_DETERRENCE, "action": "point"},
{"id": "head_shake", "label_key": "radial.head_shake", "level": Rules.Level.SIMPLE_DETERRENCE, "action": "head shake"},
{"id": "polite", "label_key": "radial.polite", "level": Rules.Level.SIMPLE_DETERRENCE, "action": "polite direction"},
@@ -17,6 +18,8 @@ const SLICE_DEFS := [
{"id": "wand", "label_key": "radial.wand", "level": -1, "action": "wand"},
{"id": "warning", "label_key": "radial.warning", "level": Rules.Level.FORMAL_WARNING, "action": "formal warning"},
{"id": "block", "label_key": "radial.block", "level": Rules.Level.BLOCKING, "action": "blocking"},
{"id": "reason", "label_key": "radial.reason", "level": Rules.Level.BLOCKING, "action": "request reason"},
{"id": "recognize", "label_key": "radial.recognize", "level": -1, "action": "recognize"},
{"id": "radio", "label_key": "radial.radio", "level": -1, "action": "radio assist"},
]
@@ -146,8 +149,8 @@ func _on_wheel_draw() -> void:
return
var center := _wheel.size * 0.5
var n := SLICE_DEFS.size()
var outer := 150.0
var inner := 58.0
var outer := 174.0
var inner := 64.0
for i in n:
var a0 := -PI / 2.0 + TAU * float(i) / float(n)
var a1 := -PI / 2.0 + TAU * float(i + 1) / float(n)
@@ -160,7 +163,10 @@ func _on_wheel_draw() -> void:
var mid := (a0 + a1) * 0.5
var label_pos := center + Vector2(cos(mid), sin(mid)) * ((inner + outer) * 0.5)
var text := Strings.lookup(str(SLICE_DEFS[i]["label_key"]))
_wheel.draw_string(ThemeDB.fallback_font, label_pos + Vector2(-28, 4), text, HORIZONTAL_ALIGNMENT_LEFT, -1, 13, Color.WHITE)
# With 12 slices the label box is tight: center it on the slice midpoint.
var font := ThemeDB.fallback_font
var text_size := font.get_string_size(text, HORIZONTAL_ALIGNMENT_LEFT, -1, 12)
_wheel.draw_string(font, label_pos - Vector2(text_size.x * 0.5, -4), text, HORIZONTAL_ALIGNMENT_LEFT, -1, 12, Color.WHITE)
func _draw_slice(center: Vector2, inner: float, outer: float, a0: float, a1: float, color: Color) -> void:
var pts := PackedVector2Array()