NEO-25: route E/R interact via main _unhandled_key_input for reliable input
parent
0e72d98177
commit
06d9104157
|
|
@ -91,7 +91,7 @@ On boot, **`InteractablesCatalogClient`** **`GET`s** **`/game/world/interactable
|
||||||
1. Run the game server and client as in NEO-7.
|
1. Run the game server and client as in NEO-7.
|
||||||
2. **F5** in Godot: after catalog load, two interactable sites appear (center + **+X / −Z** floor, anchor **(12, 0.5, −6)** — clear of the gray **Obstacle** near **(6, 1, 5)**). Default spawn **(-5, 0.9, -5)** is out of range of the **center** node (3 m); markers near **origin** stay **dim** until you **WASD** within horizontal **3** m of **(0, 0.5, 0)** on X/Z — then that pair **brightens**.
|
2. **F5** in Godot: after catalog load, two interactable sites appear (center + **+X / −Z** floor, anchor **(12, 0.5, −6)** — clear of the gray **Obstacle** near **(6, 1, 5)**). Default spawn **(-5, 0.9, -5)** is out of range of the **center** node (3 m); markers near **origin** stay **dim** until you **WASD** within horizontal **3** m of **(0, 0.5, 0)** on X/Z — then that pair **brightens**.
|
||||||
3. Press **E** (**`interact`**): Output **`allowed=true`** when the **terminal** pair glows; **`allowed=false`** / **`out_of_range`** when dim. Walk toward **(12, −6)** until the **second** pair glows; press **R** (**`interact_secondary`**) for **`allowed=true`** on the resource node.
|
3. Press **E** (**`interact`**): Output **`allowed=true`** when the **terminal** pair glows; **`allowed=false`** / **`out_of_range`** when dim. Walk toward **(12, −6)** until the **second** pair glows; press **R** (**`interact_secondary`**) for **`allowed=true`** on the resource node.
|
||||||
4. Interaction uses **`_input`** (and key fallbacks) so keys register in the embedded **Game** dock; click the game view if the editor had focus elsewhere.
|
4. **E** / **R** are handled in **`main.gd`** **`_unhandled_key_input`** and forwarded to **`InteractionRequestClient`** (same pattern as dev keys) so they still register in the embedded **Game** dock; click the game view if the editor had focus elsewhere.
|
||||||
|
|
||||||
Full checklist: [`docs/manual-qa/NEO-25.md`](../manual-qa/NEO-25.md).
|
Full checklist: [`docs/manual-qa/NEO-25.md`](../manual-qa/NEO-25.md).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@ extends Node
|
||||||
|
|
||||||
## NS-18 / NEO-25: POST InteractionRequest; prints server allow/deny to Output (prototype).
|
## NS-18 / NEO-25: POST InteractionRequest; prints server allow/deny to Output (prototype).
|
||||||
## [kbd]E[/kbd] → [code]prototype_terminal[/code]; [kbd]R[/kbd] → [code]prototype_resource_node_alpha[/code]
|
## [kbd]E[/kbd] → [code]prototype_terminal[/code]; [kbd]R[/kbd] → [code]prototype_resource_node_alpha[/code]
|
||||||
## (stable contract ids — must match server registry).
|
## (stable contract ids — must match server registry). Key handling is delegated from
|
||||||
|
## [code]main.gd[/code] [method Node._unhandled_key_input] (see [method post_interact_terminal] /
|
||||||
|
## [method post_interact_resource]) so the embedded Game dock still receives E/R reliably.
|
||||||
|
|
||||||
const ID_TERMINAL := "prototype_terminal"
|
const ID_TERMINAL := "prototype_terminal"
|
||||||
const ID_RESOURCE_NODE := "prototype_resource_node_alpha"
|
const ID_RESOURCE_NODE := "prototype_resource_node_alpha"
|
||||||
|
|
@ -28,17 +30,12 @@ func _ready() -> void:
|
||||||
_http.request_completed.connect(_on_request_completed)
|
_http.request_completed.connect(_on_request_completed)
|
||||||
|
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func post_interact_terminal() -> void:
|
||||||
if event.is_action_pressed("interact"):
|
_post_interact(ID_TERMINAL)
|
||||||
_post_interact(ID_TERMINAL)
|
|
||||||
elif event.is_action_pressed("interact_secondary"):
|
|
||||||
_post_interact(ID_RESOURCE_NODE)
|
func post_interact_resource() -> void:
|
||||||
elif event is InputEventKey:
|
_post_interact(ID_RESOURCE_NODE)
|
||||||
var k := event as InputEventKey
|
|
||||||
if k.pressed and not k.echo and (k.physical_keycode == KEY_E or k.keycode == KEY_E):
|
|
||||||
_post_interact(ID_TERMINAL)
|
|
||||||
elif k.pressed and not k.echo and (k.physical_keycode == KEY_R or k.keycode == KEY_R):
|
|
||||||
_post_interact(ID_RESOURCE_NODE)
|
|
||||||
|
|
||||||
|
|
||||||
func _base_root() -> String:
|
func _base_root() -> String:
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ var _dev_obstacle_smoke: Node3D
|
||||||
@onready var _player_pos_label: Label = $UICanvas/PlayerPositionLabel
|
@onready var _player_pos_label: Label = $UICanvas/PlayerPositionLabel
|
||||||
@onready var _target_lock_label: Label = $UICanvas/TargetLockLabel
|
@onready var _target_lock_label: Label = $UICanvas/TargetLockLabel
|
||||||
@onready var _target_client: Node = $TargetSelectionClient
|
@onready var _target_client: Node = $TargetSelectionClient
|
||||||
|
@onready var _interaction_client: Node = $InteractionRequestClient
|
||||||
@onready var _floor: StaticBody3D = $World/NavigationRegion3D/Floor
|
@onready var _floor: StaticBody3D = $World/NavigationRegion3D/Floor
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -279,11 +280,34 @@ func _on_move_rejected(reason_code: String) -> void:
|
||||||
## **F9** / **F10** are debugger shortcuts in the embedded game; use the Input Map action.
|
## **F9** / **F10** are debugger shortcuts in the embedded game; use the Input Map action.
|
||||||
## For a true **freed-node** occluder test, reload the scene or remove the body in the editor.
|
## For a true **freed-node** occluder test, reload the scene or remove the body in the editor.
|
||||||
func _unhandled_key_input(event: InputEvent) -> void:
|
func _unhandled_key_input(event: InputEvent) -> void:
|
||||||
|
# NEO-25: route interact keys here — `InteractionRequestClient._input` is unreliable in the
|
||||||
|
# embedded Game dock / focus order; `_unhandled_key_input` on the scene root still fires.
|
||||||
|
if event.is_action_pressed("interact"):
|
||||||
|
_forward_interact_post("post_interact_terminal")
|
||||||
|
return
|
||||||
|
if event.is_action_pressed("interact_secondary"):
|
||||||
|
_forward_interact_post("post_interact_resource")
|
||||||
|
return
|
||||||
|
if event is InputEventKey:
|
||||||
|
var k := event as InputEventKey
|
||||||
|
if k.pressed and not k.echo and (k.physical_keycode == KEY_E or k.keycode == KEY_E):
|
||||||
|
_forward_interact_post("post_interact_terminal")
|
||||||
|
return
|
||||||
|
if k.pressed and not k.echo and (k.physical_keycode == KEY_R or k.keycode == KEY_R):
|
||||||
|
_forward_interact_post("post_interact_resource")
|
||||||
|
return
|
||||||
if not event.is_action_pressed("dev_toggle_occluder_obstacle"):
|
if not event.is_action_pressed("dev_toggle_occluder_obstacle"):
|
||||||
return
|
return
|
||||||
call_deferred("_dev_toggle_obstacle_smoke_deferred")
|
call_deferred("_dev_toggle_obstacle_smoke_deferred")
|
||||||
|
|
||||||
|
|
||||||
|
func _forward_interact_post(method: String) -> void:
|
||||||
|
if not is_instance_valid(_interaction_client):
|
||||||
|
return
|
||||||
|
if _interaction_client.has_method(method):
|
||||||
|
_interaction_client.call(method)
|
||||||
|
|
||||||
|
|
||||||
func _dev_toggle_obstacle_smoke_deferred() -> void:
|
func _dev_toggle_obstacle_smoke_deferred() -> void:
|
||||||
var obstacle := _dev_obstacle_smoke
|
var obstacle := _dev_obstacle_smoke
|
||||||
if obstacle == null or not is_instance_valid(obstacle):
|
if obstacle == null or not is_instance_valid(obstacle):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue