Removes the version-mismatch guard from Protocol.decode_snapshot() and the PROTOCOL_VERSION constant from the client (server side done in #874). Core changes: - protocol.gd: remove const PROTOCOL_VERSION, remove version mismatch guard, remove "version" from return dict, add gauntlet_mode/room_id decode - sim_bridge.gd: remove handshake version check; relax handshake guard to require only a valid Dictionary (server no longer sends protocol_version); emit handshake_complete(0) for API compat - loading_screen.gd: drop "· protocol N" suffix from version label - test_harness.gd: replace Protocol.PROTOCOL_VERSION with literal 23 Test updates (21 files): replace "version": Protocol.PROTOCOL_VERSION with "version": 23 in all snapshot bytes dicts; remove snapshot.version == N assertions; remove version-rejection tests (test_rejects_version_6, test_decode_snapshot_rejects_missing_version, test_decode_snapshot_rejects_old_version, test_protocol_rejects_version_mismatch, test_sim_bridge_test_snapshot_uses_current_protocol_version). Also includes: #872 bookmark_catalog carry-forward regression test, and #873 merge-path flow tests (test_merge_path_flows_sprint37.gd). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
214 lines
7.6 KiB
GDScript
214 lines
7.6 KiB
GDScript
class_name TestInteractionPrompt
|
|
extends GdUnitTestSuite
|
|
|
|
# Tests for ticket #405: Interaction prompt system.
|
|
# Covers protocol v4 decode, GameState storage, SimBridge test mode,
|
|
# InteractionPrompt UI, and input encoding.
|
|
|
|
|
|
# -- Protocol: v4 nearby_interactions decoding --
|
|
|
|
func test_protocol_decode_v4_with_nearby_interactions() -> void:
|
|
var raw := {
|
|
"tick": 10,
|
|
"version": 23,
|
|
"entities": [
|
|
{"entity_id": 1, "x": 5.0, "y": 5.0, "z": 0, "kind": "Player",
|
|
"visibility": "Forward", "relationship": "Unknown", "observation": "Visible"},
|
|
],
|
|
"nearby_interactions": [{
|
|
"entity_id": 2,
|
|
"entity_type": "Npc",
|
|
"distance": 1,
|
|
"verbs": [
|
|
{"kind": "Talk", "label": "Talk", "priority": 1, "available": true},
|
|
{"kind": "ExamineNpc", "label": "Observe", "priority": 2, "available": true},
|
|
],
|
|
}],
|
|
}
|
|
var encoded = Messagepack.encode(raw)
|
|
assert_that(encoded.status == null).is_true()
|
|
var snapshot = Protocol.decode_snapshot(encoded.value)
|
|
|
|
assert_that(snapshot).is_not_null()
|
|
assert_that(snapshot.nearby_interactions.size()).is_equal(1)
|
|
var ni = snapshot.nearby_interactions[0]
|
|
assert_that(ni.entity_id).is_equal(2)
|
|
assert_that(ni.entity_type).is_equal("Npc")
|
|
assert_that(ni.distance).is_equal(1)
|
|
assert_that(ni.verbs.size()).is_equal(2)
|
|
assert_that(ni.verbs[0].kind).is_equal("Talk")
|
|
assert_that(ni.verbs[0].label).is_equal("Talk")
|
|
assert_that(ni.verbs[0].priority).is_equal(1)
|
|
assert_that(ni.verbs[0].available).is_true()
|
|
assert_that(ni.verbs[1].kind).is_equal("ExamineNpc")
|
|
|
|
func test_protocol_decode_v4_no_nearby_interactions() -> void:
|
|
var raw := {
|
|
"tick": 5,
|
|
"version": 23,
|
|
"entities": [],
|
|
}
|
|
var encoded = Messagepack.encode(raw)
|
|
var snapshot = Protocol.decode_snapshot(encoded.value)
|
|
assert_that(snapshot).is_not_null()
|
|
assert_that(snapshot.nearby_interactions.size()).is_equal(0)
|
|
|
|
func test_protocol_decode_empty_nearby_interactions() -> void:
|
|
var raw := {
|
|
"tick": 1,
|
|
"version": 23,
|
|
"entities": [],
|
|
"nearby_interactions": [],
|
|
}
|
|
var encoded = Messagepack.encode(raw)
|
|
var snapshot = Protocol.decode_snapshot(encoded.value)
|
|
assert_that(snapshot.nearby_interactions.size()).is_equal(0)
|
|
|
|
func test_protocol_decode_interaction_missing_verbs() -> void:
|
|
var raw := {
|
|
"tick": 1,
|
|
"version": 23,
|
|
"entities": [],
|
|
"nearby_interactions": [{"entity_id": 2}],
|
|
}
|
|
var encoded = Messagepack.encode(raw)
|
|
var snapshot = Protocol.decode_snapshot(encoded.value)
|
|
assert_that(snapshot.nearby_interactions.size()).is_equal(0)
|
|
|
|
func test_protocol_decode_interaction_empty_verbs() -> void:
|
|
var raw := {
|
|
"tick": 1,
|
|
"version": 23,
|
|
"entities": [],
|
|
"nearby_interactions": [{"entity_id": 2, "entity_type": "Npc", "distance": 1, "verbs": []}],
|
|
}
|
|
var encoded = Messagepack.encode(raw)
|
|
var snapshot = Protocol.decode_snapshot(encoded.value)
|
|
assert_that(snapshot.nearby_interactions.size()).is_equal(0)
|
|
|
|
func test_protocol_decode_v4_entity_relationship() -> void:
|
|
var raw := {
|
|
"tick": 1,
|
|
"version": 23,
|
|
"entities": [
|
|
{"entity_id": 1, "x": 5.0, "y": 5.0, "z": 0, "kind": "Npc",
|
|
"visibility": "Forward", "relationship": "Friendly", "observation": "Visible"},
|
|
],
|
|
}
|
|
var encoded = Messagepack.encode(raw)
|
|
var snapshot = Protocol.decode_snapshot(encoded.value)
|
|
assert_that(snapshot.entities[0].relationship).is_equal("Friendly")
|
|
|
|
# -- GameState: nearby_interactions storage --
|
|
|
|
func test_game_state_stores_nearby_interactions() -> void:
|
|
var ni := [{"entity_id": 2, "entity_type": "Npc", "distance": 1,
|
|
"verbs": [{"kind": "Talk", "label": "Talk", "priority": 1, "available": true}]}]
|
|
GameState.apply_snapshot({"tick": 1, "entities": [], "nearby_interactions": ni})
|
|
assert_that(GameState.nearby_interactions.size()).is_equal(1)
|
|
assert_that(GameState.nearby_interactions[0].entity_id).is_equal(2)
|
|
GameState.nearby_interactions = []
|
|
|
|
func test_game_state_clears_nearby_interactions_when_absent() -> void:
|
|
var ni := [{"entity_id": 2, "entity_type": "Npc", "distance": 1,
|
|
"verbs": [{"kind": "Talk", "label": "Talk", "priority": 1, "available": true}]}]
|
|
GameState.apply_snapshot({"tick": 1, "entities": [], "nearby_interactions": ni})
|
|
assert_that(GameState.nearby_interactions.size()).is_equal(1)
|
|
GameState.apply_snapshot({"tick": 2, "entities": []})
|
|
assert_that(GameState.nearby_interactions.size()).is_equal(0)
|
|
|
|
|
|
# -- SimBridge test mode: nearby_interactions generation --
|
|
|
|
func test_sim_bridge_test_snapshot_interaction_when_near_npc() -> void:
|
|
SimBridge.reset_test_state()
|
|
SimBridge._test_player_pos = Vector2i(11, 9)
|
|
var snap = SimBridge._test_snapshot()
|
|
assert_that(snap.has("nearby_interactions")).is_true()
|
|
assert_that(snap.nearby_interactions.size()).is_equal(1)
|
|
assert_that(snap.nearby_interactions[0].entity_id).is_equal(2)
|
|
assert_that(snap.nearby_interactions[0].verbs.size()).is_greater(0)
|
|
|
|
func test_sim_bridge_test_snapshot_no_interaction_when_far() -> void:
|
|
SimBridge.reset_test_state()
|
|
SimBridge._test_player_pos = Vector2i(10, 10)
|
|
var snap = SimBridge._test_snapshot()
|
|
assert_that(snap.nearby_interactions.size()).is_equal(0)
|
|
|
|
func test_sim_bridge_test_snapshot_interaction_at_range_2() -> void:
|
|
SimBridge.reset_test_state()
|
|
SimBridge._test_player_pos = Vector2i(10, 9)
|
|
var snap = SimBridge._test_snapshot()
|
|
assert_that(snap.nearby_interactions.size()).is_equal(1)
|
|
|
|
# -- InteractionPrompt UI --
|
|
|
|
func test_prompt_get_selected_verb_returns_first_kind() -> void:
|
|
GameState.nearby_interactions = [{
|
|
"entity_id": 2, "entity_type": "Npc", "distance": 1,
|
|
"verbs": [
|
|
{"kind": "Talk", "label": "Talk", "priority": 1, "available": true},
|
|
{"kind": "ExamineNpc", "label": "Observe", "priority": 2, "available": true},
|
|
],
|
|
}]
|
|
var prompt = _make_prompt()
|
|
assert_that(prompt.get_selected_verb()).is_equal("Talk")
|
|
prompt.queue_free()
|
|
GameState.nearby_interactions = []
|
|
|
|
func test_prompt_get_selected_verb_empty_when_no_interactions() -> void:
|
|
GameState.nearby_interactions = []
|
|
var prompt = _make_prompt()
|
|
assert_that(prompt.get_selected_verb()).is_equal("")
|
|
prompt.queue_free()
|
|
|
|
func test_prompt_get_target_negative_when_no_interactions() -> void:
|
|
GameState.nearby_interactions = []
|
|
var prompt = _make_prompt()
|
|
assert_that(prompt.get_interaction_target()).is_equal(-1)
|
|
prompt.queue_free()
|
|
|
|
func test_prompt_hidden_initially() -> void:
|
|
GameState.nearby_interactions = []
|
|
var prompt = _make_prompt()
|
|
assert_that(prompt._is_showing).is_false()
|
|
prompt.queue_free()
|
|
|
|
|
|
# -- Input encoding: Interact --
|
|
|
|
func test_interact_encodes_as_struct_variant() -> void:
|
|
# Interact is always a struct variant: {"Interact": {"target_entity_id": null, "verb": null}}
|
|
var inputs: Array = [{"tick": 100, "action_name": "Interact"}]
|
|
var bytes := Protocol.encode_player_inputs(inputs)
|
|
var raw = Messagepack.decode(bytes)
|
|
assert_that(raw.status == null).is_true()
|
|
assert_that(raw.value[0]["action"] is Dictionary).is_true()
|
|
assert_that(raw.value[0]["action"].has("Interact")).is_true()
|
|
var interact_data: Dictionary = raw.value[0]["action"]["Interact"]
|
|
assert_that(interact_data.has("target_entity_id")).is_true()
|
|
assert_that(interact_data.has("verb")).is_true()
|
|
|
|
func test_interact_with_data_encodes_as_data_variant() -> void:
|
|
var inputs: Array = [{
|
|
"tick": 100,
|
|
"action_name": "Interact",
|
|
"action_data": {"target_entity_id": 2, "verb": "Talk"},
|
|
}]
|
|
var bytes := Protocol.encode_player_inputs(inputs)
|
|
var raw = Messagepack.decode(bytes)
|
|
assert_that(raw.status == null).is_true()
|
|
assert_that(raw.value[0]["action"] is Dictionary).is_true()
|
|
assert_that(raw.value[0]["action"].has("Interact")).is_true()
|
|
|
|
|
|
# -- Helpers --
|
|
|
|
func _make_prompt() -> Control:
|
|
var PromptScript = load("res://ui/interaction_prompt.gd")
|
|
var ctrl = Control.new()
|
|
ctrl.set_script(PromptScript)
|
|
add_child(ctrl) # triggers _ready() which creates ImplantPanel + ImplantDataRow
|
|
return ctrl
|