walker.gd 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. class_name Walker
  2. extends CharacterBody3D
  3. signal focus_required(me: Node3D)
  4. signal got_in(vehicle: SeatedVehicle)
  5. signal got_out
  6. signal can_get_in(possible: bool)
  7. signal chocolate_collected
  8. ## How fast the player moves in meters per second.
  9. @export var speed = 14
  10. ## Convert an axis from [-1, -1] to rad
  11. @export var turn_to_rad: float = 0.020
  12. ## The downward acceleration when in the air, in meters per second squared.
  13. @export var fall_acceleration = 75
  14. ## Vertical impulse applied to the character upon jumping in meters per second.
  15. @export var jump_impulse = 20
  16. @export_group("Boarding", "board_")
  17. @export var board_door_access_duration: float = 1.0
  18. @export var board_seat_access_duration: float = 0.5
  19. var target_velocity := Vector3.ZERO
  20. var target_character_direction := Vector3.ZERO # From command
  21. var _vehicle: SeatedVehicle = null
  22. var _can_get_in: bool = false
  23. @onready var _vehicle_range: RayCast3D = $VehicleRange
  24. func _ready() -> void:
  25. focus_required.emit(self)
  26. func trigger_jump() -> void:
  27. if is_on_floor():
  28. target_velocity.y = jump_impulse
  29. func trigger_direction(dir: Vector2) -> void:
  30. target_character_direction = Vector3(dir.x, 0.0, -dir.y)
  31. ## Return true if inside a vehicle
  32. func is_onboard() -> bool:
  33. return _vehicle != null
  34. ## Return true if the player can get in a vehicle
  35. func can_get_in_vehicle() -> bool:
  36. return !is_onboard() and _get_closest_vehicle() != null
  37. ## Give this walker a chocolate bar
  38. func give_chocolate():
  39. chocolate_collected.emit()
  40. ## Return local pocket position where the chocolate is collected
  41. func get_pocket_position() -> Vector3:
  42. return $ChocolatePocket.get_position()
  43. func _physics_process(delta: float) -> void:
  44. if is_onboard():
  45. pass # Nothing
  46. else:
  47. _move_with_feet(delta)
  48. _signal_when_can_get_in()
  49. func _move_with_feet(delta: float):
  50. # Walk according to the camera angle
  51. var camera_basis: Basis = get_viewport().get_camera_3d().get_camera_transform().basis
  52. var target_world_direction: Vector3 = camera_basis * target_character_direction
  53. target_world_direction.y = 0.0
  54. var target_walk_velocity = target_world_direction.normalized() * speed * target_character_direction.length()
  55. target_velocity.x = target_walk_velocity.x
  56. target_velocity.z = target_walk_velocity.z
  57. # Face forward
  58. _look_forward(target_velocity)
  59. # Gravity
  60. if not is_on_floor():
  61. target_velocity.y = target_velocity.y - (fall_acceleration * delta)
  62. # Moving the Character
  63. velocity = target_velocity
  64. move_and_slide()
  65. ## Trigger only signal when ability is changed
  66. func _signal_when_can_get_in() -> void:
  67. var can_get_in_now : bool = can_get_in_vehicle()
  68. if _can_get_in != can_get_in_now:
  69. can_get_in.emit(can_get_in_now)
  70. _can_get_in = can_get_in_now
  71. func _on_dir_changed(dir: Vector2) -> void:
  72. if is_onboard():
  73. return
  74. trigger_direction(dir)
  75. func _on_main_action(pressed: bool) -> void:
  76. if is_onboard():
  77. return
  78. if pressed:
  79. trigger_jump()
  80. func _on_get_in_action(commander: LocalInput) -> void:
  81. if is_onboard():
  82. _get_out_vehicle()
  83. else:
  84. _get_in_vehicle(commander)
  85. ## Seek the first vehicle in front of the player
  86. ## and get in to it
  87. ## and take the wheel !
  88. func _get_in_vehicle(commander: LocalInput) -> void:
  89. var closest_vehicle: SeatedVehicle = _get_closest_vehicle()
  90. if closest_vehicle == null:
  91. return # No vehicle to get inside
  92. _vehicle = closest_vehicle
  93. reparent(_vehicle)
  94. add_collision_exception_with(_vehicle)
  95. _vehicle.drive_with(commander)
  96. _move_to_seat(_vehicle)
  97. got_in.emit(_vehicle)
  98. ## Get out of the current vehicle
  99. func _get_out_vehicle() -> void:
  100. _vehicle.get_out()
  101. remove_collision_exception_with(_vehicle)
  102. reparent(_vehicle.get_parent())
  103. _vehicle = null
  104. _get_head_up()
  105. got_out.emit()
  106. ## Make the player stand up
  107. func _get_head_up():
  108. set_rotation(Vector3(0.0, rotation.y, 0.0))
  109. ## Make the player look forward
  110. func _look_forward(forward: Vector3) -> void:
  111. var horizontal_forward: Vector3 = forward.slide(Vector3.UP)
  112. if horizontal_forward.is_zero_approx():
  113. return
  114. else:
  115. look_at(position + horizontal_forward)
  116. ## Return closest vehicle within reach
  117. ## or null if there are none
  118. func _get_closest_vehicle() -> SeatedVehicle:
  119. _vehicle_range.force_raycast_update()
  120. var object_within_range: Object = _vehicle_range.get_collider()
  121. if object_within_range is SeatedVehicle:
  122. return object_within_range as SeatedVehicle
  123. return null
  124. ## Move gently to a free seat from the nearest door
  125. func _move_to_seat(vehicule: SeatedVehicle) -> void:
  126. var tween: Tween = create_tween()
  127. var door: Node3D = vehicule.get_closest_door(get_position())
  128. tween.tween_property(self, "position", door.get_position(), board_door_access_duration)
  129. tween.parallel().tween_property(self, "quaternion", door.get_quaternion(), board_door_access_duration)
  130. var seat: Node3D = vehicule.get_free_seat()
  131. tween.tween_property(self, "position", seat.get_position(), board_seat_access_duration)
  132. tween.parallel().tween_property(self, "quaternion", seat.get_quaternion(), board_seat_access_duration)