You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
322 lines
7.7 KiB
322 lines
7.7 KiB
extends CharacterBody2D
|
|
|
|
class_name Player
|
|
|
|
@export_file('*.tscn') var LOSE_SCREEN;
|
|
|
|
@onready var sound_player = $sound_player
|
|
@onready var gliding_player = $gliding_player
|
|
@onready var animator = $animator
|
|
@onready var body = $oriented_container/body
|
|
@onready var grab_ray = get_node('oriented_container/body/grab_ray')
|
|
@onready var sprite = get_node('oriented_container/body')
|
|
|
|
const SPEED = 100.0
|
|
const JUMP_VELOCITY = 200.0
|
|
const APNEA_DURATION = 10
|
|
const DURATION_BEFORE_LOSE_SCREEN = 2
|
|
|
|
|
|
|
|
const STATES= [
|
|
"climb",
|
|
"die",
|
|
"idle",
|
|
"jump",
|
|
"spawn",
|
|
"walk",
|
|
"swim"
|
|
]
|
|
|
|
var use_gravity = {
|
|
"climb": false,
|
|
"die": true,
|
|
"idle": true,
|
|
"jump": true,
|
|
"spawn": true,
|
|
"walk": true,
|
|
"swim": true
|
|
}
|
|
|
|
var can_grab_obstacles = {
|
|
"climb": true,
|
|
"die": false,
|
|
"idle": true,
|
|
"jump": true,
|
|
"spawn": false,
|
|
"walk": true,
|
|
"swim": true
|
|
}
|
|
|
|
const SOUNDS = {
|
|
"death": {"asset": preload("res://assets/sounds/player_death.wav"), "volume": 0.5},
|
|
"jump": {"asset": preload("res://assets/sounds/jump.wav"), "volume": 0.5},
|
|
"grab": {"asset": preload("res://assets/sounds/grab.wav"), "volume": 0.5},
|
|
"gliding": {"asset": preload("res://assets/sounds/gliding.wav"), "volume": -20},
|
|
}
|
|
|
|
|
|
# Get the gravity from the project settings to be synced with RigidBody nodes.
|
|
var WORLD_GRAVITY = ProjectSettings.get_setting("physics/2d/default_gravity")
|
|
var gravity = WORLD_GRAVITY
|
|
|
|
var grabbed_obstacle = null
|
|
|
|
func _ready():
|
|
$fsm.set_states(STATES)
|
|
add_to_group("player")
|
|
g_game_state.restore_checkpoint.connect(on_restore_checkpoint)
|
|
gliding_player.finished.connect(on_gliding_sound_finished)
|
|
|
|
func _exit_tree():
|
|
g_game_state.restore_checkpoint.disconnect(on_restore_checkpoint)
|
|
|
|
func on_enter_spawn_state():
|
|
$fsm.set_next_state("idle")
|
|
|
|
func on_enter_idle_state():
|
|
pass
|
|
|
|
func idle_state(_delta):
|
|
var direction = Input.get_axis("player_left", "player_right")
|
|
|
|
if is_on_floor():
|
|
velocity.x = 0
|
|
|
|
if direction:
|
|
$fsm.set_next_state("walk")
|
|
elif Input.is_action_just_pressed("player_jump") and is_on_floor():
|
|
$fsm.set_next_state("jump")
|
|
|
|
func walk_state(_delta):
|
|
var direction = Input.get_axis("player_left", "player_right")
|
|
|
|
if direction != 0:
|
|
velocity.x = direction * SPEED
|
|
else:
|
|
velocity.x = 0
|
|
$fsm.set_next_state("idle")
|
|
|
|
if Input.is_action_just_pressed("player_jump") and is_on_floor():
|
|
$fsm.set_next_state("jump")
|
|
|
|
if is_on_wall() or (Input.is_action_pressed('player_up') && is_on_ceiling()):
|
|
velocity.y = 0
|
|
$fsm.set_next_state("climb")
|
|
|
|
func on_enter_jump_state():
|
|
if is_on_floor():
|
|
velocity.y = -JUMP_VELOCITY
|
|
sound_player.play_sound(SOUNDS["jump"])
|
|
|
|
func jump_state(_delta):
|
|
var direction = Input.get_axis("player_left", "player_right")
|
|
|
|
if(direction):
|
|
velocity.x = move_toward(velocity.x, direction * SPEED, SPEED)
|
|
|
|
|
|
if is_on_floor() and velocity.y > 0:
|
|
if direction:
|
|
$fsm.set_next_state("walk")
|
|
else:
|
|
velocity.x = 0
|
|
$fsm.set_next_state("idle")
|
|
|
|
if is_on_wall() || (is_on_ceiling() ):
|
|
$fsm.set_next_state("climb")
|
|
|
|
func on_exit_jump_state():
|
|
sound_player.stop_sound()
|
|
|
|
func on_enter_climb_state():
|
|
velocity = Vector2(0, 0)
|
|
|
|
func climb_state(_delta):
|
|
if is_on_wall():
|
|
var direction = Input.get_axis("player_up", "player_down")
|
|
velocity = Vector2(0, direction * SPEED)
|
|
|
|
if is_on_ceiling():
|
|
var direction = Input.get_axis("player_left", "player_right")
|
|
velocity = Vector2(direction * SPEED, -1)
|
|
|
|
if Input.is_action_just_pressed("player_jump") and is_on_wall():
|
|
var wall_position = get_slide_collision(0).get_normal().x
|
|
var jump_normal = Vector2(wall_position, -0.5).normalized()
|
|
velocity = jump_normal * JUMP_VELOCITY
|
|
|
|
$fsm.set_next_state("jump")
|
|
if is_on_ceiling() && Input.is_action_just_pressed("player_down"):
|
|
velocity.y = 0
|
|
$fsm.set_next_state("idle")
|
|
|
|
if !is_on_wall() && !is_on_ceiling():
|
|
$fsm.set_next_state("idle")
|
|
|
|
|
|
func after_state(delta):
|
|
move_and_slide()
|
|
if is_dead():
|
|
return
|
|
|
|
if use_gravity[$fsm.current_state] == true:
|
|
handle_gravity(delta)
|
|
|
|
if can_grab_obstacles[$fsm.current_state] == true:
|
|
handle_grab()
|
|
|
|
handle_glide()
|
|
|
|
handle_sprite_orientation()
|
|
|
|
func handle_glide():
|
|
if Input.is_action_just_pressed('player_jump'):
|
|
gliding_player.play_sound(SOUNDS["gliding"])
|
|
if Input.is_action_just_released('player_jump'):
|
|
gliding_player.stop_sound()
|
|
|
|
func handle_gravity(delta):
|
|
var gravity_delta = gravity
|
|
|
|
if velocity.y > 0:
|
|
if Input.is_action_pressed('player_jump'):
|
|
gravity_delta = WORLD_GRAVITY / 10
|
|
else:
|
|
gravity_delta = WORLD_GRAVITY
|
|
|
|
velocity.y += gravity_delta * delta
|
|
|
|
func handle_grab():
|
|
if Input.is_action_just_pressed('player_action'):
|
|
if grabbed_obstacle == null:
|
|
var collision = grab_ray.get_collider()
|
|
|
|
if collision:
|
|
var obstacle = collision
|
|
if obstacle.is_in_group("obstacle"):
|
|
do_grab(obstacle)
|
|
else:
|
|
drop_obstacle()
|
|
|
|
func do_grab(obstacle):
|
|
# BUGFIX : When grabbing a rock, allows other rocks to fall
|
|
for ob in get_tree().get_nodes_in_group("obstacle"):
|
|
if ob.has_method("set_sleeping"):
|
|
ob.sleeping = false
|
|
|
|
grabbed_obstacle = obstacle
|
|
obstacle.grabbed($oriented_container)
|
|
sound_player.play_sound(SOUNDS["grab"])
|
|
|
|
|
|
func drop_obstacle():
|
|
grabbed_obstacle.dropped()
|
|
grabbed_obstacle = null
|
|
|
|
func handle_sprite_orientation():
|
|
|
|
if is_on_wall_only():
|
|
if velocity.y != 0:
|
|
var wall_position = get_slide_collision(0).get_position().x
|
|
var player_position = $oriented_container.get_global_position().x
|
|
var is_wall_right = wall_position > player_position
|
|
|
|
if is_wall_right && velocity.y > 0:
|
|
body.look_down_on_right_wall()
|
|
put_grabbed_object_at(Vector2(0, 35))
|
|
|
|
if is_wall_right && velocity.y < 0:
|
|
body.look_up_on_right_wall()
|
|
put_grabbed_object_at(Vector2(0, -35))
|
|
|
|
if !is_wall_right && velocity.y > 0:
|
|
body.look_down_on_left_wall()
|
|
put_grabbed_object_at(Vector2(0, 35))
|
|
|
|
if !is_wall_right && velocity.y < 0:
|
|
body.look_up_on_left_wall()
|
|
put_grabbed_object_at(Vector2(0, -35))
|
|
|
|
|
|
elif is_on_ceiling_only():
|
|
if velocity.x != 0:
|
|
if velocity.x > 0:
|
|
body.look_right_on_ceiling()
|
|
put_grabbed_object_at(Vector2(35, 0))
|
|
else:
|
|
body.look_left_on_ceiling()
|
|
put_grabbed_object_at(Vector2(-35, 0))
|
|
else:
|
|
if velocity.x != 0:
|
|
if velocity.x > 0:
|
|
body.look_right()
|
|
put_grabbed_object_at(Vector2(35, 0))
|
|
else:
|
|
body.look_left()
|
|
put_grabbed_object_at(Vector2(-35, 0))
|
|
|
|
func put_grabbed_object_at(at):
|
|
if grabbed_obstacle:
|
|
grabbed_obstacle.position = at
|
|
|
|
|
|
func on_enter_swim_state():
|
|
sprite.modulate = Color(0, 0, 1) # TODO : light effect
|
|
gravity = WORLD_GRAVITY / 2
|
|
|
|
func swim_state(_delta):
|
|
var direction_x = Input.get_axis("player_left", "player_right")
|
|
var direction_y = Input.get_axis("player_up", "player_down")
|
|
velocity = Vector2(direction_x, direction_y).normalized() * SPEED
|
|
|
|
var progress_to_black = 1 - clamp($fsm.state_duration / APNEA_DURATION, 0, 1)
|
|
sprite.modulate = Color(progress_to_black, progress_to_black, progress_to_black )
|
|
|
|
if $fsm.state_duration >= APNEA_DURATION:
|
|
die()
|
|
|
|
func on_exit_swim_state():
|
|
sprite.modulate = Color(1, 1, 1)
|
|
gravity = WORLD_GRAVITY
|
|
sprite.modulate = Color(1,1,1)
|
|
|
|
func swim():
|
|
if is_dead():
|
|
return
|
|
|
|
$fsm.set_next_state("swim")
|
|
|
|
func unswim():
|
|
if is_dead():
|
|
return
|
|
|
|
$fsm.set_next_state("idle")
|
|
|
|
func die():
|
|
$fsm.set_next_state("die")
|
|
sound_player.play_sound(SOUNDS["death"])
|
|
|
|
func is_dead():
|
|
return $fsm.current_state == "die"
|
|
|
|
func on_enter_die_state():
|
|
velocity = Vector2(0, 0)
|
|
$shape.disabled = true
|
|
$oriented_container.rotation = 0
|
|
sprite.flip_v = true
|
|
|
|
|
|
func die_state(_delta):
|
|
if $fsm.state_duration > DURATION_BEFORE_LOSE_SCREEN:
|
|
get_tree().change_scene_to_file(LOSE_SCREEN)
|
|
|
|
func on_restore_checkpoint(_level_rising):
|
|
global_position = g_game_state.player_position_at_checkpoint
|
|
|
|
func on_gliding_sound_finished():
|
|
if Input.is_action_pressed('player_jump'):
|
|
gliding_player.play_sound(SOUNDS["gliding"])
|
|
|
|
func on_state_changed(_previous, current):
|
|
animator.play(current)
|
|
|