extends RigidBody3D @export var drive_speed := 18.0 @export var drive_accel := 20.0 @export var brake_strength := 28.0 @export var turn_speed := 2.0 @export var turn_accel := 8.0 @export var lateral_damp := 10.0 @export var launch_impulse := 28.0 @export var launch_up_impulse := 6.0 @export var seat_path: NodePath @export var exit_path: NodePath @export var camera_path: NodePath @export var interact_area_path: NodePath @onready var seat: Node3D = get_node(seat_path) if seat_path != NodePath("") else null @onready var exit_point: Node3D = get_node(exit_path) if exit_path != NodePath("") else null @onready var car_camera: Camera3D = get_node(camera_path) if camera_path != NodePath("") else null @onready var interact_area: Area3D = get_node(interact_area_path) if interact_area_path != NodePath("") else null var _nearby_driver: Node = null var _driver: Node = null func _ready() -> void: if interact_area: interact_area.collision_layer = 2 interact_area.collision_mask = 1 interact_area.body_entered.connect(_on_interact_body_entered) interact_area.body_exited.connect(_on_interact_body_exited) if car_camera: car_camera.current = false contact_monitor = true max_contacts_reported = 8 func _process(_delta: float) -> void: if _driver == null and _nearby_driver != null and Input.is_action_just_pressed("interact"): _enter_vehicle(_nearby_driver) elif _driver != null and Input.is_action_just_pressed("interact"): _exit_vehicle() func _integrate_forces(state: PhysicsDirectBodyState3D) -> void: if _driver == null: return var input2v := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") var forward := global_transform.basis.z forward.y = 0.0 forward = forward.normalized() var current_speed := linear_velocity.dot(forward) var target_speed := input2v.y * drive_speed var accel := drive_accel if abs(input2v.y) > 0.01 else brake_strength var new_forward_speed := move_toward(current_speed, target_speed, accel * state.step) var forward_vel := forward * new_forward_speed var lateral_vel := linear_velocity - (forward * current_speed) lateral_vel = lateral_vel.move_toward(Vector3.ZERO, lateral_damp * state.step) linear_velocity = forward_vel + lateral_vel var speed_factor: float = clamp(abs(new_forward_speed) / drive_speed, 0.0, 1.0) var turn_input := input2v.x if new_forward_speed < -0.1: turn_input = -turn_input var target_turn: float = turn_input * turn_speed * speed_factor angular_velocity.y = move_toward(angular_velocity.y, target_turn, turn_accel * state.step) if speed_factor > 0.2: var hit_ids := {} for i in state.get_contact_count(): var collider := state.get_contact_collider_object(i) if collider is RigidBody3D and collider != self: var collider_id := (collider as Node).get_instance_id() if hit_ids.has(collider_id): continue hit_ids[collider_id] = true var contact_pos := state.get_contact_collider_position(i) var launch_dir := (contact_pos - global_position).normalized() if launch_dir.length() <= 0.001: var normal_world := (global_transform.basis * state.get_contact_local_normal(i)).normalized() launch_dir = normal_world if normal_world.length() > 0.001 else forward.normalized() var impulse := launch_dir * (launch_impulse * speed_factor) + Vector3.UP * launch_up_impulse (collider as RigidBody3D).apply_central_impulse(impulse) func _enter_vehicle(player: Node) -> void: if seat == null: return _driver = player player.call("enter_vehicle", self, seat, car_camera) if car_camera: car_camera.current = true func _exit_vehicle() -> void: if _driver == null: return _driver.call("exit_vehicle", exit_point, car_camera) _driver = null if car_camera: car_camera.current = false func _on_interact_body_entered(body: Node) -> void: if body.has_method("enter_vehicle"): _nearby_driver = body func _on_interact_body_exited(body: Node) -> void: if body == _nearby_driver: _nearby_driver = null