Files
boatgame/scripts/player_controller.gd
T
2026-07-10 17:26:23 +02:00

122 lines
3.6 KiB
GDScript

class_name PlayerController
extends Node
@export var current_controlled : Boat:
set(value):
if current_controlled != null and current_controlled != value:
current_controlled.desired_throttle = 0.0
current_controlled.steer_input = 0.0
current_controlled.fire_intents.clear()
current_controlled.aim_point = null
current_controlled = value
if is_node_ready():
if current_controlled != null:
desired_throttle = current_controlled.throttle
_possess()
@export var world_manager : WorldManager:
set(value):
world_manager = value
if is_node_ready():
_possess()
@export var camera_rig : CameraRig:
set(value):
camera_rig = value
if is_node_ready():
_possess()
@export var hud : Hud:
set(value):
hud = value
if is_node_ready():
_possess()
@export var throttle_ramp := 0.5
@export var aim_ray_length := 2000.0
@export_flags_3d_physics var aim_collision_mask := 0b1100111 # terrain, player, enemies, water, subsystems
@export var aim_marker: Node3D
var desired_throttle := 0.0
func _ready() -> void:
if current_controlled != null: # Only happens on intial loading if boat already moves
desired_throttle = current_controlled.throttle
_possess()
func _physics_process(_delta: float) -> void:
if current_controlled == null:
if aim_marker != null:
aim_marker.visible = false
return
current_controlled.aim_point = _compute_aim_point()
if aim_marker != null:
var p = current_controlled.aim_point
aim_marker.visible = p != null
if p != null:
aim_marker.global_position = p
func _process(delta: float) -> void:
if current_controlled == null:
return
if Input.is_action_pressed("throttle_increase"):
desired_throttle = move_toward(desired_throttle, 1, throttle_ramp * delta)
elif Input.is_action_pressed("throttle_decrease"):
desired_throttle = move_toward(desired_throttle, -1, throttle_ramp * delta)
elif Input.is_action_pressed("throttle_stop"):
desired_throttle = 0.0
var steer_input := Input.get_axis("rudder_right", "rudder_left")
current_controlled.desired_throttle = desired_throttle
current_controlled.steer_input = steer_input
current_controlled.fire_intents[0] = Input.is_action_pressed("fire")
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed(&"debug_possess_next"):
_possess_next()
func _possess() -> void:
if camera_rig != null:
camera_rig.target = current_controlled
if world_manager != null:
world_manager.focus = current_controlled
if hud != null:
hud.boat = current_controlled
func _possess_next() -> void:
var boats := get_tree().get_nodes_in_group(&"boats")
if boats.is_empty():
current_controlled = null
return
var idx := boats.find(current_controlled)
current_controlled = boats[(idx + 1) % boats.size()]
func _compute_aim_point():
if camera_rig == null or camera_rig.cam == null:
return null
var cam := camera_rig.cam
var mouse := get_viewport().get_mouse_position()
var from := cam.project_ray_origin(mouse)
var dir := cam.project_ray_normal(mouse)
var query := PhysicsRayQueryParameters3D.create(
from, from + dir * aim_ray_length,
aim_collision_mask, current_controlled.aim_ray_exclusions()
)
var hit := current_controlled.get_world_3d().direct_space_state.intersect_ray(query)
if hit.is_empty():
return from + dir * aim_ray_length # above the horizon
if hit.collider.is_in_group(&"water"):
# Flat-plane hit → refine to the actual wave surface.
var ocean := get_tree().get_first_node_in_group(&"ocean") as Ocean
if ocean != null:
var refined = Plane(Vector3.UP, ocean.get_wave_height(hit.position)).intersects_ray(from, dir)
if refined != null:
return refined
return hit.position