fix(client): address PR #86 review — archetype validation, teleport clear, ticker layout

- protocol.gd: replace capitalize() with explicit match for archetype
  string mapping, push_error on unknown input with Detective fallback
- main.gd: clear _known_triangle_ids in _teleport_transition() alongside
  _known_recognition_ids so chime re-fires after room change
- news_ticker.gd: defer get_minimum_size() via call_deferred to run
  after layout pass, fixing first-frame scroll distance
- 3 new tests: unknown archetype fallback, triangle dedup per-id,
  independent triangle ID firing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 16:28:12 +01:00
co-authored by Claude Opus 4.6
parent 349f02fbcb
commit e0eb3cd35e
4 changed files with 69 additions and 3 deletions
+45
View File
@@ -27,6 +27,17 @@ func test_game_state_character_archetype_default_is_detective() -> void:
).is_equal("detective")
func test_protocol_startup_message_unknown_archetype_defaults_to_detective() -> void:
# Unknown archetype strings must not silently pass garbage to the server.
# The match guard falls back to "Detective" and calls push_error.
var bytes: PackedByteArray = Protocol.encode_startup_message(0, "hacker")
var decoded = Messagepack.decode(bytes)
assert_that(decoded.status).is_null()
assert_str(decoded.value["character_archetype"]).override_failure_message(
"Unknown archetype must fall back to 'Detective'"
).is_equal("Detective")
func test_protocol_startup_message_includes_character_archetype() -> void:
# StartupMessage wire payload must carry "character_archetype" key (#588).
var bytes: PackedByteArray = Protocol.encode_startup_message(12345, "detective")
@@ -119,6 +130,40 @@ func test_protocol_decode_triangle_crisis_events_absent_returns_empty() -> void:
assert_int(events.size()).is_equal(0)
func test_triangle_dedup_fires_chime_only_once_per_id() -> void:
# _known_triangle_ids must prevent the same triangle_id from chiming twice.
# We test the dedup dict directly — main.gd cannot be easily instantiated headless.
# The dict is the single source of truth for dedup state.
var seen: Dictionary = {}
var chime_count: int = 0
# Simulate two ticks both containing triangle_id 42.
for _tick in range(2):
var tid: int = 42
if not seen.has(tid):
seen[tid] = true
chime_count += 1
assert_int(chime_count).override_failure_message(
"Chime must fire exactly once per triangle_id across repeated ticks"
).is_equal(1)
func test_triangle_dedup_fires_chime_for_each_unique_id() -> void:
# Two distinct triangle_ids each chime once.
var seen: Dictionary = {}
var chime_count: int = 0
for tid in [42, 99]:
if not seen.has(tid):
seen[tid] = true
chime_count += 1
assert_int(chime_count).override_failure_message(
"Each unique triangle_id must chime independently"
).is_equal(2)
# -- #592: current_ticker decode ----------------------------------------------
func test_protocol_decode_includes_current_ticker_field() -> void: