Added more sound effects. Added initial weapon muzzle fire logic.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
10
AlientAttack.MonoGame/Things/Items/PickupKind.cs
Normal file
10
AlientAttack.MonoGame/Things/Items/PickupKind.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace AlienAttack.MonoGame.Things.Items;
|
||||
|
||||
public enum PickupKind
|
||||
{
|
||||
Health,
|
||||
Shield,
|
||||
Ammo,
|
||||
Rockets,
|
||||
Plasma
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
9
AlientAttack.MonoGame/Things/Muzzles/Muzzle.cs
Normal file
9
AlientAttack.MonoGame/Things/Muzzles/Muzzle.cs
Normal file
@@ -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;
|
||||
}
|
||||
8
AlientAttack.MonoGame/Things/Muzzles/MuzzleId.cs
Normal file
8
AlientAttack.MonoGame/Things/Muzzles/MuzzleId.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace AlienAttack.MonoGame.Things.Muzzles;
|
||||
|
||||
public enum MuzzleId
|
||||
{
|
||||
Center,
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
20
AlientAttack.MonoGame/Things/Muzzles/MuzzleMath.cs
Normal file
20
AlientAttack.MonoGame/Things/Muzzles/MuzzleMath.cs
Normal file
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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<Muzzle> 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<SoundEffect>(@$"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<SoundEffect>(@$"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);
|
||||
}
|
||||
|
||||
12
AlientAttack.MonoGame/Things/Weapons/Anchor.cs
Normal file
12
AlientAttack.MonoGame/Things/Weapons/Anchor.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace AlienAttack.MonoGame.Things.Weapons;
|
||||
|
||||
public enum Anchor
|
||||
{
|
||||
TopLeft,
|
||||
TopCenter,
|
||||
TopRight,
|
||||
Center,
|
||||
BottomLeft,
|
||||
BottomCenter,
|
||||
BottomRight
|
||||
}
|
||||
43
AlientAttack.MonoGame/Things/Weapons/FireBulletContext.cs
Normal file
43
AlientAttack.MonoGame/Things/Weapons/FireBulletContext.cs
Normal file
@@ -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<float, float, float, float, Sprite, Bullet> 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<float, float, float, float, Sprite, Bullet> Factory { get; init; }
|
||||
}
|
||||
|
||||
public record MuzzleShot
|
||||
{
|
||||
public MuzzleId Muzzle { get; init; }
|
||||
public float XVelocity { get; init; }
|
||||
public float YVelocity { get; init; }
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user