123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- class_name Walker
- extends CharacterBody3D
- signal focus_required(me: Node3D)
- signal got_in(vehicle: SeatedVehicle)
- signal got_out
- signal can_get_in(possible: bool)
- signal chocolate_collected
- ## How fast the player moves in meters per second.
- @export var speed = 14
- ## Convert an axis from [-1, -1] to rad
- @export var turn_to_rad: float = 0.020
- ## The downward acceleration when in the air, in meters per second squared.
- @export var fall_acceleration = 75
- ## Vertical impulse applied to the character upon jumping in meters per second.
- @export var jump_impulse = 20
- @export_group("Boarding", "board_")
- @export var board_door_access_duration: float = 1.0
- @export var board_seat_access_duration: float = 0.5
- var target_velocity := Vector3.ZERO
- var target_character_direction := Vector3.ZERO # From command
- var _vehicle: SeatedVehicle = null
- var _can_get_in: bool = false
- var _in_transition: bool = false
- @onready var _vehicle_range: RayCast3D = $VehicleRange
- func _ready() -> void:
- focus_required.emit(self)
- func trigger_jump() -> void:
- if is_on_floor():
- target_velocity.y = jump_impulse
- func trigger_direction(dir: Vector2) -> void:
- target_character_direction = Vector3(dir.x, 0.0, -dir.y)
- ## Return true if inside a vehicle
- func is_onboard() -> bool:
- return _vehicle != null
- ## Return true if the player can get in a vehicle
- func can_get_in_vehicle() -> bool:
- return !is_onboard() and _get_closest_vehicle() != null
- ## Give this walker a chocolate bar
- func give_chocolate():
- chocolate_collected.emit()
- ## Return local pocket position where the chocolate is collected
- func get_pocket_position() -> Vector3:
- return $ChocolatePocket.get_position()
- func _physics_process(delta: float) -> void:
- if is_onboard():
- pass # Nothing
- else:
- _move_with_feet(delta)
- _signal_when_can_get_in()
- func _move_with_feet(delta: float):
- # Walk according to the camera angle
- var camera_basis: Basis = get_viewport().get_camera_3d().get_camera_transform().basis
- var target_world_direction: Vector3 = camera_basis * target_character_direction
- target_world_direction.y = 0.0
- var target_walk_velocity = target_world_direction.normalized() * speed * target_character_direction.length()
- target_velocity.x = target_walk_velocity.x
- target_velocity.z = target_walk_velocity.z
- # Face forward
- _look_forward(target_velocity)
- # Gravity
- if not is_on_floor():
- target_velocity.y = target_velocity.y - (fall_acceleration * delta)
- # Moving the Character
- velocity = target_velocity
- move_and_slide()
- ## Trigger only signal when ability is changed
- func _signal_when_can_get_in() -> void:
- var can_get_in_now : bool = can_get_in_vehicle()
- if _can_get_in != can_get_in_now:
- can_get_in.emit(can_get_in_now)
- _can_get_in = can_get_in_now
- func _on_dir_changed(dir: Vector2) -> void:
- if is_onboard():
- return
- trigger_direction(dir)
- func _on_main_action(pressed: bool) -> void:
- if is_onboard():
- return
- if pressed:
- trigger_jump()
- func _on_get_in_action(commander: LocalInput) -> void:
- if is_onboard():
- _get_out_vehicle()
- else:
- _get_in_vehicle(commander)
- ## Seek the first vehicle in front of the player
- ## and get in to it
- ## and take the wheel !
- func _get_in_vehicle(commander: LocalInput) -> void:
- if _in_transition:
- return
- var closest_vehicle: SeatedVehicle = _get_closest_vehicle()
- if closest_vehicle == null:
- return # No vehicle to get inside
- _vehicle = closest_vehicle
- reparent(_vehicle)
- add_collision_exception_with(_vehicle)
- _vehicle.become_driven_by(self)
- _vehicle.drive_with(commander)
- _move_to_seat(_vehicle)
- got_in.emit(_vehicle)
- ## Get out of the current vehicle
- func _get_out_vehicle() -> void:
- if _in_transition:
- return
- _init_transition()
- var tween = create_tween()
- tween.tween_property(self, "transform", _vehicle.get_door().get_transform(), board_seat_access_duration)
- tween.tween_callback(_finish_getting_out)
- func _finish_getting_out() -> void:
- _end_transition()
- _vehicle.get_out()
- remove_collision_exception_with(_vehicle)
- reparent(_vehicle.get_parent())
- _vehicle = null
- _get_head_up()
- got_out.emit()
- ## Make the player stand up
- func _get_head_up():
- set_rotation(Vector3(0.0, rotation.y, 0.0))
- ## Make the player look forward
- func _look_forward(forward: Vector3) -> void:
- var horizontal_forward: Vector3 = forward.slide(Vector3.UP)
- if horizontal_forward.is_zero_approx():
- return
- else:
- look_at(position + horizontal_forward)
- ## Return closest vehicle within reach
- ## or null if there are none
- func _get_closest_vehicle() -> SeatedVehicle:
- _vehicle_range.force_raycast_update()
- var object_within_range: Object = _vehicle_range.get_collider()
- if object_within_range is SeatedVehicle:
- return object_within_range as SeatedVehicle
- return null
- ## Move gently to a free seat from the nearest door
- func _move_to_seat(vehicule: SeatedVehicle) -> void:
- _init_transition()
- var tween: Tween = create_tween()
- var door: Node3D = vehicule.get_closest_door(get_position())
- tween.tween_property(self, "position", door.get_position(), board_door_access_duration)
- tween.parallel().tween_property(self, "quaternion", door.get_quaternion(), board_door_access_duration)
- var seat: Node3D = vehicule.get_free_seat()
- tween.tween_property(self, "position", seat.get_position(), board_seat_access_duration)
- tween.parallel().tween_property(self, "quaternion", seat.get_quaternion(), board_seat_access_duration)
- tween.tween_callback(_end_transition)
- ## Lock getting in / out while still doing it
- func _init_transition() -> void:
- _in_transition = true
- ## Allow walker to get in / out again
- func _end_transition() -> void:
- _in_transition = false
|