diff --git a/.gitignore b/.gitignore index 36c968c..5e2bba6 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ mono_crash.*.json repomix-output.txt .idea + +assets/packs/ diff --git a/addons/ability_editor/ability_data_inspector.gd b/addons/ability_editor/ability_data_inspector.gd deleted file mode 100644 index 11093a5..0000000 --- a/addons/ability_editor/ability_data_inspector.gd +++ /dev/null @@ -1,42 +0,0 @@ -# ability_data_inspector.gd -@tool -extends EditorInspectorPlugin - -func _can_handle(object): - return object is AbilityData - -func _parse_property(object: Object, type: Variant.Type, name: String, - hint_type: PropertyHint, hint_string: String, - usage_flags: int, wide: bool) -> bool: - # Add preview after all properties - if name == "vfx_scene": # Last property - var preview = PreviewControl.new(object) - add_custom_control(preview) - return false # Continue normal property handling - -class PreviewControl extends VBoxContainer: - var ability_data: AbilityData - var preview_label: Label - - func _init(object: AbilityData): - ability_data = object - custom_minimum_size.y = 100 - - var title = Label.new() - title.text = "Preview" - title.add_theme_font_size_override("font_size", 16) - add_child(title) - - var panel = PanelContainer.new() - add_child(panel) - - preview_label = Label.new() - preview_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART - panel.add_child(preview_label) - - # Connect to property changes - ability_data.changed.connect(_update_preview) - _update_preview() - - func _update_preview(): - preview_label.text = ability_data.get_preview_text() diff --git a/addons/ability_editor/ability_editor_plugin.gd b/addons/ability_editor/ability_editor_plugin.gd deleted file mode 100644 index a2ef1c2..0000000 --- a/addons/ability_editor/ability_editor_plugin.gd +++ /dev/null @@ -1,13 +0,0 @@ -# ability_editor_plugin.gd -@tool -extends EditorPlugin - -var inspector_plugin = null - -func _enter_tree(): - inspector_plugin = preload("ability_data_inspector.gd").new() - add_inspector_plugin(inspector_plugin) - -func _exit_tree(): - if inspector_plugin: - remove_inspector_plugin(inspector_plugin) diff --git a/addons/ability_editor/plugin.cfg b/addons/ability_editor/plugin.cfg deleted file mode 100644 index 128022d..0000000 --- a/addons/ability_editor/plugin.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[plugin] - -name="Ability Editor" -description="Custom editor for AbilityData resources" -author="Pixel Pilgrims" -version="1.0" -script="ability_editor_plugin.gd" diff --git a/assets/audio/sfx/03_Fighter_Damage_1.wav b/assets/audio/sfx/03_Fighter_Damage_1.wav new file mode 100644 index 0000000..860615a Binary files /dev/null and b/assets/audio/sfx/03_Fighter_Damage_1.wav differ diff --git a/assets/audio/sfx/04_Fighter_Damage_2.wav b/assets/audio/sfx/04_Fighter_Damage_2.wav new file mode 100644 index 0000000..e5930a7 Binary files /dev/null and b/assets/audio/sfx/04_Fighter_Damage_2.wav differ diff --git a/assets/audio/sfx/28_swoosh_01.wav b/assets/audio/sfx/28_swoosh_01.wav new file mode 100644 index 0000000..248066b Binary files /dev/null and b/assets/audio/sfx/28_swoosh_01.wav differ diff --git a/assets/audio/sfx/29_swoosh_02.wav b/assets/audio/sfx/29_swoosh_02.wav new file mode 100644 index 0000000..5655c36 Binary files /dev/null and b/assets/audio/sfx/29_swoosh_02.wav differ diff --git a/assets/audio/sfx/30_swoosh_03.wav b/assets/audio/sfx/30_swoosh_03.wav new file mode 100644 index 0000000..2952621 Binary files /dev/null and b/assets/audio/sfx/30_swoosh_03.wav differ diff --git a/assets/audio/sfx/37_Block_01.wav b/assets/audio/sfx/37_Block_01.wav new file mode 100644 index 0000000..361bd35 Binary files /dev/null and b/assets/audio/sfx/37_Block_01.wav differ diff --git a/assets/audio/sfx/40_Block_04.wav b/assets/audio/sfx/40_Block_04.wav new file mode 100644 index 0000000..eb77bf4 Binary files /dev/null and b/assets/audio/sfx/40_Block_04.wav differ diff --git a/assets/audio/ui/007_Hover_07.wav b/assets/audio/ui/007_Hover_07.wav new file mode 100644 index 0000000..19d1b80 Binary files /dev/null and b/assets/audio/ui/007_Hover_07.wav differ diff --git a/assets/audio/ui/052_use_item_02.wav b/assets/audio/ui/052_use_item_02.wav new file mode 100644 index 0000000..89ba090 Binary files /dev/null and b/assets/audio/ui/052_use_item_02.wav differ diff --git a/assets/audio/ui/Click_1.wav b/assets/audio/ui/Click_1.wav new file mode 100644 index 0000000..312441d Binary files /dev/null and b/assets/audio/ui/Click_1.wav differ diff --git a/assets/audio/ui/Hover_1.wav b/assets/audio/ui/Hover_1.wav new file mode 100644 index 0000000..ebe4ab6 Binary files /dev/null and b/assets/audio/ui/Hover_1.wav differ diff --git a/project.godot b/project.godot index 8c5793d..9dcdf8d 100644 --- a/project.godot +++ b/project.godot @@ -22,6 +22,7 @@ AdventureSystem="*res://scripts/systems/adventure_system.gd" BackgroundSystem="*res://scripts/systems/background_system.gd" CutsceneSystem="*res://scripts/systems/cutscene_system.gd" MusicSystem="*res://scripts/systems/music_system.gd" +SoundEffectsSystem="*res://scripts/systems/sound_effects_system.gd" [display] @@ -31,7 +32,7 @@ window/size/resizable=false [editor_plugins] -enabled=PackedStringArray("res://addons/ability_editor/plugin.cfg") +enabled=PackedStringArray() [global_group] diff --git a/resources/audio_bus_layout.tres b/resources/audio_bus_layout.tres new file mode 100644 index 0000000..1d18b47 --- /dev/null +++ b/resources/audio_bus_layout.tres @@ -0,0 +1,15 @@ +[gd_resource type="AudioBusLayout" format=3 uid="uid://b2bkub5s8hm0j"] + +[resource] +bus/1/name = &"Music" +bus/1/solo = false +bus/1/mute = false +bus/1/bypass_fx = false +bus/1/volume_db = -8.0 +bus/1/send = &"Master" +bus/2/name = &"SoundEffects" +bus/2/solo = false +bus/2/mute = false +bus/2/bypass_fx = false +bus/2/volume_db = -5.0 +bus/2/send = &"Master" diff --git a/scenes/adventure_map.tscn b/scenes/adventure_map.tscn index 10ce532..b125a6a 100644 --- a/scenes/adventure_map.tscn +++ b/scenes/adventure_map.tscn @@ -26,6 +26,12 @@ vertical_scroll_mode = 0 layout_mode = 2 size_flags_vertical = 3 +[node name="HealthLabel" type="Label" parent="ScrollContainer/AdventureStages"] +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "100/100" +horizontal_alignment = 1 + [node name="LinesContainer" type="Node2D" parent="ScrollContainer"] z_index = -1 diff --git a/scenes/enemies/enemy.tscn b/scenes/enemies/enemy.tscn index 8cee936..b6dbbd0 100644 --- a/scenes/enemies/enemy.tscn +++ b/scenes/enemies/enemy.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=3 format=3 uid="uid://b3hv5sm4hjouf"] +[gd_scene load_steps=4 format=3 uid="uid://b3hv5sm4hjouf"] [ext_resource type="Script" path="res://scripts/enemy.gd" id="1_wa7o7"] [ext_resource type="PackedScene" uid="uid://c0l75fsf0a0lh" path="res://scenes/ui/health_bar.tscn" id="2_ahs3m"] +[ext_resource type="Script" path="res://scripts/animators/combat_animator.gd" id="3_l6amq"] [node name="Enemy" type="Node2D"] script = ExtResource("1_wa7o7") @@ -11,3 +12,6 @@ offset_left = -50.0 offset_top = 630.0 offset_right = 350.0 offset_bottom = 680.0 + +[node name="CombatAnimator" type="Node" parent="."] +script = ExtResource("3_l6amq") diff --git a/scenes/menus/main_menu.tscn b/scenes/menus/main_menu.tscn index 2519895..871b2f2 100644 --- a/scenes/menus/main_menu.tscn +++ b/scenes/menus/main_menu.tscn @@ -1,6 +1,7 @@ -[gd_scene load_steps=2 format=3 uid="uid://bxru22786wp6n"] +[gd_scene load_steps=3 format=3 uid="uid://bxru22786wp6n"] [ext_resource type="Script" path="res://scripts/scenes/main_menu.gd" id="1_prvtn"] +[ext_resource type="Script" path="res://scripts/ui/audio_button.gd" id="2_l6w84"] [node name="MainMenu" type="CenterContainer"] anchors_preset = 15 @@ -23,21 +24,25 @@ layout_mode = 2 theme_override_font_sizes/font_size = 32 disabled = true text = "Continue Game" +script = ExtResource("2_l6w84") [node name="StartButton" type="Button" parent="CenterContainer/VBoxContainer"] custom_minimum_size = Vector2(400, 100) layout_mode = 2 theme_override_font_sizes/font_size = 32 text = "New Game" +script = ExtResource("2_l6w84") [node name="Options" type="Button" parent="CenterContainer/VBoxContainer"] custom_minimum_size = Vector2(400, 100) layout_mode = 2 theme_override_font_sizes/font_size = 32 text = "Options" +script = ExtResource("2_l6w84") [node name="QuitButton" type="Button" parent="CenterContainer/VBoxContainer"] custom_minimum_size = Vector2(400, 100) layout_mode = 2 theme_override_font_sizes/font_size = 32 text = "Quit" +script = ExtResource("2_l6w84") diff --git a/scenes/menus/pause_menu.tscn b/scenes/menus/pause_menu.tscn new file mode 100644 index 0000000..e7b1477 --- /dev/null +++ b/scenes/menus/pause_menu.tscn @@ -0,0 +1,76 @@ +[gd_scene load_steps=3 format=3 uid="uid://dtnutgtckwipv"] + +[ext_resource type="Script" path="res://scripts/pause_menu.gd" id="1_p7luq"] +[ext_resource type="Script" path="res://scripts/ui/audio_button.gd" id="2_yb0ds"] + +[node name="PauseMenu" type="Control"] +process_mode = 3 +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("1_p7luq") + +[node name="ColorRect" type="ColorRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +color = Color(0, 0, 0, 0.5) + +[node name="CenterContainer" type="CenterContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 + +[node name="PanelContainer" type="PanelContainer" parent="CenterContainer"] +custom_minimum_size = Vector2(400, 200) +layout_mode = 2 +mouse_filter = 2 + +[node name="MarginContainer" type="MarginContainer" parent="CenterContainer/PanelContainer"] +layout_mode = 2 +mouse_filter = 2 +theme_override_constants/margin_left = 16 +theme_override_constants/margin_top = 16 +theme_override_constants/margin_right = 16 +theme_override_constants/margin_bottom = 16 + +[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/PanelContainer/MarginContainer"] +layout_mode = 2 +mouse_filter = 2 +theme_override_constants/separation = 16 + +[node name="Label" type="Label" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 32 +text = "PAUSED" +horizontal_alignment = 1 + +[node name="ResumeButton" type="Button" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(200, 50) +layout_mode = 2 +size_flags_horizontal = 4 +mouse_default_cursor_shape = 2 +theme_override_font_sizes/font_size = 24 +text = "Resume" +script = ExtResource("2_yb0ds") + +[node name="QuitButton" type="Button" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(200, 50) +layout_mode = 2 +size_flags_horizontal = 4 +mouse_default_cursor_shape = 2 +theme_override_font_sizes/font_size = 24 +text = "Quit to Menu" +script = ExtResource("2_yb0ds") diff --git a/scenes/pause_menu.tscn b/scenes/pause_menu.tscn deleted file mode 100644 index 881d1a3..0000000 --- a/scenes/pause_menu.tscn +++ /dev/null @@ -1,69 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://dtnutgtckwipv"] - -[ext_resource type="Script" path="res://scripts/pause_menu.gd" id="1_p7luq"] - -[node name="PauseMenu" type="Control"] -process_mode = 3 -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_p7luq") - -[node name="ColorRect" type="ColorRect" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 1 -color = Color(0, 0, 0, 0.5) - -[node name="PanelContainer" type="PanelContainer" parent="."] -layout_mode = 1 -anchors_preset = 8 -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -offset_left = -100.0 -offset_top = -68.0 -offset_right = 100.0 -offset_bottom = 68.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 1 - -[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"] -layout_mode = 2 -theme_override_constants/margin_left = 16 -theme_override_constants/margin_top = 16 -theme_override_constants/margin_right = 16 -theme_override_constants/margin_bottom = 16 - -[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer"] -layout_mode = 2 -theme_override_constants/separation = 16 - -[node name="Label" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer"] -layout_mode = 2 -theme_override_font_sizes/font_size = 32 -text = "PAUSED" -horizontal_alignment = 1 - -[node name="ResumeButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"] -custom_minimum_size = Vector2(200, 50) -layout_mode = 2 -size_flags_horizontal = 4 -mouse_default_cursor_shape = 2 -text = "Resume" - -[node name="QuitButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"] -custom_minimum_size = Vector2(200, 50) -layout_mode = 2 -size_flags_horizontal = 4 -mouse_default_cursor_shape = 2 -text = "Quit to Menu" diff --git a/scenes/player.tscn b/scenes/player.tscn index c3029f9..f6c4333 100644 --- a/scenes/player.tscn +++ b/scenes/player.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=4 format=3 uid="uid://boov2saejccy7"] +[gd_scene load_steps=5 format=3 uid="uid://boov2saejccy7"] [ext_resource type="Script" path="res://scripts/player.gd" id="1_o86w8"] [ext_resource type="Texture2D" uid="uid://bqjhgi43f43w2" path="res://assets/player/player_sprite.png" id="2_h6yh7"] [ext_resource type="PackedScene" uid="uid://c0l75fsf0a0lh" path="res://scenes/ui/health_bar.tscn" id="3_gufyl"] +[ext_resource type="Script" path="res://scripts/animators/combat_animator.gd" id="4_10708"] [node name="Player" type="Node2D"] script = ExtResource("1_o86w8") @@ -19,3 +20,6 @@ offset_left = -50.0 offset_top = 630.0 offset_right = 350.0 offset_bottom = 680.0 + +[node name="CombatAnimator" type="Node" parent="."] +script = ExtResource("4_10708") diff --git a/scripts/animators/combat_animator.gd b/scripts/animators/combat_animator.gd new file mode 100644 index 0000000..7ee61ae --- /dev/null +++ b/scripts/animators/combat_animator.gd @@ -0,0 +1,123 @@ +extends Node +class_name CombatAnimator + +signal animation_completed + +# Animation constants +const HEALTH_CHANGE_DURATION = 0.4 +const BLOCK_CHANGE_DURATION = 0.3 +const ATTACK_WIND_UP = 0.3 +const ATTACK_FOLLOW_THROUGH = 0.2 +const DAMAGE_FLASH_DURATION = 0.2 +const DAMAGE_SHAKE_DISTANCE = 10.0 +const DEFAULT_TWEEN_TRANS = Tween.TRANS_EXPO +const DEFAULT_TWEEN_EASE = Tween.EASE_OUT + +var is_animating := false +var sprite_original_position: Vector2 +var current_tween: Tween + +@onready var parent = get_parent() +@onready var visual = parent.get_node_or_null("Visual") + +func _ready() -> void: + # Store original position for attack animations + if parent is Node2D: + sprite_original_position = parent.position + +func animate_damage_taken() -> Signal: + if not visual: + animation_completed.emit() + return animation_completed + + if current_tween and current_tween.is_valid(): + current_tween.kill() + + is_animating = true + current_tween = create_tween().set_parallel(true) + + # Flash white + current_tween.tween_property(visual, "modulate", Color.WHITE, DAMAGE_FLASH_DURATION/2) + current_tween.tween_property(visual, "modulate", Color.RED, DAMAGE_FLASH_DURATION/2) + current_tween.chain().tween_property(visual, "modulate", Color.WHITE, DAMAGE_FLASH_DURATION/2) + current_tween.chain().tween_property(visual, "modulate", Color(1,1,1,1), DAMAGE_FLASH_DURATION/2) + + # Shake effect + var original_pos = visual.position + var shake_positions = [ + original_pos + Vector2.RIGHT * DAMAGE_SHAKE_DISTANCE, + original_pos + Vector2.LEFT * DAMAGE_SHAKE_DISTANCE, + original_pos + Vector2.UP * DAMAGE_SHAKE_DISTANCE, + original_pos + Vector2.DOWN * DAMAGE_SHAKE_DISTANCE, + original_pos + ] + + for pos in shake_positions: + current_tween.chain().tween_property(visual, "position", pos, 0.05) + + current_tween.finished.connect(func(): + # Ensure we reset to original state + visual.position = original_pos + visual.modulate = Color(1,1,1,1) + is_animating = false + animation_completed.emit() + ) + + return animation_completed + +func animate_health_change(from: float, to: float, health_bar: HealthBar) -> Signal: + is_animating = true + var tween = create_tween() + tween.tween_method(health_bar.set_health, from, to, HEALTH_CHANGE_DURATION)\ + .set_trans(DEFAULT_TWEEN_TRANS)\ + .set_ease(DEFAULT_TWEEN_EASE) + tween.finished.connect(func(): + is_animating = false + animation_completed.emit() + ) + return animation_completed + +func animate_block_change(from: float, to: float, health_bar: HealthBar) -> Signal: + is_animating = true + var tween = create_tween() + tween.tween_method(health_bar.set_block, from, to, BLOCK_CHANGE_DURATION)\ + .set_trans(DEFAULT_TWEEN_TRANS)\ + .set_ease(DEFAULT_TWEEN_EASE) + tween.finished.connect(func(): + is_animating = false + animation_completed.emit() + ) + return animation_completed + +func animate_attack(target_node: Node2D) -> Signal: + is_animating = true + if not parent is Node2D: + is_animating = false + animation_completed.emit() + return animation_completed + + var direction = (target_node.position - parent.position).normalized() + var attack_offset = direction * 100 # Distance to move forward + + var tween = create_tween() + # Wind up + tween.tween_property(parent, "position", + sprite_original_position - (attack_offset * 0.3), + ATTACK_WIND_UP) + # Attack lunge + tween.tween_property(parent, "position", + sprite_original_position + attack_offset, + ATTACK_WIND_UP) + # Return to position + tween.tween_property(parent, "position", + sprite_original_position, + ATTACK_FOLLOW_THROUGH) + + tween.finished.connect(func(): + is_animating = false + animation_completed.emit() + ) + return animation_completed + +func is_busy() -> bool: + return is_animating diff --git a/scripts/enemy.gd b/scripts/enemy.gd index 13bdb2d..e0d0d25 100644 --- a/scripts/enemy.gd +++ b/scripts/enemy.gd @@ -1,4 +1,3 @@ -# scripts/enemy.gd extends Node2D signal died @@ -9,16 +8,21 @@ signal attack_executed(damage: int, target: Node) signal block_gained(amount: int) @onready var health_bar: HealthBar = $HealthBar +@onready var combat_animator: CombatAnimator = $CombatAnimator var health: int = 30 var max_health: int = 30 var abilities: Array[AbilityData] = [] var current_intent: AbilityData var block: int = 0 +var is_executing_turn := false func take_damage(amount: int) -> void: + var initial_health = health health -= amount - health_bar.set_health(health) + + SoundEffectsSystem.play_sound("combat", "damage_taken", -5.0) + await combat_animator.animate_health_change(initial_health, health, health_bar) damage_taken.emit(amount) print("Enemy took ", amount, " damage") if health <= 0: @@ -27,23 +31,47 @@ func take_damage(amount: int) -> void: queue_free() func take_turn() -> void: + if is_executing_turn: + push_error("Enemy is already executing a turn") + return + + is_executing_turn = true + if current_intent: - execute_intent() + await execute_intent() + + # Add a small delay between intent execution and turn end + await get_tree().create_timer(0.2).timeout + set_next_intent() + is_executing_turn = false turn_ended.emit() func execute_intent() -> void: print("Enemy uses ", current_intent.name) match current_intent.type: AbilityData.AbilityType.ATTACK: - attack_executed.emit(current_intent.value, self) + # Find player node + var player = get_node("/root/Main/Combat/Player") + if player: + # Start attack animation + await combat_animator.animate_attack(player) + # Execute attack + attack_executed.emit(current_intent.value, self) + # Wait for a moment after attack + await get_tree().create_timer(0.2).timeout + AbilityData.AbilityType.BLOCK: - gain_block(current_intent.value) - # Add other ability types as needed + var initial_block = block + block += current_intent.value + await combat_animator.animate_block_change(initial_block, block, health_bar) + block_gained.emit(current_intent.value) + await get_tree().create_timer(0.2).timeout func gain_block(amount: int) -> void: + var initial_block = block block += amount - health_bar.set_block(block) + combat_animator.animate_block_change(initial_block, block, health_bar) block_gained.emit(amount) func select_random_ability() -> AbilityData: diff --git a/scripts/managers/combat_manager.gd b/scripts/managers/combat_manager.gd index 972c59a..a59f220 100644 --- a/scripts/managers/combat_manager.gd +++ b/scripts/managers/combat_manager.gd @@ -1,4 +1,3 @@ -# scripts/managers/combat_manager.gd extends Node signal combat_started @@ -10,7 +9,7 @@ const GAME_OVER_SCENE = preload("res://scenes/game_over.tscn") var is_player_turn: bool = true var turn_count: int = 0 -var enemies_completed_turn: int = 0 +var current_enemy_index: int = -1 @onready var hand_manager: HandManager = $"../HandManager" @onready var enemies_container: Node = $"../EnemiesContainer" @@ -63,6 +62,7 @@ func start_combat() -> void: func start_player_turn() -> void: turn_count += 1 is_player_turn = true + current_enemy_index = -1 turn_started.emit() player.start_turn() hand_manager.draw_cards(5) @@ -75,24 +75,30 @@ func end_player_turn() -> void: is_player_turn = false end_turn_button.disabled = true hand_manager.discard_hand() - - # Reset enemy turn counter and start enemy turns - enemies_completed_turn = 0 - _start_enemy_turns() - -func _start_enemy_turns() -> void: - for enemy in enemies_container.get_children(): - if enemy.has_method("take_turn"): - enemy.take_turn() + start_next_enemy_turn() -func _on_enemy_turn_ended() -> void: - enemies_completed_turn += 1 +func start_next_enemy_turn() -> void: + current_enemy_index += 1 + var enemies = enemies_container.get_children() + + # Skip any enemies that were removed during previous turns + while current_enemy_index < enemies.size() and not is_instance_valid(enemies[current_enemy_index]): + current_enemy_index += 1 - # Only start player turn when all enemies have finished - if enemies_completed_turn >= enemies_container.get_child_count(): + if current_enemy_index >= enemies.size(): + # All enemies have taken their turn start_player_turn() + return + + # Start the current enemy's turn + var current_enemy = enemies[current_enemy_index] + if current_enemy.has_method("take_turn"): + current_enemy.take_turn() + +func _on_enemy_turn_ended() -> void: + start_next_enemy_turn() -func _on_enemy_attack(damage: int, _enemy: Node) -> void: +func _on_enemy_attack(damage: int, enemy: Node) -> void: player.take_damage(damage) func _on_player_energy_changed(new_amount: int, _max_amount: int) -> void: diff --git a/scripts/managers/pause_manager.gd b/scripts/managers/pause_manager.gd index 0abced2..041ffc8 100644 --- a/scripts/managers/pause_manager.gd +++ b/scripts/managers/pause_manager.gd @@ -1,6 +1,6 @@ extends Node -const PAUSE_MENU = preload("res://scenes/pause_menu.tscn") +const PAUSE_MENU = preload("res://scenes/menus/pause_menu.tscn") var pause_menu: Control = null var is_paused: bool = false diff --git a/scripts/pause_menu.gd b/scripts/pause_menu.gd index 9b2aa71..fbac16d 100644 --- a/scripts/pause_menu.gd +++ b/scripts/pause_menu.gd @@ -6,8 +6,8 @@ signal quit_pressed func _ready() -> void: hide() # Connect button signals - var resume_button = $PanelContainer/MarginContainer/VBoxContainer/ResumeButton - var quit_button = $PanelContainer/MarginContainer/VBoxContainer/QuitButton + var resume_button = $CenterContainer/PanelContainer/MarginContainer/VBoxContainer/ResumeButton + var quit_button = $CenterContainer/PanelContainer/MarginContainer/VBoxContainer/QuitButton if not resume_button.pressed.is_connected(_on_resume_pressed): resume_button.pressed.connect(_on_resume_pressed) @@ -22,7 +22,7 @@ func _on_quit_pressed() -> void: func open() -> void: show() - $PanelContainer/MarginContainer/VBoxContainer/ResumeButton.grab_focus() + $CenterContainer/PanelContainer/MarginContainer/VBoxContainer/ResumeButton.grab_focus() func close() -> void: hide() diff --git a/scripts/player.gd b/scripts/player.gd index 11b0971..4a55345 100644 --- a/scripts/player.gd +++ b/scripts/player.gd @@ -1,4 +1,3 @@ -# scripts/player.gd extends Node2D signal died @@ -7,6 +6,7 @@ signal damage_taken(amount: int) signal block_gained(amount: int) @onready var health_bar: HealthBar = $HealthBar +@onready var combat_animator: CombatAnimator = $CombatAnimator @export var health: int = 100 @export var max_health: int = 100 @@ -16,31 +16,39 @@ signal block_gained(amount: int) func _ready() -> void: update_energy_display() - health_bar.setup(max_health, true) # 100 max HP, show numbers + health_bar.setup(max_health, true) func take_damage(amount: int) -> void: + SoundEffectsSystem.play_sound("combat", "damage_taken", -5.0) + # First reduce block + var initial_block = block + var initial_health = health + var remaining_damage = amount + if block > 0: var blocked = min(block, amount) - amount -= blocked + remaining_damage -= blocked block -= blocked - health_bar.set_block(block) + if blocked > 0: + await combat_animator.animate_block_change(initial_block, block, health_bar) print("Player blocked ", blocked, " damage.") # Then reduce health - if amount > 0: - health -= amount - health_bar.set_health(health) - print("Player took ", amount, " damage. Health: ", health) + if remaining_damage > 0: + health -= remaining_damage + await combat_animator.animate_health_change(initial_health, health, health_bar) - damage_taken.emit(amount) + damage_taken.emit(amount) if health <= 0: died.emit() func gain_block(amount: int) -> void: + SoundEffectsSystem.play_sound("combat", "block", -5.0) + var initial_block = block block += amount - health_bar.set_block(block) + await combat_animator.animate_block_change(initial_block, block, health_bar) block_gained.emit(amount) func update_energy_display() -> void: @@ -48,8 +56,10 @@ func update_energy_display() -> void: func start_turn() -> void: current_energy = max_energy + var initial_block = block block = 0 - health_bar.set_block(block) + if initial_block > 0: + await combat_animator.animate_block_change(initial_block, 0, health_bar) update_energy_display() func spend_energy(amount: int) -> void: diff --git a/scripts/scenes/card.gd b/scripts/scenes/card.gd index 9c248d7..8ae522d 100644 --- a/scripts/scenes/card.gd +++ b/scripts/scenes/card.gd @@ -92,11 +92,15 @@ func play_card() -> void: var player = combat_scene.get_node("Player") if player.can_spend_energy(card_data.energy_cost): + # Play card sound + SoundEffectsSystem.play_sound("ui", "card_play", -5.0) print("Player played ", card_data.name) player.spend_energy(card_data.energy_cost) execute_effect() card_played.emit(self) queue_free() + else: + SoundEffectsSystem.play_sound("ui", "card_deny", -5.0) func execute_effect() -> void: var combat_scene = get_node("/root/Main/Combat") @@ -145,3 +149,6 @@ func _on_decode_option_selected(id: int) -> void: 1: decoder.decode_aspect(self, "type") 2: decoder.decode_aspect(self, "value") 3: decoder.decode_aspect(self, "description") + +func _on_mouse_entered() -> void: + SoundEffectsSystem.play_sound("ui", "card_hover", -15.0) diff --git a/scripts/systems/sound_effects_system.gd b/scripts/systems/sound_effects_system.gd new file mode 100644 index 0000000..81a0484 --- /dev/null +++ b/scripts/systems/sound_effects_system.gd @@ -0,0 +1,77 @@ +extends Node + +# Preloaded sound effect dictionaries +var combat_sfx = { + "attack": [ + preload("res://assets/audio/sfx/28_swoosh_01.wav"), + preload("res://assets/audio/sfx/29_swoosh_02.wav"), + preload("res://assets/audio/sfx/30_swoosh_03.wav") + ], + "block": [ + preload("res://assets/audio/sfx/37_Block_01.wav"), + preload("res://assets/audio/sfx/40_Block_04.wav") + ], + "damage_taken": [ + preload("res://assets/audio/sfx/03_Fighter_Damage_1.wav"), + preload("res://assets/audio/sfx/04_Fighter_Damage_2.wav") + ] +} + +var ui_sfx = { + "button_hover": [ + preload("res://assets/audio/ui/007_Hover_07.wav"), + ], + "button_click": [ + preload("res://assets/audio/ui/Click_1.wav"), + ], + "card_hover": [ + preload("res://assets/audio/ui/Hover_1.wav"), + ], + "card_play": [ + preload("res://assets/audio/ui/052_use_item_02.wav"), + ], +} + +# Pool of audio players for sound effects +var audio_players: Array[AudioStreamPlayer] = [] +const POOL_SIZE = 8 + +func _ready() -> void: + # Initialize pool of audio players + for i in range(POOL_SIZE): + var player = AudioStreamPlayer.new() + player.bus = "SoundEffects" # Make sure to create this bus in the project settings + add_child(player) + audio_players.append(player) + +func play_sound(category: String, type: String, volume_db: float = 0.0) -> void: + var sounds = [] + match category: + "combat": + sounds = combat_sfx.get(type, []) + "ui": + sounds = ui_sfx.get(type, []) + + if sounds.is_empty(): + return + + # Get random sound from array + var sound = sounds[randi() % sounds.size()] + + # Find available player + for player in audio_players: + if not player.playing: + player.stream = sound + player.volume_db = volume_db + player.play() + return + + # If no player is available, stop oldest and reuse + audio_players[0].stop() + audio_players[0].stream = sound + audio_players[0].volume_db = volume_db + audio_players[0].play() + + # Move used player to end of array + var player = audio_players.pop_front() + audio_players.push_back(player) diff --git a/scripts/ui/audio_button.gd b/scripts/ui/audio_button.gd new file mode 100644 index 0000000..750fbc6 --- /dev/null +++ b/scripts/ui/audio_button.gd @@ -0,0 +1,16 @@ +extends Button +class_name AudioButton + +func _ready() -> void: + mouse_entered.connect(_on_mouse_entered) + pressed.connect(_on_pressed) + focus_entered.connect(_on_focus_entered) + +func _on_mouse_entered() -> void: + SoundEffectsSystem.play_sound("ui", "button_hover", -10.0) + +func _on_pressed() -> void: + SoundEffectsSystem.play_sound("ui", "button_click", -5.0) + +func _on_focus_entered() -> void: + SoundEffectsSystem.play_sound("ui", "button_hover", -10.0)