diff --git a/Zennysoft.Game.Ma.Implementation/Game/GameRepo.cs b/Zennysoft.Game.Ma.Implementation/Game/GameRepo.cs index a1cc83fb..42ec9bb7 100644 --- a/Zennysoft.Game.Ma.Implementation/Game/GameRepo.cs +++ b/Zennysoft.Game.Ma.Implementation/Game/GameRepo.cs @@ -19,6 +19,10 @@ public interface IGameRepo : IDisposable event Action? RemoveItemFromInventoryEvent; + event Action? PlayerAttack; + + event Action? PlayerAttackedWall; + void Pause(); void Resume(); @@ -35,6 +39,10 @@ public interface IGameRepo : IDisposable public void RemoveItemFromInventory(InventoryItem item); + public void OnPlayerAttack(); + + public void OnPlayerAttackedWall(); + public void CloseInventory(); public void GameEnded(); @@ -51,6 +59,8 @@ public class GameRepo : IGameRepo public event Action? DoubleExpTimeStart; public event Action? DoubleExpTimeEnd; public event Action? RemoveItemFromInventoryEvent; + public event Action? PlayerAttack; + public event Action? PlayerAttackedWall; public IAutoProp IsPaused => _isPaused; private readonly AutoProp _isPaused; @@ -105,6 +115,16 @@ public class GameRepo : IGameRepo RemoveItemFromInventoryEvent?.Invoke(item); } + public void OnPlayerAttack() + { + PlayerAttack?.Invoke(); + } + + public void OnPlayerAttackedWall() + { + PlayerAttackedWall?.Invoke(); + } + public void CloseInventory() { CloseInventoryEvent?.Invoke(); diff --git a/Zennysoft.Game.Ma/src/audio/InGameAudio.cs b/Zennysoft.Game.Ma/src/audio/InGameAudio.cs index 3f53992b..aa6fb9d5 100644 --- a/Zennysoft.Game.Ma/src/audio/InGameAudio.cs +++ b/Zennysoft.Game.Ma/src/audio/InGameAudio.cs @@ -3,6 +3,7 @@ using Chickensoft.GodotNodeInterfaces; using Chickensoft.Introspection; using Godot; using Zennysoft.Game.Abstractions; +using Zennysoft.Ma.Adapter; namespace Zennysoft.Game.Ma; @@ -17,6 +18,8 @@ public partial class InGameAudio : Node [Dependency] public IPlayer Player => this.DependOn(); + [Dependency] public IGameRepo GameRepo => this.DependOn(); + #region BGM Nodes [Node] public IDimmableAudioStreamPlayer MenuBgm { get; set; } = default!; @@ -32,6 +35,8 @@ public partial class InGameAudio : Node #region SFX Nodes [Node] public IAudioStreamPlayer PlayerAttackSFX { get; set; } = default!; + [Node] public IAudioStreamPlayer PlayerAttackWallSFX { get; set; } = default!; + [Node] public IAudioStreamPlayer MenuScrollSFX { get; set; } = default!; [Node] public IAudioStreamPlayer EquipSFX { get; set; } = default!; @@ -58,6 +63,7 @@ public partial class InGameAudio : Node InGameAudioLogic.Set(AppRepo); InGameAudioLogic.Set(GameEventDepot); InGameAudioLogic.Set(Player); + InGameAudioLogic.Set(GameRepo); InGameAudioBinding = InGameAudioLogic.Bind(); @@ -69,7 +75,9 @@ public partial class InGameAudio : Node .Handle((in InGameAudioLogic.Output.PlayMenuBackSound _) => PlayMenuBackSound()) .Handle((in InGameAudioLogic.Output.PlayInventorySortedSound _) => PlayInventorySortedSound()) .Handle((in InGameAudioLogic.Output.PlayHealingItemSound _) => PlayHealingItemSound()) - .Handle((in InGameAudioLogic.Output.PlayTeleportSound _) => PlayTeleportSound()); + .Handle((in InGameAudioLogic.Output.PlayTeleportSound _) => PlayTeleportSound()) + .Handle((in InGameAudioLogic.Output.PlayPlayerAttackSound _) => { PlayerAttackSFX.Stop(); PlayerAttackSFX.Play(); }) + .Handle((in InGameAudioLogic.Output.PlayPlayerAttackWallSound _) => { PlayerAttackWallSFX.Stop(); PlayerAttackWallSFX.Play(); }); InGameAudioLogic.Start(); } diff --git a/Zennysoft.Game.Ma/src/audio/InGameAudio.tscn b/Zennysoft.Game.Ma/src/audio/InGameAudio.tscn index e48a72de..40b06866 100644 --- a/Zennysoft.Game.Ma/src/audio/InGameAudio.tscn +++ b/Zennysoft.Game.Ma/src/audio/InGameAudio.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=13 format=3 uid="uid://b16ejcwanod72"] +[gd_scene load_steps=15 format=3 uid="uid://b16ejcwanod72"] [ext_resource type="Script" uid="uid://2mnouyn1jcqs" path="res://src/audio/InGameAudio.cs" id="1_gpmcr"] [ext_resource type="AudioStream" uid="uid://dfu0fksb6slhx" path="res://src/audio/music/droney.mp3" id="2_8hfyr"] @@ -6,7 +6,9 @@ [ext_resource type="AudioStream" uid="uid://d2jrktp06xsba" path="res://src/audio/music/crossing-the-gate.mp3" id="3_wbmd6"] [ext_resource type="AudioStream" uid="uid://dn2e2hqujlia1" path="res://src/audio/music/tar-winds.mp3" id="4_surnl"] [ext_resource type="AudioStream" uid="uid://t3g04u722f2k" path="res://src/audio/music/useless immune system-1.mp3" id="6_agr3r"] +[ext_resource type="AudioStream" uid="uid://cn8cugshq3o8k" path="res://src/audio/sfx/PlayerHitWallSFX.wav" id="7_8vh2f"] [ext_resource type="AudioStream" uid="uid://dor0in0x2fg48" path="res://src/audio/sfx/TempFFVII/menu-move.ogg" id="7_777nl"] +[ext_resource type="AudioStream" uid="uid://d1mlduwauechv" path="res://src/audio/sfx/PlayerAttackSFX.wav" id="7_wtvpb"] [ext_resource type="AudioStream" uid="uid://r1tryiit38i8" path="res://src/audio/sfx/TempFFVII/menu-back.ogg" id="8_1xcgo"] [ext_resource type="AudioStream" uid="uid://bjj61s8q2gwb8" path="res://src/audio/sfx/TempFFVII/junction.ogg" id="8_kwybb"] [ext_resource type="AudioStream" uid="uid://myx4s8lmarc2" path="res://src/audio/sfx/TempFFVII/something-earned.ogg" id="10_3lcw5"] @@ -56,46 +58,45 @@ script = ExtResource("2_857rw") [node name="PlayerAttackSFX" type="AudioStreamPlayer" parent="."] unique_name_in_owner = true -script = ExtResource("2_857rw") +stream = ExtResource("7_wtvpb") +volume_db = -5.0 + +[node name="PlayerAttackWallSFX" type="AudioStreamPlayer" parent="."] +unique_name_in_owner = true +stream = ExtResource("7_8vh2f") +volume_db = -5.0 [node name="MenuScrollSFX" type="AudioStreamPlayer" parent="."] unique_name_in_owner = true stream = ExtResource("7_777nl") volume_db = -10.0 -script = ExtResource("2_857rw") [node name="MenuBackSFX" type="AudioStreamPlayer" parent="."] unique_name_in_owner = true stream = ExtResource("8_1xcgo") volume_db = -10.0 -script = ExtResource("2_857rw") [node name="EquipSFX" type="AudioStreamPlayer" parent="."] unique_name_in_owner = true stream = ExtResource("8_kwybb") volume_db = -10.0 -script = ExtResource("2_857rw") [node name="HealSFX" type="AudioStreamPlayer" parent="."] unique_name_in_owner = true stream = ExtResource("10_3lcw5") volume_db = -10.0 -script = ExtResource("2_857rw") [node name="TeleportSFX" type="AudioStreamPlayer" parent="."] unique_name_in_owner = true stream = ExtResource("11_offhc") volume_db = -18.0 -script = ExtResource("2_857rw") [node name="InventorySortedSFX" type="AudioStreamPlayer" parent="."] unique_name_in_owner = true stream = ExtResource("12_wprjr") volume_db = -15.0 -script = ExtResource("2_857rw") [node name="HealingItemSFX" type="AudioStreamPlayer" parent="."] unique_name_in_owner = true stream = ExtResource("10_3lcw5") volume_db = -15.0 -script = ExtResource("2_857rw") diff --git a/Zennysoft.Game.Ma/src/audio/sfx/PlayerAttackSFX.wav b/Zennysoft.Game.Ma/src/audio/sfx/PlayerAttackSFX.wav new file mode 100644 index 00000000..fe820fcc Binary files /dev/null and b/Zennysoft.Game.Ma/src/audio/sfx/PlayerAttackSFX.wav differ diff --git a/Zennysoft.Game.Ma/src/audio/sfx/PlayerAttackSFX.wav.import b/Zennysoft.Game.Ma/src/audio/sfx/PlayerAttackSFX.wav.import new file mode 100644 index 00000000..c34fb217 --- /dev/null +++ b/Zennysoft.Game.Ma/src/audio/sfx/PlayerAttackSFX.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://d1mlduwauechv" +path="res://.godot/imported/PlayerAttackSFX.wav-25ff4e405f0437faa32be96b9f5ca832.sample" + +[deps] + +source_file="res://src/audio/sfx/PlayerAttackSFX.wav" +dest_files=["res://.godot/imported/PlayerAttackSFX.wav-25ff4e405f0437faa32be96b9f5ca832.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/Zennysoft.Game.Ma/src/audio/sfx/PlayerHitWallSFX.wav b/Zennysoft.Game.Ma/src/audio/sfx/PlayerHitWallSFX.wav new file mode 100644 index 00000000..f36ea770 Binary files /dev/null and b/Zennysoft.Game.Ma/src/audio/sfx/PlayerHitWallSFX.wav differ diff --git a/Zennysoft.Game.Ma/src/audio/sfx/PlayerHitWallSFX.wav.import b/Zennysoft.Game.Ma/src/audio/sfx/PlayerHitWallSFX.wav.import new file mode 100644 index 00000000..c0ef859f --- /dev/null +++ b/Zennysoft.Game.Ma/src/audio/sfx/PlayerHitWallSFX.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://cn8cugshq3o8k" +path="res://.godot/imported/PlayerHitWallSFX.wav-bee36ba71b5478424176ab61368ea9f8.sample" + +[deps] + +source_file="res://src/audio/sfx/PlayerHitWallSFX.wav" +dest_files=["res://.godot/imported/PlayerHitWallSFX.wav-bee36ba71b5478424176ab61368ea9f8.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/Zennysoft.Game.Ma/src/audio/state/InGameAudioLogic.Output.cs b/Zennysoft.Game.Ma/src/audio/state/InGameAudioLogic.Output.cs index 84922cb1..59d706a5 100644 --- a/Zennysoft.Game.Ma/src/audio/state/InGameAudioLogic.Output.cs +++ b/Zennysoft.Game.Ma/src/audio/state/InGameAudioLogic.Output.cs @@ -13,6 +13,8 @@ public partial class InGameAudioLogic #region SFX public readonly record struct PlayPlayerAttackSound; + public readonly record struct PlayPlayerAttackWallSound; + public readonly record struct PlayMenuScrollSound; public readonly record struct PlayEquipSound; diff --git a/Zennysoft.Game.Ma/src/audio/state/states/InGameAudioLogic.State.Enabled.cs b/Zennysoft.Game.Ma/src/audio/state/states/InGameAudioLogic.State.Enabled.cs index 5af06c20..9b275405 100644 --- a/Zennysoft.Game.Ma/src/audio/state/states/InGameAudioLogic.State.Enabled.cs +++ b/Zennysoft.Game.Ma/src/audio/state/states/InGameAudioLogic.State.Enabled.cs @@ -1,4 +1,5 @@ using Chickensoft.Introspection; +using Zennysoft.Ma.Adapter; namespace Zennysoft.Game.Ma; @@ -14,6 +15,7 @@ public partial class InGameAudioLogic var player = Get(); OnOverworldEntered(); var gameEventDepot = Get(); + var gameRepo = Get(); gameEventDepot.OverworldEntered += OnOverworldEntered; gameEventDepot.DungeonAThemeAreaEntered += OnDungeonAThemeEntered; gameEventDepot.MenuScrolled += OnMenuScrolled; @@ -25,11 +27,14 @@ public partial class InGameAudioLogic gameEventDepot.HealingItemConsumed += OnHealingItemConsumed; gameEventDepot.RestorativePickedUp += OnRestorativePickedUp; gameEventDepot.TeleportEntered += OnTeleportEntered; + gameRepo.PlayerAttack += OnPlayerAttack; + gameRepo.PlayerAttackedWall += OnPlayerAttackWall; }); OnDetach(() => { var gameEventDepot = Get(); var player = Get(); + var gameRepo = Get(); gameEventDepot.OverworldEntered -= OnOverworldEntered; gameEventDepot.DungeonAThemeAreaEntered -= OnDungeonAThemeEntered; gameEventDepot.MenuScrolled -= OnMenuScrolled; @@ -39,9 +44,15 @@ public partial class InGameAudioLogic player.EquippedAccessory.Changed -= OnEquippedItem; gameEventDepot.InventorySorted -= OnInventorySorted; gameEventDepot.TeleportEntered -= OnTeleportEntered; + gameRepo.PlayerAttack -= OnPlayerAttack; + gameRepo.PlayerAttackedWall -= OnPlayerAttackWall; }); } + private void OnPlayerAttack() => Output(new Output.PlayPlayerAttackSound()); + + private void OnPlayerAttackWall() => Output(new Output.PlayPlayerAttackWallSound()); + private void OnRestorativePickedUp(Restorative restorative) => Output(new Output.PlayHealingItemSound()); private void OnMenuBackedOut() => Output(new Output.PlayMenuBackSound()); diff --git a/Zennysoft.Game.Ma/src/player/Player.cs b/Zennysoft.Game.Ma/src/player/Player.cs index 4236d0f3..c4cb6e1f 100644 --- a/Zennysoft.Game.Ma/src/player/Player.cs +++ b/Zennysoft.Game.Ma/src/player/Player.cs @@ -8,7 +8,6 @@ using Godot.Collections; using SimpleInjector; using System; using System.Linq; -using Zennysoft.Game.Abstractions; using Zennysoft.Ma.Adapter; namespace Zennysoft.Game.Ma; @@ -88,6 +87,8 @@ public partial class Player : CharacterBody3D, IPlayer [Node] private Area3D CollisionDetector { get; set; } = default!; [Node] private Timer HealthTimer { get; set; } = default!; + + [Node] private RigidBody3D WallCheck { get; set; } = default!; #endregion @@ -211,10 +212,18 @@ public partial class Player : CharacterBody3D, IPlayer PlayerBinding .Handle((in PlayerLogic.Output.Animations.Attack output) => { - var attackSpeed = EquippedWeapon.Value.AttackSpeed; - AnimationPlayer.SetSpeedScale((float)attackSpeed); - - AnimationPlayer.Play("attack"); + if (PlayerIsHittingGeometry()) + { + AnimationPlayer.Play("hit_wall"); + _gameRepo.OnPlayerAttackedWall(); + } + else + { + var attackSpeed = EquippedWeapon.Value.AttackSpeed; + AnimationPlayer.SetSpeedScale((float)attackSpeed); + AnimationPlayer.Play("attack"); + _gameRepo.OnPlayerAttack(); + } }) .Handle((in PlayerLogic.Output.ThrowItem output) => { @@ -230,32 +239,6 @@ public partial class Player : CharacterBody3D, IPlayer this.Provide(); } - private void CollisionDetector_AreaEntered(Area3D area) - { - if (area.GetParent() is InventoryItem inventoryItem) - { - var isAdded = Inventory.TryAdd(inventoryItem); - if (isAdded) - { - _gameRepo.AnnounceMessageOnMainScreen($"{inventoryItem.ItemName} picked up."); - inventoryItem.QueueFree(); - } - else - _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {inventoryItem.ItemName}."); - } - if (area.GetParent() is DroppedItem droppedItem) - { - var isAdded = Inventory.TryAdd(droppedItem.Item); - if (isAdded) - { - _gameRepo.AnnounceMessageOnMainScreen($"{droppedItem.Item.ItemName} picked up."); - droppedItem.QueueFree(); - } - else - _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {droppedItem.Item.ItemName}."); - } - } - public void OnReady() { SetPhysicsProcess(true); @@ -502,7 +485,6 @@ public partial class Player : CharacterBody3D, IPlayer private void OnAnimationFinished(StringName animation) { - GD.Print("Attack finished"); PlayerLogic.Input(new PlayerLogic.Input.AttackAnimationFinished()); } @@ -594,4 +576,43 @@ public partial class Player : CharacterBody3D, IPlayer if (EquippedWeapon.Value.WeaponTag == WeaponTag.Knockback) enemy.Knockback(0.3f, -CurrentBasis.Z.Normalized()); } + + private void CollisionDetector_AreaEntered(Area3D area) + { + if (area.GetParent() is InventoryItem inventoryItem) + { + var isAdded = Inventory.TryAdd(inventoryItem); + if (isAdded) + { + _gameRepo.AnnounceMessageOnMainScreen($"{inventoryItem.ItemName} picked up."); + inventoryItem.QueueFree(); + } + else + _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {inventoryItem.ItemName}."); + } + if (area.GetParent() is DroppedItem droppedItem) + { + var isAdded = Inventory.TryAdd(droppedItem.Item); + if (isAdded) + { + _gameRepo.AnnounceMessageOnMainScreen($"{droppedItem.Item.ItemName} picked up."); + droppedItem.QueueFree(); + } + else + _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {droppedItem.Item.ItemName}."); + } + } + + private bool PlayerIsHittingGeometry() + { + var collisions = WallCheck.GetCollidingBodies(); + return collisions.Count > 0; + } + + private void WallCheck_BodyEntered(Node body) + { + PlayerLogic.Input(new PlayerLogic.Input.AttackAnimationFinished()); + GD.Print("Hit wall"); + AnimationPlayer.Stop(); + } } diff --git a/Zennysoft.Game.Ma/src/player/Player.tscn b/Zennysoft.Game.Ma/src/player/Player.tscn index 9334e660..8d756943 100644 --- a/Zennysoft.Game.Ma/src/player/Player.tscn +++ b/Zennysoft.Game.Ma/src/player/Player.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=60 format=4 uid="uid://cfecvvav8kkp6"] +[gd_scene load_steps=61 format=4 uid="uid://cfecvvav8kkp6"] [ext_resource type="Script" uid="uid://yxmiqy7i0t7r" path="res://src/player/Player.cs" id="1_xcol5"] [ext_resource type="Script" uid="uid://6edayafleq8y" path="res://src/hitbox/Hitbox.cs" id="2_lb3qc"] @@ -96,16 +96,28 @@ tracks/1/keys = { "values": [10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] } -[sub_resource type="Animation" id="Animation_uxo8q"] -resource_name = "explosion" -length = 3.66668 +[sub_resource type="Animation" id="Animation_es4xk"] +resource_name = "hit_wall" +length = 0.3 step = 0.0833333 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("SwordSlashAnimation:frame") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.0833333, 0.166667), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 1, +"values": [10, 1, 11] +} [sub_resource type="AnimationLibrary" id="AnimationLibrary_w8l8m"] _data = { &"RESET": SubResource("Animation_hcjph"), &"attack": SubResource("Animation_0jjwv"), -&"explosion": SubResource("Animation_uxo8q") +&"hit_wall": SubResource("Animation_es4xk") } [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_d4v1j"] @@ -465,6 +477,9 @@ animations = [{ "speed": 12.0 }] +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_es4xk"] +height = 1.6909 + [node name="Player" type="CharacterBody3D"] collision_layer = 806 collision_mask = 775 @@ -564,3 +579,19 @@ spot_attenuation = 0.22 [node name="OmniLight3D2" type="OmniLight3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 5.65529, 0.471719) + +[node name="WallCheck" type="RigidBody3D" parent="."] +unique_name_in_owner = true +collision_layer = 0 +axis_lock_linear_x = true +axis_lock_linear_y = true +axis_lock_linear_z = true +axis_lock_angular_x = true +axis_lock_angular_y = true +axis_lock_angular_z = true +contact_monitor = true +max_contacts_reported = 100 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="WallCheck"] +transform = Transform3D(1.91069e-15, 4.37114e-08, 1, 1, -4.37114e-08, 0, 4.37114e-08, 1, -4.37114e-08, 0.293308, 1.35803, -0.602638) +shape = SubResource("CapsuleShape3D_es4xk")