class_name Character extends Node2D const BASE_SPEED := 50.0 const VERTICAL_DISTANCE := 24 signal speed_changed var speed_modifier := 1.0: set(value): speed_modifier = value speed_changed.emit() enum Type { Rock, Paper, Scissors, } signal type_change_finished signal type_changed @export var current_type: Type = Type.Rock: set(value): current_type = value type_changed.emit() enum Direction { None, Left, Right, } enum State { Idle, Walk, Jump, Fall, Transform, } var current_state: State = State.Idle var vertical_tween: Tween @onready var sprite: AnimatedSprite2D = $AnimatedSprite2D func _ready() -> void: speed_changed.connect(func(): $AnimatedSprite2D.speed_scale = speed_modifier ) type_changed.connect(func(): if current_state == State.Jump: $AnimatedSprite2D.play("%s_jump" % get_type_name()) elif current_state == State.Fall: $AnimatedSprite2D.play("%s_fall" % get_type_name()) ) func get_type_name(type: Type = current_type): match type: Type.Rock: return "rock" Type.Paper: return "paper" Type.Scissors: return "scissors" func idle(): current_state = State.Idle $AnimatedSprite2D.play("%s_idle" % get_type_name()) func walk(direction: Direction): current_state = State.Walk $AnimatedSprite2D.flip_h = direction == Direction.Left $AnimatedSprite2D.play("%s_walk" % get_type_name()) if is_on_wall(direction): return var direction_vector: Vector2 match direction: Direction.Left: direction_vector = Vector2.LEFT Direction.Right: direction_vector = Vector2.RIGHT position.x = lerp( position.x, position.x + (BASE_SPEED * speed_modifier * direction_vector.x), get_process_delta_time() ) func jump(): if is_below_ceiling(): return current_state = State.Jump $AnimatedSprite2D.play("%s_jump" % get_type_name()) vertical_tween = get_tree().create_tween() vertical_tween.tween_property( self, "position", position - Vector2(0, VERTICAL_DISTANCE), min(1.0 / speed_modifier, 1.0) ) await vertical_tween.finished if not is_on_floor(): fall() else: current_state = State.Idle func fall(): current_state = State.Fall $AnimatedSprite2D.play("%s_fall" % get_type_name()) vertical_tween = get_tree().create_tween() vertical_tween.tween_property( self, "position", position + Vector2(0, VERTICAL_DISTANCE), min(1.0 / speed_modifier, 1.0) ) await vertical_tween.finished if not is_on_floor(): fall() else: current_state = State.Idle func is_on_floor() -> bool: $RayDownLeft.force_raycast_update() $RayDownRight.force_raycast_update() return $RayDownLeft.is_colliding() or $RayDownRight.is_colliding() func _is_on_ledge() -> bool: var is_on := false if $RayDownLeft.is_colliding(): is_on = is_on or $RayDownLeft.get_collider().is_ledge($RayDownLeft.get_collision_point()) if $RayDownRight.is_colliding(): is_on = is_on or $RayDownRight.get_collider().is_ledge($RayDownRight.get_collision_point()) return is_on func is_on_solid_floor() -> bool: var is_on := false if $RayDownLeft.is_colliding(): is_on = is_on or $RayDownLeft.get_collider().is_wall($RayDownLeft.get_collision_point()) if $RayDownRight.is_colliding(): is_on = is_on or $RayDownRight.get_collider().is_wall($RayDownRight.get_collision_point()) return is_on func is_below_ceiling() -> bool: return $RayUpLeft.is_colliding() or $RayUpRight.is_colliding() func is_on_wall(direction: Direction) -> bool: var stage: Stage = get_tree().current_scene match direction: Direction.None: return $RayLeft.is_colliding() or $RayRight.is_colliding() Direction.Left: var left = position.x - (get_sprite_size().x / 2) return $RayLeft.is_colliding() or left <= stage.get_world_boundaries()[Vector2.LEFT] Direction.Right: var right = position.x + (get_sprite_size().x / 2) return $RayRight.is_colliding() or right >= stage.get_world_boundaries()[Vector2.RIGHT] return false func change_type_random(): var types = Type.values() types.erase(current_type) current_type = types.pick_random() func animate_type_change(): var previous_state := current_state current_state = State.Transform var tween_was_running := false if vertical_tween and vertical_tween.is_running(): vertical_tween.pause() tween_was_running = true $AnimatedSprite2D.play("transform") await $AnimatedSprite2D.animation_finished current_state = previous_state type_change_finished.emit() if tween_was_running: vertical_tween.play() func get_sprite_size() -> Vector2: var sprite_frames: SpriteFrames = $AnimatedSprite2D.sprite_frames var texture := sprite_frames.get_frame_texture($AnimatedSprite2D.animation, $AnimatedSprite2D.frame) return texture.get_size() - Vector2(4, 0)