class_name Player extends CharacterBody2D ## Character controller. ## TODO: fix bullet cooldown timer const SPEED = 200.0 const JUMP_VELOCITY = -500.0 enum FaceDirection{LEFT, RIGHT} enum State{IDLE, WALK, JUMP, FALLING, SHOOT_STILL, SHOOT_RUN} @export var BUMP_POWER := 50 @export var SHOVE_POWER := 300 @export var ACCELERATION := 10 ## Amount of "slide-y-ness" in side-to-side-movement @export var HARD_GRAVITY := 2 var current_state: State = State.IDLE var facing : FaceDirection = FaceDirection.RIGHT var direction : float = 0.0 var push_target : RigidBody2D var push_enabled : bool = false var jump_buffer_timer : Timer var shoot_cooldown_timer : Timer @onready var right_cast: RayCast2D = $RightCast @onready var left_cast: RayCast2D = $LeftCast @onready var right_spawn: Marker2D = $RightSpawn @onready var left_spawn: Marker2D = $LeftSpawn @onready var player_sprite: AnimatedSprite2D = $PlayerSprite func _ready() -> void: jump_buffer_timer = Timer.new() add_child(jump_buffer_timer) shoot_cooldown_timer = Timer.new() shoot_cooldown_timer.one_shot = true add_child(shoot_cooldown_timer) func _physics_process(delta: float) -> void: handle_input() handle_movement(delta) move_and_slide() handle_collisions() update_state() update_animation() func handle_input() -> void: # Handle jumping. if Input.is_action_just_pressed("jump"): jump_buffer_timer.start(0.1) if is_on_floor() and jump_buffer_timer.time_left > 0: velocity.y = JUMP_VELOCITY current_state = State.JUMP jump_buffer_timer.stop() # Set direction. direction = Input.get_axis("move_left", "move_right") if direction < 0: facing = FaceDirection.LEFT if direction > 0: facing = FaceDirection.RIGHT # Handle shoving. if Input.is_action_just_pressed("shove") and push_enabled == true: var shove_direction : int match facing: FaceDirection.RIGHT: shove_direction = 1 FaceDirection.LEFT: shove_direction = -1 push_target.apply_central_impulse(Vector2(shove_direction, 0) * SHOVE_POWER) # Handle shooting if Input.is_action_just_pressed("shoot"): if shoot_cooldown_timer.time_left == 0: if velocity.x: current_state = State.SHOOT_RUN else: current_state = State.SHOOT_STILL print("pew-pew") match facing: FaceDirection.RIGHT: %SceneManager.make_bullet(right_spawn.global_transform, 700) print("shoot right") FaceDirection.LEFT: %SceneManager.make_bullet(left_spawn.global_transform, -700) print("shoot left") shoot_cooldown_timer.start(0.2) else: print("can't shoot right now because there are %s seconds left in the cooldown timer" % shoot_cooldown_timer.time_left) # Handle throwing grenades if Input.is_action_just_pressed("throw"): print("grenade!") match facing: FaceDirection.RIGHT: %SceneManager.make_grenade(right_spawn.global_transform, 1.0) print("throwing right") FaceDirection.LEFT: %SceneManager.make_grenade(left_spawn.global_transform, -1.0) print("throwing left") func handle_movement(delta) -> void: # Left-right movement. Use acceleration for smoothing if direction: velocity.x = move_toward(velocity.x, SPEED * direction, ACCELERATION) else: velocity.x = move_toward(velocity.x, 0, ACCELERATION) if not is_on_floor(): # Add gravity. if current_state == State.JUMP: # Character is jumping; apply normal gravity velocity += get_gravity() * delta if velocity.y > 0: current_state = State.FALLING else: # Character falling; apply hard gravity current_state = State.FALLING velocity += get_gravity() * HARD_GRAVITY * delta func handle_collisions() -> void: for i in get_slide_collision_count(): var c = get_slide_collision(i) if c.get_collider() is RigidBody2D and c.get_collider() is not Grenade: c.get_collider().apply_central_impulse(-c.get_normal() * BUMP_POWER) if right_cast.is_colliding() and facing == FaceDirection.RIGHT: var collider = right_cast.get_collider() # check if this is OK if collider is Node and collider is RigidBody2D and collider.is_in_group("pushable"): push_target = collider push_enabled = true if left_cast.is_colliding() and facing == FaceDirection.LEFT: var collider = left_cast.get_collider() if collider is Node and collider is RigidBody2D and collider.is_in_group("pushable"): push_target = collider push_enabled = true if not right_cast.is_colliding() and not left_cast.is_colliding(): push_enabled = false func update_state() -> void: match current_state: # If player is moving left or right State.IDLE when velocity.x !=0: current_state = State.WALK # If player stops walking, or starts falling State.WALK: # If not moving left or right if velocity.x == 0: current_state = State.IDLE # If falling if not is_on_floor() and velocity.y > 0: current_state = State.FALLING # When jump peaks, we start to fall State.JUMP when velocity.y > 0: current_state = State.FALLING # Player lands, either still or moving State.FALLING when is_on_floor(): if velocity.x == 0: current_state = State.IDLE else: current_state = State.WALK # Player shooting State.SHOOT_STILL: await player_sprite.animation_finished current_state = State.IDLE # Player shooting while moving State.SHOOT_RUN: await player_sprite.animation_finished current_state = State.WALK func update_animation() -> void: match facing: FaceDirection.LEFT: player_sprite.flip_h = true FaceDirection.RIGHT: player_sprite.flip_h = false match current_state: State.IDLE: player_sprite.play("idle") State.WALK: player_sprite.play("run") State.JUMP: player_sprite.play("jump") State.FALLING: player_sprite.play("fall") State.SHOOT_STILL: player_sprite.play("shoot_still") State.SHOOT_RUN: player_sprite.play("shoot_run")