class_name Player extends CharacterBody2D @export_group("Ground Movement") @export var run_acceleration: float @export var max_run_speed: float @export var turn_acceleration: float @export var stopping_force: float @export_group("Air Movement") @export var gravity: float @export var jump_power: float @export var splat_launch_power: float @export var splat_offset: float @export_group("Internal References") @export var state_chart: StateChart @export var graphics: Node2D @export var run_animation: SpritesheetAnimation @onready var start_position: Vector2 = global_position var input_dir: Vector2 = Vector2.ZERO: set(value): input_dir = value.sign() state_chart.set_expression_property(&"input_dir", input_dir) var _last_velocity: Vector2 = Vector2.ZERO var _splat_normal: Vector2 = Vector2.ZERO func _physics_process(delta: float) -> void: _last_velocity = velocity move_and_slide() state_chart.set_expression_property(&"velocity", velocity) if velocity.x != 0.0: graphics.scale.x = signf(velocity.x) state_chart.set_expression_property(&"on_floor", is_on_floor()) input_dir = Input.get_vector(&"move_left", &"move_right", &"move_up", &"move_down") state_chart.send_event(&"tick") func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed(&"jump"): state_chart.send_event(&"jump_pressed") func kill() -> void: state_chart.send_event(&"killed") func launch(impulse: Vector2) -> void: velocity = impulse state_chart.send_event(&"launched") #region Idle func _slow_to_stop(delta: float) -> void: velocity.x = move_toward(velocity.x, 0.0, stopping_force * delta) #endregion #region Running func _apply_run_acceleration(delta: float) -> void: if absf(velocity.x) < max_run_speed: velocity.x += input_dir.x * run_acceleration * delta func _scale_run_animation(delta:float) -> void: run_animation.speed_scale = inverse_lerp(0.0, max_run_speed, absf(velocity.x)) #endregion #region Turning func _apply_turn_acceleration(delta: float) -> void: velocity.x += input_dir.x * turn_acceleration * delta #endregion #region Falling func _apply_gravity(delta: float) -> void: velocity.y += gravity * delta #endregion #region Jumping func _start_jump() -> void: velocity.y = -jump_power #endregion #region Missile func _restore_graphics_rotation() -> void: graphics.rotation = 0.0 func _face_towards_velocity(_delta: float) -> void: graphics.rotation = Vector2(graphics.scale.x, 0.0).angle_to(velocity) func _check_for_splat(delta: float) -> void: var col = move_and_collide(_last_velocity * delta, true) if col: velocity = Vector2.ZERO global_position += col.get_travel() _splat_normal = col.get_normal() var angle = col.get_normal().angle() if graphics.scale.x > 0.0: angle += PI graphics.set_deferred(&"rotation", angle) state_chart.send_event(&"splatted") #endregion #region Splatting func _do_splat_launch() -> void: var dir = input_dir.project(_splat_normal.orthogonal()).normalized() if dir == Vector2.ZERO: dir = _splat_normal dir = dir.rotated(dir.angle_to(_splat_normal) * 0.5) launch(dir * splat_launch_power) #endregion #region Death func _reset_position() -> void: global_position = start_position velocity = Vector2.ZERO graphics.scale.x = 1.0 graphics.rotation = 0.0 #endregion