mirror of
https://github.com/kodzukye/omens-ascent.git
synced 2026-08-10 13:23:10 +00:00
247 lines
6.3 KiB
GDScript
247 lines
6.3 KiB
GDScript
class_name Player
|
|
extends CharacterBody2D
|
|
|
|
# Player States
|
|
enum State {
|
|
IDLE,
|
|
RUN,
|
|
JUMP,
|
|
ATTACK_1,
|
|
ATTACK_2,
|
|
ATTACK_3,
|
|
HEAVY_ATTACK, # A rajouter ?
|
|
DASH,
|
|
DASH_ATTACK, # A rajouter
|
|
DEFEND, # A rajouter
|
|
HURT,
|
|
DEATH,
|
|
}
|
|
|
|
# Player Nodes Variables
|
|
@onready var _sprite: AnimatedSprite2D = $AnimatedSprite2D
|
|
@onready var _hitbox: HitBox = $HitBox
|
|
@onready var _hitbox_collision: CollisionShape2D = $HitBox/CollisionShape2D
|
|
@onready var _health: HealthComponent = $HealthComponent
|
|
@onready var _hurtbox: HurtBox = $HurtBox
|
|
|
|
# Collision masks (World = 1, EnemyBody = 8)
|
|
const _DEFAULT_COLLISION_MASK: int = 1 | 8
|
|
const _DASH_COLLISION_MASK: int = 1
|
|
|
|
const _DEATH_SCREEN_PATH: String = "res://scenes/components/death_screen.tscn"
|
|
|
|
# Damage dealt per attack state
|
|
const _ATTACK_DAMAGE: Dictionary = {
|
|
State.ATTACK_1: 3,
|
|
State.ATTACK_2: 3,
|
|
State.ATTACK_3: 5,
|
|
State.HEAVY_ATTACK: 8,
|
|
State.DASH_ATTACK: 2,
|
|
}
|
|
|
|
# Hitbox-active frame range per attack state
|
|
const _ATTACK_HIT_FRAMES: Dictionary = {
|
|
State.ATTACK_1: Vector2i(2, 4),
|
|
State.ATTACK_2: Vector2i(2, 4),
|
|
State.ATTACK_3: Vector2i(2, 4),
|
|
State.DASH_ATTACK: Vector2i(4, 6),
|
|
State.HEAVY_ATTACK: Vector2i(6, 8),
|
|
}
|
|
|
|
# Player Runtime Variables
|
|
var direction: float = 0.0
|
|
var facing_direction: float = 1.0
|
|
var current_state: State = State.IDLE
|
|
var is_attacking: bool = false
|
|
var combo_step: int = 0
|
|
var combo_timer: float = 0.0
|
|
var is_dashing: bool = false
|
|
var dash_timer: float = 0.0
|
|
var dash_cooldown_timer: float = 0.0
|
|
var is_hurt: bool = false
|
|
var is_dead: bool = false
|
|
|
|
# Player Exported Variables
|
|
@export_group("Movement")
|
|
@export var move_speed: float = 200.0
|
|
|
|
@export_group("Jump")
|
|
@export var jump_velocity: float = -350.0
|
|
|
|
@export_group("Combat")
|
|
@export var attack_cooldown: float = 0.4
|
|
@export var heavy_attack_cooldown: float = 0.6
|
|
@export var combo_window: float = 0.6
|
|
@export var attack_move_speed: float = 0.0
|
|
|
|
@export_group("Dash")
|
|
@export var dash_speed: float = 500.0
|
|
@export var dash_duration: float = 0.2
|
|
@export var dash_cooldown: float = 0.8
|
|
|
|
func _ready() -> void:
|
|
add_to_group("player")
|
|
_sprite.animation_finished.connect(_on_animation_finished)
|
|
_health.health_changed.connect(_on_health_changed)
|
|
_health.died.connect(_on_died)
|
|
_hurtbox.hit_received.connect(_on_hurtbox_hit_received)
|
|
if EventBus.has_checkpoint:
|
|
global_position = EventBus.last_checkpoint_position
|
|
_on_health_changed.call_deferred(_health.current_health, _health.max_health)
|
|
|
|
func _physics_process(delta: float) -> void:
|
|
_apply_gravity(delta)
|
|
_process_dash_input()
|
|
_apply_movement(delta)
|
|
_process_attack_input()
|
|
_update_combo_timer(delta)
|
|
_update_dash_timers(delta)
|
|
_update_state()
|
|
_update_animation()
|
|
_update_hitbox()
|
|
move_and_slide()
|
|
|
|
func _apply_gravity(delta: float) -> void:
|
|
if is_dashing:
|
|
return
|
|
if not is_on_floor():
|
|
velocity.y += ProjectSettings.get_setting("physics/2d/default_gravity") * delta
|
|
|
|
func _apply_movement(delta: float) -> void:
|
|
if is_dead or is_hurt:
|
|
velocity.x = 0.0
|
|
return
|
|
|
|
if is_dashing:
|
|
velocity.x = facing_direction * dash_speed
|
|
return
|
|
|
|
if is_attacking:
|
|
velocity.x = facing_direction * attack_move_speed
|
|
return
|
|
|
|
direction = Input.get_axis("move_left", "move_right")
|
|
velocity.x = direction * move_speed
|
|
if direction != 0.0:
|
|
facing_direction = direction
|
|
|
|
if is_on_floor() and Input.is_action_just_pressed("jump"):
|
|
velocity.y = jump_velocity
|
|
|
|
func _process_dash_input() -> void:
|
|
if is_dashing or is_attacking or is_hurt or is_dead or dash_cooldown_timer > 0.0:
|
|
return
|
|
if not Input.is_action_just_pressed("dash"):
|
|
return
|
|
|
|
is_dashing = true
|
|
dash_timer = dash_duration
|
|
dash_cooldown_timer = dash_cooldown
|
|
velocity.y = 0.0
|
|
current_state = State.DASH
|
|
collision_mask = _DASH_COLLISION_MASK
|
|
|
|
func _process_attack_input() -> void:
|
|
if is_attacking or is_dashing or is_hurt or is_dead or not Input.is_action_just_pressed("light_attack"):
|
|
return
|
|
|
|
if combo_step > 0 and combo_timer > 0.0:
|
|
combo_step += 1
|
|
else:
|
|
combo_step = 1
|
|
|
|
is_attacking = true
|
|
combo_timer = 0.0
|
|
current_state = State.values()[State.ATTACK_1 + combo_step - 1]
|
|
|
|
func _update_combo_timer(delta: float) -> void:
|
|
if combo_timer > 0.0:
|
|
combo_timer -= delta
|
|
if combo_timer <= 0.0:
|
|
combo_step = 0
|
|
|
|
func _update_dash_timers(delta: float) -> void:
|
|
if is_dashing:
|
|
dash_timer -= delta
|
|
if dash_timer <= 0.0:
|
|
is_dashing = false
|
|
collision_mask = _DEFAULT_COLLISION_MASK
|
|
|
|
if dash_cooldown_timer > 0.0:
|
|
dash_cooldown_timer -= delta
|
|
|
|
func _on_animation_finished() -> void:
|
|
if current_state == State.HURT:
|
|
is_hurt = false
|
|
return
|
|
|
|
if not is_attacking:
|
|
return
|
|
|
|
is_attacking = false
|
|
if current_state == State.ATTACK_3:
|
|
combo_step = 0
|
|
else:
|
|
combo_timer = combo_window
|
|
|
|
func _update_state() -> void:
|
|
if is_dead:
|
|
return
|
|
if is_hurt:
|
|
current_state = State.HURT
|
|
return
|
|
if is_attacking:
|
|
return
|
|
if is_dashing:
|
|
current_state = State.DASH
|
|
return
|
|
|
|
if not is_on_floor():
|
|
current_state = State.JUMP
|
|
elif direction != 0.0:
|
|
current_state = State.RUN
|
|
else:
|
|
current_state = State.IDLE
|
|
|
|
func _update_animation() -> void:
|
|
var animation_name: String = State.keys()[current_state].to_lower()
|
|
if _sprite.animation != animation_name:
|
|
_sprite.play(animation_name)
|
|
_sprite.flip_h = facing_direction < 0.0
|
|
|
|
func _update_hitbox() -> void:
|
|
_hitbox.scale.x = facing_direction
|
|
_hitbox.damage = _ATTACK_DAMAGE.get(current_state, 0)
|
|
|
|
var hit_frames: Vector2i = _ATTACK_HIT_FRAMES.get(current_state, Vector2i(-1, -1))
|
|
var is_active_hit_frame: bool = _sprite.frame >= hit_frames.x and _sprite.frame <= hit_frames.y
|
|
_hitbox_collision.disabled = not (is_attacking and is_active_hit_frame)
|
|
|
|
func kill() -> void:
|
|
_health.kill()
|
|
|
|
func _on_hurtbox_hit_received(hitbox: HitBox) -> void:
|
|
_health.take_damage(hitbox.damage)
|
|
|
|
is_attacking = false
|
|
if is_dashing:
|
|
is_dashing = false
|
|
collision_mask = _DEFAULT_COLLISION_MASK
|
|
is_hurt = true
|
|
current_state = State.HURT
|
|
|
|
func _on_health_changed(current_health: int, max_health: int) -> void:
|
|
EventBus.player_health_changed.emit(current_health, max_health)
|
|
|
|
func _on_died() -> void:
|
|
EventBus.player_died.emit()
|
|
is_dead = true
|
|
_hurtbox.set_deferred("monitoring", false)
|
|
_hitbox_collision.set_deferred("disabled", true)
|
|
_switch_to_death_screen()
|
|
|
|
func _switch_to_death_screen() -> void:
|
|
EventBus.last_level_path = get_tree().current_scene.scene_file_path
|
|
EventBus.last_death_facing_direction = facing_direction
|
|
SceneManager.call_deferred("change_scene", _DEATH_SCREEN_PATH)
|