diff --git a/scenes/actors/player.gd b/scenes/actors/player.gd index 59561ed..3b13be1 100644 --- a/scenes/actors/player.gd +++ b/scenes/actors/player.gd @@ -17,6 +17,37 @@ enum State { 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 @@ -24,16 +55,18 @@ var current_state: State = State.IDLE var is_attacking: bool = false var combo_step: int = 0 var combo_timer: float = 0.0 - -@onready var _sprite: AnimatedSprite2D = $AnimatedSprite2D +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 = -300.0 -@export var gravity_scale: float = 1.0 +@export var jump_velocity: float = -350.0 @export_group("Combat") @export var attack_cooldown: float = 0.4 @@ -43,25 +76,46 @@ var combo_timer: 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") * gravity_scale * delta + 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 @@ -74,8 +128,21 @@ func _apply_movement(delta: float) -> void: 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 not Input.is_action_just_pressed("light_attack"): + 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: @@ -93,7 +160,21 @@ func _update_combo_timer(delta: float) -> void: 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 @@ -104,8 +185,16 @@ func _on_animation_finished() -> void: 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 @@ -119,3 +208,36 @@ func _update_animation() -> void: 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 _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) diff --git a/scenes/actors/player.tscn b/scenes/actors/player.tscn index 56f6af2..4496ab1 100644 --- a/scenes/actors/player.tscn +++ b/scenes/actors/player.tscn @@ -2,6 +2,7 @@ [ext_resource type="Texture2D" uid="uid://d3xfbjj71fkdd" path="res://assets/images/sprites/Player/ATTACK 1.png" id="1_c4rn8"] [ext_resource type="Script" uid="uid://d1uiexft8gvs6" path="res://scenes/actors/player.gd" id="1_nmlhc"] +[ext_resource type="Script" uid="uid://ev7uglfcbl02" path="res://scenes/components/health_component.gd" id="2_dferu"] [ext_resource type="Texture2D" uid="uid://cb6o2osebvrn0" path="res://assets/images/sprites/Player/ATTACK 2.png" id="2_ypltb"] [ext_resource type="Texture2D" uid="uid://c1jj5pxaeywbx" path="res://assets/images/sprites/Player/ATTACK 3.png" id="3_nmlhc"] [ext_resource type="Texture2D" uid="uid://bmfchsitduvs1" path="res://assets/images/sprites/Player/DASH.png" id="4_h5w3c"] @@ -13,7 +14,9 @@ [ext_resource type="Texture2D" uid="uid://caguehadpvpe1" path="res://assets/images/sprites/Player/IDLE.png" id="10_afsss"] [ext_resource type="Texture2D" uid="uid://h8gbvo170igh" path="res://assets/images/sprites/Player/JUMP.png" id="11_6a6mx"] [ext_resource type="Texture2D" uid="uid://dy4dgu0my3u34" path="res://assets/images/sprites/Player/RUN.png" id="12_j3wsw"] -[ext_resource type="Texture2D" uid="uid://06lbqwhfj4nq" path="res://assets/images/sprites/Enemies/Demon-Samurai/HURT.png" id="13_ypltb"] +[ext_resource type="Script" uid="uid://dw1cot7pm0rp6" path="res://scenes/components/hit_box.gd" id="14_hitbox"] +[ext_resource type="Texture2D" uid="uid://b2o42nnemli5j" path="res://assets/images/sprites/Player/THROW.png" id="15_dferu"] +[ext_resource type="Script" uid="uid://cxsbkcxpnu3t6" path="res://scenes/components/hurt_box.gd" id="16_0878f"] [sub_resource type="AtlasTexture" id="AtlasTexture_ds2sr"] atlas = ExtResource("1_c4rn8") @@ -363,21 +366,33 @@ region = Rect2(636, 0, 106, 84) atlas = ExtResource("12_j3wsw") region = Rect2(742, 0, 106, 84) -[sub_resource type="AtlasTexture" id="AtlasTexture_nmlhc"] -atlas = ExtResource("13_ypltb") -region = Rect2(0, 0, 128, 108) +[sub_resource type="AtlasTexture" id="AtlasTexture_t62v4"] +atlas = ExtResource("15_dferu") +region = Rect2(0, 0, 106, 84) -[sub_resource type="AtlasTexture" id="AtlasTexture_h5w3c"] -atlas = ExtResource("13_ypltb") -region = Rect2(128, 0, 128, 108) +[sub_resource type="AtlasTexture" id="AtlasTexture_uqfkb"] +atlas = ExtResource("15_dferu") +region = Rect2(106, 0, 106, 84) -[sub_resource type="AtlasTexture" id="AtlasTexture_0878f"] -atlas = ExtResource("13_ypltb") -region = Rect2(256, 0, 128, 108) +[sub_resource type="AtlasTexture" id="AtlasTexture_642so"] +atlas = ExtResource("15_dferu") +region = Rect2(212, 0, 106, 84) -[sub_resource type="AtlasTexture" id="AtlasTexture_dferu"] -atlas = ExtResource("13_ypltb") -region = Rect2(384, 0, 128, 108) +[sub_resource type="AtlasTexture" id="AtlasTexture_afsss"] +atlas = ExtResource("15_dferu") +region = Rect2(318, 0, 106, 84) + +[sub_resource type="AtlasTexture" id="AtlasTexture_6a6mx"] +atlas = ExtResource("15_dferu") +region = Rect2(424, 0, 106, 84) + +[sub_resource type="AtlasTexture" id="AtlasTexture_j3wsw"] +atlas = ExtResource("15_dferu") +region = Rect2(530, 0, 106, 84) + +[sub_resource type="AtlasTexture" id="AtlasTexture_bahrw"] +atlas = ExtResource("15_dferu") +region = Rect2(636, 0, 106, 84) [sub_resource type="SpriteFrames" id="SpriteFrames_uvqlf"] animations = [{ @@ -463,7 +478,7 @@ animations = [{ "duration": 1.0, "texture": SubResource("AtlasTexture_awnt6") }], -"loop": 1, +"loop": 0, "name": &"dash", "speed": 10.0 }, { @@ -495,7 +510,7 @@ animations = [{ "duration": 1.0, "texture": SubResource("AtlasTexture_t81fx") }], -"loop": 1, +"loop": 0, "name": &"dash_attack", "speed": 12.0 }, { @@ -530,7 +545,7 @@ animations = [{ "duration": 1.0, "texture": SubResource("AtlasTexture_i8bcl") }], -"loop": 1, +"loop": 0, "name": &"death", "speed": 7.0 }, { @@ -553,7 +568,7 @@ animations = [{ "duration": 1.0, "texture": SubResource("AtlasTexture_yi68q") }], -"loop": 1, +"loop": 0, "name": &"defend", "speed": 10.0 }, { @@ -591,7 +606,7 @@ animations = [{ "duration": 1.0, "texture": SubResource("AtlasTexture_k7ew7") }], -"loop": 1, +"loop": 0, "name": &"heavy_attack", "speed": 10.0 }, { @@ -608,7 +623,7 @@ animations = [{ "duration": 1.0, "texture": SubResource("AtlasTexture_0d35d") }], -"loop": 1, +"loop": 0, "name": &"hurt", "speed": 10.0 }, { @@ -704,20 +719,29 @@ animations = [{ }, { "frames": [{ "duration": 1.0, -"texture": SubResource("AtlasTexture_nmlhc") +"texture": SubResource("AtlasTexture_t62v4") }, { "duration": 1.0, -"texture": SubResource("AtlasTexture_h5w3c") +"texture": SubResource("AtlasTexture_uqfkb") }, { "duration": 1.0, -"texture": SubResource("AtlasTexture_0878f") +"texture": SubResource("AtlasTexture_642so") }, { "duration": 1.0, -"texture": SubResource("AtlasTexture_dferu") +"texture": SubResource("AtlasTexture_afsss") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_6a6mx") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_j3wsw") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_bahrw") }], "loop": 1, -"name": &"test", -"speed": 5.0 +"name": &"throw", +"speed": 10.0 }] [sub_resource type="CapsuleShape2D" id="CapsuleShape2D_gy2im"] @@ -725,37 +749,55 @@ radius = 7.0 height = 34.0 [sub_resource type="RectangleShape2D" id="RectangleShape2D_uvqlf"] -size = Vector2(29, 40) +size = Vector2(28.5, 41) [sub_resource type="CapsuleShape2D" id="CapsuleShape2D_uvqlf"] -radius = 6.0 +radius = 13.0 +height = 36.0 [node name="Player" type="CharacterBody2D" unique_id=1267310378] +collision_layer = 16 +collision_mask = 9 script = ExtResource("1_nmlhc") +[node name="HealthComponent" type="Node" parent="." unique_id=536498056] +script = ExtResource("2_dferu") +max_health = 12 + [node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="." unique_id=752538207] position = Vector2(0, -33) sprite_frames = SubResource("SpriteFrames_uvqlf") -animation = &"jump" +animation = &"throw" autoplay = "idle" +frame_progress = 0.9020072 [node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=807357149] +visible = false position = Vector2(0, -17) shape = SubResource("CapsuleShape2D_gy2im") [node name="HitBox" type="Area2D" parent="." unique_id=510680537] +visible = false +collision_layer = 2 +collision_mask = 0 +script = ExtResource("14_hitbox") [node name="CollisionShape2D" type="CollisionShape2D" parent="HitBox" unique_id=52536804] -position = Vector2(21.5, -20) +position = Vector2(25.75, -20.5) shape = SubResource("RectangleShape2D_uvqlf") [node name="HurtBox" type="Area2D" parent="." unique_id=2064020607] +visible = false +collision_layer = 4 +collision_mask = 2 +script = ExtResource("16_0878f") [node name="CollisionShape2D" type="CollisionShape2D" parent="HurtBox" unique_id=424603672] -position = Vector2(2, -18) +position = Vector2(0, -18) shape = SubResource("CapsuleShape2D_uvqlf") debug_color = Color(0.9843137, 0, 0, 0.41960785) [node name="Camera2D" type="Camera2D" parent="." unique_id=2010864644] +offset = Vector2(0, -40) zoom = Vector2(0.8, 0.8) position_smoothing_enabled = true