Implement prototype: venue-driven simulation core, scenarios, tests, and playable entrance scene
CI / headless-tests (push) Canceled after 49s
CI / headless-tests (push) Canceled after 49s
- Godot 4.3 project scaffold with first-person guard, procedural world, interaction HUD, access inspection, radio forms, debug overlay, shift review - Simulation core: ShiftCoordinator, VenueScenario, EventScheduler, NPCController/FSM, AccessVerifier, EscalationManager, RadioSystem, IncidentRecorder, ReputationEvaluator, CrowdSystem, EventBus - Three authored scenarios (Hollywood Blvd building, The Joke Shop, TV studio) - Custom headless test runner (25 tests) replacing editor-dependent GUT, plus GitHub Actions CI - Documentation workflow live: README, PLAN (tech stack), TODO/DONE, MEMORY
This commit is contained in:
1 parent
e23f284ad4
commit
439e0a4281
45 files changed
+4368
-7
No files matched your search
@@ -0,0 +1,83 @@
|
||||
class_name GuardController
|
||||
extends CharacterBody3D
|
||||
## First-person guard controller: WASD movement, mouse look, interact raycast.
|
||||
|
||||
signal picked_target(target: Node)
|
||||
signal cleared_target()
|
||||
|
||||
var move_speed: float = 3.2
|
||||
var look_sensitivity: float = 0.0022
|
||||
var is_ready := false
|
||||
var targeted_npc: Node3D = null
|
||||
|
||||
@onready var camera: Camera3D = $Head/Camera3D
|
||||
@onready var head: Node3D = $Head
|
||||
|
||||
func _ready() -> void:
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||
is_ready = true
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
|
||||
rotate_y(-event.relative.x * look_sensitivity)
|
||||
head.rotate_x(-event.relative.y * look_sensitivity)
|
||||
head.rotation.x = clampf(head.rotation.x, deg_to_rad(-80.0), deg_to_rad(80.0))
|
||||
elif event is InputEventKey and event.pressed and event.keycode == KEY_ESCAPE:
|
||||
if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||
else:
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
# Movement relative to the head's yaw only.
|
||||
var forward := -global_transform.basis.z
|
||||
forward.y = 0.0
|
||||
forward = forward.normalized()
|
||||
var right := global_transform.basis.x
|
||||
right.y = 0.0
|
||||
right = right.normalized()
|
||||
|
||||
var input_dir := Vector2(
|
||||
Input.get_axis("move_left", "move_right"),
|
||||
Input.get_axis("move_forward", "move_back")
|
||||
)
|
||||
var direction := (forward * input_dir.y + right * input_dir.x).normalized()
|
||||
velocity.x = direction.x * move_speed
|
||||
velocity.z = direction.z * move_speed
|
||||
velocity.y -= 9.8 * _delta
|
||||
move_and_slide()
|
||||
_update_target()
|
||||
|
||||
func _update_target() -> void:
|
||||
var space := get_world_3d().direct_space_state
|
||||
var from := camera.global_position
|
||||
var to := from + -camera.global_transform.basis.z * 8.0
|
||||
var query := PhysicsRayQueryParameters3D.create(from, to)
|
||||
query.collide_with_areas = true
|
||||
query.collide_with_bodies = true
|
||||
query.exclude = [get_rid()]
|
||||
var hit := space.intersect_ray(query)
|
||||
var new_target: Node3D = null
|
||||
if hit:
|
||||
var obj: Object = hit.get("collider")
|
||||
var parent_avatar: Node3D = _find_avatar(obj)
|
||||
if parent_avatar != null:
|
||||
new_target = parent_avatar
|
||||
if new_target != targeted_npc:
|
||||
targeted_npc = new_target
|
||||
if new_target != null:
|
||||
picked_target.emit(new_target)
|
||||
else:
|
||||
cleared_target.emit()
|
||||
|
||||
func _find_avatar(obj: Object) -> Node3D:
|
||||
# Avatars are CharacterBody3D with a collision shape; guard against self and scene junk.
|
||||
var n := obj as Node
|
||||
if n == null:
|
||||
return null
|
||||
var current: Node = n
|
||||
while current != null:
|
||||
if current.has_method("get_npc_controller"):
|
||||
return current as Node3D
|
||||
current = current.get_parent()
|
||||
return null
|
||||
Reference in new issue
Block a user