diff --git a/AlientAttack.MonoGame/Audio/AudioManager.cs b/AlientAttack.MonoGame/Audio/AudioManager.cs index d62d6b8..b8c39e3 100644 --- a/AlientAttack.MonoGame/Audio/AudioManager.cs +++ b/AlientAttack.MonoGame/Audio/AudioManager.cs @@ -1,8 +1,10 @@ -using Microsoft.Xna.Framework; +using AlienAttack.MonoGame.Things.Items; +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Media; using System; +using System.Collections.Generic; namespace AlienAttack.MonoGame.Audio; @@ -19,6 +21,7 @@ public sealed class AudioManager private VariantSoundPool _enemyGunPool = default!; private VariantSoundPool _explosionPool = default!; private VariantSoundPool _impactPool = default!; + private Dictionary _pickupPool = []; // Rate limiters (global caps per category/event) private RateLimiter _playerGunLimiter = new(0.06f); // max ~16 plays/sec @@ -39,16 +42,9 @@ public sealed class AudioManager LoadImpactPool(content); LoadExplosionPool(content); - //var playerGun = content.Load("Audio/Sfx/player_fire"); - //var enemyGun = content.Load("Audio/Sfx/enemy_fire"); - //var explosion = content.Load("Audio/Sfx/explosion"); - //var impact = content.Load("Audio/Sfx/impact"); - - // Create pools with sane voice caps - //_playerGunPool = new SoundPool(playerGun, maxVoices: 2); - //_enemyGunPool = new SoundPool(enemyGun, maxVoices: 3); - //_explosionPool = new SoundPool(explosion, maxVoices: 6); - //_impactPool = new SoundPool(impact, maxVoices: 4); + LoadShieldPickupPool(content); + LoadAmmoPickupPool(content); + LoadRocketsPickupPool(content); } private void LoadPlayerGunPool(ContentManager content) @@ -111,6 +107,56 @@ public sealed class AudioManager _explosionPool = new VariantSoundPool(variants, voicesPerVariant: 1, rng: _random); } + private void LoadShieldPickupPool(ContentManager content) + { + var shieldVariants = new[] + { + content.Load("Sfx/Shield/SCIEnrg_Shield Activate_01_SFRMS_SCIWPNS"), + content.Load("Sfx/Shield/SCIEnrg_Shield Activate_02_SFRMS_SCIWPNS"), + content.Load("Sfx/Shield/SCIEnrg_Shield Activate_03_SFRMS_SCIWPNS"), + content.Load("Sfx/Shield/SCIEnrg_Shield Activate_04_SFRMS_SCIWPNS"), + content.Load("Sfx/Shield/SCIEnrg_Shield Activate_05_SFRMS_SCIWPNS") + }; + + _pickupPool.Add(PickupKind.Shield, new VariantSoundPool(shieldVariants, voicesPerVariant: 1, rng: _random)); + } + + private void LoadAmmoPickupPool(ContentManager content) + { + var variants = new[] + { + content.Load("Sfx/Reload/GUNMech_Insert Clip_01_SFRMS_SCIWPNS"), + content.Load("Sfx/Reload/GUNMech_Insert Clip_02_SFRMS_SCIWPNS"), + content.Load("Sfx/Reload/GUNMech_Insert Clip_03_SFRMS_SCIWPNS"), + content.Load("Sfx/Reload/GUNMech_Insert Clip_04_SFRMS_SCIWPNS"), + content.Load("Sfx/Reload/GUNMech_Insert Clip_05_SFRMS_SCIWPNS") + }; + + _pickupPool.Add(PickupKind.Ammo, new VariantSoundPool(variants, voicesPerVariant: 1, rng: _random)); + } + + private void LoadRocketsPickupPool(ContentManager content) + { + var variants = new[] + { + content.Load("Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_01_SFRMS_SCIWPNS"), + content.Load("Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_02_SFRMS_SCIWPNS"), + content.Load("Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_03_SFRMS_SCIWPNS"), + content.Load("Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_04_SFRMS_SCIWPNS"), + content.Load("Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_05_SFRMS_SCIWPNS"), + content.Load("Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_06_SFRMS_SCIWPNS"), + content.Load("Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_07_SFRMS_SCIWPNS"), + content.Load("Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_08_SFRMS_SCIWPNS"), + content.Load("Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_09_SFRMS_SCIWPNS"), + content.Load("Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_10_SFRMS_SCIWPNS"), + content.Load("Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_11_SFRMS_SCIWPNS"), + }; + + _pickupPool.Add(PickupKind.Rockets, new VariantSoundPool(variants, voicesPerVariant: 1, rng: _random)); + } + + //GUNMech_Rocket Launcher Reload_01_SFRMS_SCIWPNS + // Call this once per frame public void Update(GameTime gameTime) { @@ -192,6 +238,21 @@ public sealed class AudioManager } } + public void PlayPickup(PickupKind kind) + { + if (_pickupPool.TryGetValue(kind, out VariantSoundPool pool) == false) + return; + + float baseVol = 0.85f; + float vol = baseVol * RandRange(0.95f, 1.05f); + float pitch = RandRange(-0.04f, 0.04f); + + pool.Play( + volume: Clamp01(MasterVolume * SfxVolume * vol), + pitch: pitch + ); + } + // ----------------------- // Music // ----------------------- diff --git a/AlientAttack.MonoGame/Content/Content.mgcb b/AlientAttack.MonoGame/Content/Content.mgcb index 116d957..2f3c15e 100644 --- a/AlientAttack.MonoGame/Content/Content.mgcb +++ b/AlientAttack.MonoGame/Content/Content.mgcb @@ -223,6 +223,138 @@ /processorParam:Quality=Best /build:Sfx/Pistol/GUNPis_Pistol Fire_06_SFRMS_SCIWPNS.wav;Sfx/Pistol/GUNPis_Pistol Fire_06_SFRMS_SCIWPNS +#begin Sfx/Reload/GUNMech_Insert Clip_01_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Reload/GUNMech_Insert Clip_01_SFRMS_SCIWPNS.wav;Sfx/Reload/GUNMech_Insert Clip_01_SFRMS_SCIWPNS + +#begin Sfx/Reload/GUNMech_Insert Clip_02_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Reload/GUNMech_Insert Clip_02_SFRMS_SCIWPNS.wav;Sfx/Reload/GUNMech_Insert Clip_02_SFRMS_SCIWPNS + +#begin Sfx/Reload/GUNMech_Insert Clip_03_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Reload/GUNMech_Insert Clip_03_SFRMS_SCIWPNS.wav;Sfx/Reload/GUNMech_Insert Clip_03_SFRMS_SCIWPNS + +#begin Sfx/Reload/GUNMech_Insert Clip_04_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Reload/GUNMech_Insert Clip_04_SFRMS_SCIWPNS.wav;Sfx/Reload/GUNMech_Insert Clip_04_SFRMS_SCIWPNS + +#begin Sfx/Reload/GUNMech_Insert Clip_05_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Reload/GUNMech_Insert Clip_05_SFRMS_SCIWPNS.wav;Sfx/Reload/GUNMech_Insert Clip_05_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_01_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_01_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_01_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_02_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_02_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_02_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_03_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_03_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_03_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_04_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_04_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_04_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_05_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_05_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_05_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_06_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_06_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_06_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_01_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_01_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_01_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_02_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_02_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_02_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_03_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_03_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_03_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_04_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_04_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_04_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_05_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_05_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_05_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_06_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_06_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_06_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_07_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_07_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_07_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_08_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_08_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_08_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_09_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_09_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_09_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_10_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_10_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_10_SFRMS_SCIWPNS + +#begin Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_11_SFRMS_SCIWPNS.wav +/importer:WavImporter +/processor:SoundEffectProcessor +/processorParam:Quality=Best +/build:Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_11_SFRMS_SCIWPNS.wav;Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_11_SFRMS_SCIWPNS + #begin Sfx/Shield/SCIEnrg_Shield Activate_01_SFRMS_SCIWPNS.wav /importer:WavImporter /processor:SoundEffectProcessor diff --git a/AlientAttack.MonoGame/Content/Sfx/Reload/GUNMech_Insert Clip_01_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Reload/GUNMech_Insert Clip_01_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..b1dcaaf Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Reload/GUNMech_Insert Clip_01_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Reload/GUNMech_Insert Clip_02_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Reload/GUNMech_Insert Clip_02_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..7243f4f Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Reload/GUNMech_Insert Clip_02_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Reload/GUNMech_Insert Clip_03_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Reload/GUNMech_Insert Clip_03_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..a07cf99 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Reload/GUNMech_Insert Clip_03_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Reload/GUNMech_Insert Clip_04_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Reload/GUNMech_Insert Clip_04_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..2c3127a Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Reload/GUNMech_Insert Clip_04_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Reload/GUNMech_Insert Clip_05_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Reload/GUNMech_Insert Clip_05_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..20f7847 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Reload/GUNMech_Insert Clip_05_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_01_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_01_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..712d7fe Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_01_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_02_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_02_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..24f8248 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_02_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_03_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_03_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..38f1ae9 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_03_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_04_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_04_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..82a0f78 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_04_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_05_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_05_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..53d19da Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_05_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_06_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_06_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..bf226c8 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNArtl_Rocket Launcher Fire_06_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_01_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_01_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..d42f843 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_01_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_02_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_02_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..580996d Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_02_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_03_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_03_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..ecf432c Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_03_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_04_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_04_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..cecdd03 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_04_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_05_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_05_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..f77375b Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_05_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_06_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_06_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..caf6101 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_06_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_07_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_07_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..e3f5681 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_07_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_08_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_08_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..d4be55a Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_08_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_09_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_09_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..c64b854 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_09_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_10_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_10_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..21fe6e8 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_10_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_11_SFRMS_SCIWPNS.wav b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_11_SFRMS_SCIWPNS.wav new file mode 100644 index 0000000..84170d4 Binary files /dev/null and b/AlientAttack.MonoGame/Content/Sfx/Rocket Launcher/GUNMech_Rocket Launcher Reload_11_SFRMS_SCIWPNS.wav differ diff --git a/AlientAttack.MonoGame/Things/Bullets/MinigunBulletLarge.cs b/AlientAttack.MonoGame/Things/Bullets/MinigunBulletLarge.cs index 9a87e76..4fdb62d 100644 --- a/AlientAttack.MonoGame/Things/Bullets/MinigunBulletLarge.cs +++ b/AlientAttack.MonoGame/Things/Bullets/MinigunBulletLarge.cs @@ -6,9 +6,12 @@ namespace AlienAttack.MonoGame.Things.Bullets; internal class MinigunBulletLarge : Bullet { + public const int Width = 9; + public const int Height = 27; + public MinigunBulletLarge(float x, float y, float xVel, float yVel, Sprite owner) : base(x, y, xVel, yVel, owner) { - BoundBox = new(0, 0, 9, 27); + BoundBox = new(0, 0, Width, Height); Damage = 3; } diff --git a/AlientAttack.MonoGame/Things/Bullets/MinigunBulletMedium.cs b/AlientAttack.MonoGame/Things/Bullets/MinigunBulletMedium.cs index 3b604d0..564ee02 100644 --- a/AlientAttack.MonoGame/Things/Bullets/MinigunBulletMedium.cs +++ b/AlientAttack.MonoGame/Things/Bullets/MinigunBulletMedium.cs @@ -6,9 +6,12 @@ namespace AlienAttack.MonoGame.Things.Bullets; internal class MinigunBulletMedium : Bullet { + public const int Width = 11; + public const int Height = 21; + public MinigunBulletMedium(float x, float y, float xVel, float yVel, Sprite owner) : base(x, y, xVel, yVel, owner) { - BoundBox = new(0, 0, 11, 21); + BoundBox = new(0, 0, Width, Height); Damage = 2; } diff --git a/AlientAttack.MonoGame/Things/Bullets/MinigunBulletSmall.cs b/AlientAttack.MonoGame/Things/Bullets/MinigunBulletSmall.cs index 91c74ae..1c3d101 100644 --- a/AlientAttack.MonoGame/Things/Bullets/MinigunBulletSmall.cs +++ b/AlientAttack.MonoGame/Things/Bullets/MinigunBulletSmall.cs @@ -6,9 +6,12 @@ namespace AlienAttack.MonoGame.Things.Bullets; internal class MinigunBulletSmall : Bullet { + public const int Width = 11; + public const int Height = 21; + public MinigunBulletSmall(float x, float y, float xVel, float yVel, Sprite owner) : base(x, y, xVel, yVel, owner) { - BoundBox = new(0, 0, 7, 17); + BoundBox = new(0, 0, Width, Height); Damage = 1; } diff --git a/AlientAttack.MonoGame/Things/Enemies/RedEnemy.cs b/AlientAttack.MonoGame/Things/Enemies/RedEnemy.cs index 02955e0..ac54c3c 100644 --- a/AlientAttack.MonoGame/Things/Enemies/RedEnemy.cs +++ b/AlientAttack.MonoGame/Things/Enemies/RedEnemy.cs @@ -14,7 +14,7 @@ internal class RedEnemy : EnemyShip public RedEnemy(int x, int y) : base(x, y) { BoundBox = new Rectangle(0, 0, 64, 64); - YVelocity = 2; + YVelocity = 1.5f; } public override void Draw(SpriteDrawArgs args) @@ -88,12 +88,13 @@ internal class RedEnemy : EnemyShip CurrentFireThreshold--; } - if (CurrentFireThreshold == 0 && context.Random.Next(0, 20) == 1) + if (CurrentFireThreshold == 0 && context.Random.Next(0, 50) == 1) { float originX = XPosition + (BoundBox.Width / 2) - (7 / 2); - context.SpawnSprite(new MinigunBulletSmall(originX - 9, YPosition + BoundBox.Height - 12, 0, 2 + YVelocity, this)); - context.SpawnSprite(new MinigunBulletSmall(originX + 14, YPosition + BoundBox.Height - 12, 0, 2 + YVelocity, this)); + context.SpawnSprite(new MinigunBulletSmall(originX - 9, YPosition + BoundBox.Height - 12, -1, 2 + YVelocity, this)); + context.SpawnSprite(new MinigunBulletSmall(originX + 3, YPosition + BoundBox.Height - 12, 0, 2 + YVelocity, this)); + context.SpawnSprite(new MinigunBulletSmall(originX + 14, YPosition + BoundBox.Height - 12, 1, 2 + YVelocity, this)); CurrentFireThreshold = FireThreshold; diff --git a/AlientAttack.MonoGame/Things/Items/Ammo.cs b/AlientAttack.MonoGame/Things/Items/Ammo.cs index 5e6cab9..f774631 100644 --- a/AlientAttack.MonoGame/Things/Items/Ammo.cs +++ b/AlientAttack.MonoGame/Things/Items/Ammo.cs @@ -2,6 +2,8 @@ internal class Ammo : Item { + protected override PickupKind Kind => PickupKind.Ammo; + public Ammo(int x, int y) : base(x, y) { TextureName = @$"Sprites\Powerup_Ammo"; diff --git a/AlientAttack.MonoGame/Things/Items/Energy.cs b/AlientAttack.MonoGame/Things/Items/Energy.cs index 9a922e8..393a1b7 100644 --- a/AlientAttack.MonoGame/Things/Items/Energy.cs +++ b/AlientAttack.MonoGame/Things/Items/Energy.cs @@ -2,6 +2,8 @@ internal class Energy : Item { + protected override PickupKind Kind => PickupKind.Plasma; + public Energy(int x, int y) : base(x, y) { TextureName = @$"Sprites\Powerup_Energy"; diff --git a/AlientAttack.MonoGame/Things/Items/Health.cs b/AlientAttack.MonoGame/Things/Items/Health.cs index 8d42d23..eb9934f 100644 --- a/AlientAttack.MonoGame/Things/Items/Health.cs +++ b/AlientAttack.MonoGame/Things/Items/Health.cs @@ -2,6 +2,8 @@ internal class Health : Item { + protected override PickupKind Kind => PickupKind.Health; + public Health(int x, int y) : base(x, y) { TextureName = @$"Sprites\Powerup_Health"; diff --git a/AlientAttack.MonoGame/Things/Items/Item.cs b/AlientAttack.MonoGame/Things/Items/Item.cs index 3469a91..2c87a82 100644 --- a/AlientAttack.MonoGame/Things/Items/Item.cs +++ b/AlientAttack.MonoGame/Things/Items/Item.cs @@ -14,6 +14,8 @@ internal abstract class Item : MoveableSprite protected string TextureName; + protected abstract PickupKind Kind { get; } + public Item(int x, int y) : base(x, y) { YVelocity = .5f; @@ -61,6 +63,7 @@ internal abstract class Item : MoveableSprite { IsDead = true; ApplyEffect(player); + context.AudioManager.PlayPickup(Kind); } } diff --git a/AlientAttack.MonoGame/Things/Items/PickupKind.cs b/AlientAttack.MonoGame/Things/Items/PickupKind.cs new file mode 100644 index 0000000..f44ebab --- /dev/null +++ b/AlientAttack.MonoGame/Things/Items/PickupKind.cs @@ -0,0 +1,10 @@ +namespace AlienAttack.MonoGame.Things.Items; + +public enum PickupKind +{ + Health, + Shield, + Ammo, + Rockets, + Plasma +} \ No newline at end of file diff --git a/AlientAttack.MonoGame/Things/Items/Rockets.cs b/AlientAttack.MonoGame/Things/Items/Rockets.cs index 194d558..412d41b 100644 --- a/AlientAttack.MonoGame/Things/Items/Rockets.cs +++ b/AlientAttack.MonoGame/Things/Items/Rockets.cs @@ -2,6 +2,8 @@ internal class Rockets : Item { + protected override PickupKind Kind => PickupKind.Rockets; + public Rockets(int x, int y) : base(x, y) { TextureName = @$"Sprites\Powerup_Rockets"; diff --git a/AlientAttack.MonoGame/Things/Items/Shields.cs b/AlientAttack.MonoGame/Things/Items/Shields.cs index 6a6d5c2..691f509 100644 --- a/AlientAttack.MonoGame/Things/Items/Shields.cs +++ b/AlientAttack.MonoGame/Things/Items/Shields.cs @@ -2,6 +2,8 @@ internal class Shields : Item { + protected override PickupKind Kind => PickupKind.Shield; + public Shields(int x, int y) : base(x, y) { TextureName = @$"Sprites\Powerup_Shields"; diff --git a/AlientAttack.MonoGame/Things/Muzzles/Muzzle.cs b/AlientAttack.MonoGame/Things/Muzzles/Muzzle.cs new file mode 100644 index 0000000..02c492c --- /dev/null +++ b/AlientAttack.MonoGame/Things/Muzzles/Muzzle.cs @@ -0,0 +1,9 @@ +using Microsoft.Xna.Framework; + +namespace AlienAttack.MonoGame.Things.Muzzles; + +public readonly struct Muzzle(MuzzleId id, Vector2 localPx) +{ + public MuzzleId Id { get; } = id; + public Vector2 LocalPx { get; } = localPx; +} \ No newline at end of file diff --git a/AlientAttack.MonoGame/Things/Muzzles/MuzzleId.cs b/AlientAttack.MonoGame/Things/Muzzles/MuzzleId.cs new file mode 100644 index 0000000..284004c --- /dev/null +++ b/AlientAttack.MonoGame/Things/Muzzles/MuzzleId.cs @@ -0,0 +1,8 @@ +namespace AlienAttack.MonoGame.Things.Muzzles; + +public enum MuzzleId +{ + Center, + Left, + Right +} \ No newline at end of file diff --git a/AlientAttack.MonoGame/Things/Muzzles/MuzzleMath.cs b/AlientAttack.MonoGame/Things/Muzzles/MuzzleMath.cs new file mode 100644 index 0000000..488d47c --- /dev/null +++ b/AlientAttack.MonoGame/Things/Muzzles/MuzzleMath.cs @@ -0,0 +1,20 @@ +using Microsoft.Xna.Framework; + +namespace AlienAttack.MonoGame.Things.Muzzles; + +public static class MuzzleMath +{ + public static Vector2 GetMuzzleWorld(Sprite owner, Vector2 muzzleLocalPx, float ownerScale = 1f) + { + // Assumes owner.Position is the top-left of the sprite + return owner.Position + muzzleLocalPx * ownerScale; + } + + public static Vector2 CenterBulletOn(Vector2 muzzleWorld, int bulletW, int bulletH) + { + return new Vector2( + muzzleWorld.X - bulletW / 2f, + muzzleWorld.Y - bulletH / 2f + ); + } +} \ No newline at end of file diff --git a/AlientAttack.MonoGame/Things/Player.cs b/AlientAttack.MonoGame/Things/Player.cs index a408ca5..b4a6664 100644 --- a/AlientAttack.MonoGame/Things/Player.cs +++ b/AlientAttack.MonoGame/Things/Player.cs @@ -1,15 +1,13 @@ using AlienAttack.MonoGame.Things.Bullets; using AlienAttack.MonoGame.Things.Enemies; using AlienAttack.MonoGame.Things.Items; +using AlienAttack.MonoGame.Things.Muzzles; using AlienAttack.MonoGame.Things.Weapons; using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using System; using System.Collections.Generic; -using System.IO; -using static System.Net.Mime.MediaTypeNames; namespace AlienAttack.MonoGame.Things; @@ -52,11 +50,19 @@ internal class Player : MoveableSprite public int Health { get; protected set; } public int Shield { get; protected set; } + public IReadOnlyList Muzzles { get; } = + [ + new Muzzle(MuzzleId.Center, new Vector2(31.5f, 1f)), + new Muzzle(MuzzleId.Left, new Vector2(15f, 27f)), + new Muzzle(MuzzleId.Right, new Vector2(47f, 27f)), + ]; + public Player(int x, int y) : base(x, y) { BoundBox = new Rectangle(0, 0, 64, 64); - ActiveWeapons.Add(new Minigun()); + //ActiveWeapons.Add(new Minigun()); //ActiveWeapons.Add(new FastMinigun()); + ActiveWeapons.Add(new MinigunSpread()); Health = 100; Shield = 100; } @@ -336,10 +342,12 @@ internal class Player : MoveableSprite { //SCIEnrg_Shield Activate_01_SFRMS_SCIWPNS - int number = context.Random.Next(1, 6); + //int number = context.Random.Next(1, 6); - SoundEffect soundEffect = context.Content.Load(@$"Sfx\Shield\SCIEnrg_Shield Activate_0{number}_SFRMS_SCIWPNS"); - soundEffect.Play(0.85f, (float)(context.Random.NextDouble() * 0.1 - 0.05), 0); + //SoundEffect soundEffect = context.Content.Load(@$"Sfx\Shield\SCIEnrg_Shield Activate_0{number}_SFRMS_SCIWPNS"); + //soundEffect.Play(0.85f, (float)(context.Random.NextDouble() * 0.1 - 0.05), 0); + + //context.AudioManager.PlayPickup(PickupKind.Shield); GiveShield(20); } diff --git a/AlientAttack.MonoGame/Things/Weapons/Anchor.cs b/AlientAttack.MonoGame/Things/Weapons/Anchor.cs new file mode 100644 index 0000000..c45750c --- /dev/null +++ b/AlientAttack.MonoGame/Things/Weapons/Anchor.cs @@ -0,0 +1,12 @@ +namespace AlienAttack.MonoGame.Things.Weapons; + +public enum Anchor +{ + TopLeft, + TopCenter, + TopRight, + Center, + BottomLeft, + BottomCenter, + BottomRight +} \ No newline at end of file diff --git a/AlientAttack.MonoGame/Things/Weapons/FireBulletContext.cs b/AlientAttack.MonoGame/Things/Weapons/FireBulletContext.cs new file mode 100644 index 0000000..393ed11 --- /dev/null +++ b/AlientAttack.MonoGame/Things/Weapons/FireBulletContext.cs @@ -0,0 +1,43 @@ +using AlienAttack.MonoGame.Things.Bullets; +using AlienAttack.MonoGame.Things.Muzzles; +using Microsoft.Xna.Framework; +using System; + +namespace AlienAttack.MonoGame.Things.Weapons; + +internal record FireBulletContext +{ + public Sprite Owner { get; init; } + public SpriteUpdateContext SpriteUpdateContext { get; init; } + public int BulletWidth { get; init; } + public int BulletHeight { get; init; } + public Anchor Anchor { get; init; } + public Shot[] Shots { get; init; } + public Func Factory { get; init; } +} + +public record Shot +{ + public float OffsetX { get; init; } + public float OffsetY { get; init; } + public float XVelocity { get; init; } + public float YVelocity { get; init; } +} + +internal record FireBulletFromMuzzleContext +{ + public Sprite Owner { get; init; } + public SpriteUpdateContext SpriteUpdateContext { get; init; } + public int BulletWidth { get; init; } + public int BulletHeight { get; init; } + public MuzzleShot[] Shots { get; init; } + public Vector2 ExtraOffset { get; init; } = default; + public Func Factory { get; init; } +} + +public record MuzzleShot +{ + public MuzzleId Muzzle { get; init; } + public float XVelocity { get; init; } + public float YVelocity { get; init; } +} \ No newline at end of file diff --git a/AlientAttack.MonoGame/Things/Weapons/Minigun.cs b/AlientAttack.MonoGame/Things/Weapons/Minigun.cs index e67fa61..4bf2836 100644 --- a/AlientAttack.MonoGame/Things/Weapons/Minigun.cs +++ b/AlientAttack.MonoGame/Things/Weapons/Minigun.cs @@ -1,9 +1,9 @@ using AlienAttack.MonoGame.Things.Bullets; -using Microsoft.Xna.Framework.Audio; +using AlienAttack.MonoGame.Things.Muzzles; namespace AlienAttack.MonoGame.Things.Weapons; -public class Minigun : Weapon +internal class Minigun : Weapon { public override int FireThreshold => 15; @@ -31,7 +31,49 @@ public class Minigun : Weapon } } -public class FastMinigun : Weapon +internal class MinigunSpread : Weapon +{ + public override int FireThreshold => 20; + + public override void Fire(Sprite owner, SpriteUpdateContext context) + { + // Calculate bullet spawn positions relative to the player's bounding box + int x1 = (int)owner.XPosition + 14; + int x2 = (int)owner.XPosition + owner.BoundBox.Width - 16; + int x3 = (int)owner.XPosition + owner.BoundBox.Width / 2 - 1; + int y = (int)owner.YPosition + 10; + + MinigunBulletLarge bullet1 = new(x1, y, -1, -6, owner); + MinigunBulletLarge bullet2 = new(x2, y, 1, -6, owner); + MinigunBulletLarge bullet3 = new(x3, y-16, 0, -6, owner); + + //context.SpawnSprite(bullet1); + //context.SpawnSprite(bullet2); + //context.SpawnSprite(bullet3); + + FireBulletFromMuzzleContext fireBulletContext = new() + { + Owner = owner, + SpriteUpdateContext = context, + BulletWidth = MinigunBulletLarge.Width, + BulletHeight = MinigunBulletLarge.Height, + Factory = (x, y, vx, vy2, o) => new MinigunBulletLarge(x, y, vx, vy2, o), + ExtraOffset = new(4f, 0), + Shots = + [ + new() { Muzzle = MuzzleId.Left, XVelocity = -1, YVelocity = -6 }, + new() { Muzzle = MuzzleId.Right, XVelocity = 1, YVelocity = -6 }, + new() { Muzzle = MuzzleId.Center, XVelocity = 0, YVelocity = -6 } + ] + }; + + FireFromMuzzle(fireBulletContext); + + context.AudioManager.PlayPlayerFire(); + } +} + +internal class FastMinigun : Weapon { public override int FireThreshold => 10; diff --git a/AlientAttack.MonoGame/Things/Weapons/Weapon.cs b/AlientAttack.MonoGame/Things/Weapons/Weapon.cs index 6ed6a53..9a2e8db 100644 --- a/AlientAttack.MonoGame/Things/Weapons/Weapon.cs +++ b/AlientAttack.MonoGame/Things/Weapons/Weapon.cs @@ -1,6 +1,12 @@ -namespace AlienAttack.MonoGame.Things.Weapons; +using AlienAttack.MonoGame.Things.Bullets; +using AlienAttack.MonoGame.Things.Muzzles; +using Microsoft.Xna.Framework; +using System; +using System.Linq; -public abstract class Weapon : IWeapon +namespace AlienAttack.MonoGame.Things.Weapons; + +internal abstract class Weapon : IWeapon { public abstract int FireThreshold { get; } public int CurrentFireThreshold { get; private set; } @@ -23,4 +29,59 @@ public abstract class Weapon : IWeapon } public abstract void Fire(Sprite owner, SpriteUpdateContext context); + + protected static void FireBullet(FireBulletContext context) + { + var (x, y) = ComputeAnchorPosition(context); + + foreach (Shot shot in context.Shots) + { + Bullet bullet = context.Factory(x + shot.OffsetX, y + shot.OffsetY, shot.XVelocity, shot.YVelocity, context.Owner); + context.SpriteUpdateContext.SpawnSprite(bullet); + } + } + + private static (float x, float y) ComputeAnchorPosition(FireBulletContext context) + { + Sprite owner = context.Owner; + int bw = context.BulletWidth; + int bh = context.BulletHeight; + Anchor anchor = context.Anchor; + + float left = owner.XPosition; + float top = owner.YPosition; + float right = owner.XPosition + owner.BoundBox.Width; + float bottom = owner.YPosition + owner.BoundBox.Height; + + return anchor switch + { + Anchor.TopLeft => (left, top - bh), + Anchor.TopCenter => (left + (owner.BoundBox.Width - bw) / 2f, top - bh), + Anchor.TopRight => (right - bw, top - bh), + + Anchor.Center => (left + (owner.BoundBox.Width - bw) / 2f, top + (owner.BoundBox.Height - bh) / 2f), + + Anchor.BottomLeft => (left, bottom), + Anchor.BottomCenter => (left + (owner.BoundBox.Width - bw) / 2f, bottom), + Anchor.BottomRight => (right - bw, bottom), + + _ => (left, top) + }; + } + + protected static void FireFromMuzzle(FireBulletFromMuzzleContext context) + { + if (context.Owner is not Player player) + return; + + foreach (MuzzleShot shot in context.Shots) + { + var m = player.Muzzles.First(x => x.Id == shot.Muzzle).LocalPx; + Vector2 worldMuzzle = MuzzleMath.GetMuzzleWorld(player, m); + Vector2 bulletPos = MuzzleMath.CenterBulletOn(worldMuzzle, context.BulletWidth, context.BulletHeight) + context.ExtraOffset; + + Bullet bullet = context.Factory(bulletPos.X, bulletPos.Y, shot.XVelocity, shot.YVelocity, context.Owner); + context.SpriteUpdateContext.SpawnSprite(bullet); + } + } } \ No newline at end of file