extends CharacterBody2D class_name Player const SPEED = 100.0 const JUMP_VELOCITY = 200.0 const APNEA_DURATION = 10 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": false, "die": false, "idle": true, "jump": false, "spawn": false, "walk": true, "swim": true } @onready var grab_ray = get_node('oriented_container/grab_ray') @onready var sprite = get_node('oriented_container/sprite') # 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") 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 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(): if direction: $fsm.set_next_state("walk") else: velocity.x = 0 $fsm.set_next_state("idle") if is_on_wall() || (is_on_ceiling() and Input.is_action_pressed("player_up")): $fsm.set_next_state("climb") func on_enter_climb_state(): sprite.modulate = Color(1, 0, 0) velocity = Vector2(0, 0) func on_exit_climb_state(): sprite.modulate = Color(1, 1, 1) 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, 0) 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 use_gravity[$fsm.current_state] == true: velocity.y += gravity * delta if can_grab_obstacles[$fsm.current_state] == true: handle_grab() handle_sprite_orientation() func handle_grab(): if Input.is_action_just_pressed('player_action'): if grabbed_obstacle == null: var collision = grab_ray.get_collider() print('collision', collision) if collision: var obstacle = collision print('obstacle', obstacle) if obstacle.is_in_group("obstacle"): do_grab(obstacle) else: drop_obstacle() func do_grab(obstacle): grabbed_obstacle = obstacle obstacle.grabbed($oriented_container) func drop_obstacle(): grabbed_obstacle.dropped() grabbed_obstacle = null func handle_sprite_orientation(): # rotates oriented_container to face # the direction of the velocity # if is_on_wall, rotated regarding velocity.y, with up by default # if is_on_ceiling, rotated regarding velocity.x, with right by default if is_on_wall_only(): if velocity.y != 0: $oriented_container.rotation = PI/2 if (velocity.y > 0) else - PI / 2 var wall_position = get_slide_collision(0).get_position().x var player_position = $oriented_container.get_global_position().x var should_flip_with_wall_left = (wall_position > player_position) && (velocity.y > 0) var should_flip_with_wall_right = (wall_position < player_position) && (velocity.y < 0) sprite.flip_v = should_flip_with_wall_left || should_flip_with_wall_right elif is_on_ceiling(): if velocity.x != 0: $oriented_container.rotation = PI if (velocity.x > 0) else -PI else: if velocity.x != 0: $oriented_container.rotation = 0 if (velocity.x > 0) else PI sprite.flip_v = velocity.x < 0 func on_enter_swim_state(): 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(): gravity = WORLD_GRAVITY sprite.modulate = Color(1,1,1) func swim(): $fsm.set_next_state("swim") func unswim(): $fsm.set_next_state("idle") func die(): $fsm.set_next_state("die") func on_enter_die_state(): velocity = Vector2(0, -500) $shape.disabled = true # $AnimationPlayer.play("die")