Added configurable weapons.

This commit is contained in:
2026-01-03 21:49:27 -05:00
parent 79fd63c3ce
commit e8e31bb143
37 changed files with 332 additions and 88 deletions

View File

@@ -11,7 +11,7 @@ public record DrawHudContext : DrawContext
public int Shield;
}
internal class Hud
public class Hud
{
private Texture2D _pixel;

View File

@@ -1,8 +1,11 @@
using AlienAttack.MonoGame.Things.Items;
using AlienAttack.MonoGame.Things.Muzzles;
using Microsoft.Xna.Framework;
using System;
namespace AlienAttack.MonoGame.Things.Bullets;
internal class Bullet(float x, float y, float xVel, float yVel, Sprite owner) : Sprite(x, y)
public class Bullet(float x, float y, float xVel, float yVel, Sprite owner) : Sprite(x, y)
{
protected float XVelocity = xVel;
protected float YVelocity = yVel;
@@ -32,7 +35,9 @@ internal class Bullet(float x, float y, float xVel, float yVel, Sprite owner) :
public override void OnCollision(SpriteCollisionContext context)
{
if (context.Sprite is Bullet || context.Sprite == Owner || context.Sprite is Item)
Sprite sprite = context.Sprite;
if (sprite is Bullet || sprite == Owner || sprite is Item || sprite is Explosion)
return;
IsDead = true;

View File

@@ -0,0 +1,11 @@
using System;
namespace AlienAttack.MonoGame.Things.Bullets;
public sealed class BulletDef
{
public required BulletKind Kind { get; init; }
public required int Width { get; init; }
public required int Height { get; init; }
public required Func<float, float, float, float, Sprite, Bullet> Create { get; init; }
}

View File

@@ -0,0 +1,100 @@
namespace AlienAttack.MonoGame.Things.Bullets;
public static class BulletDefinitions
{
public static readonly BulletDef LaserSmall = new()
{
Kind = BulletKind.LaserSmall,
Width = LaserBulletSmall.Width,
Height = LaserBulletSmall.Height,
Create = (x, y, vx, vy, o) => new LaserBulletSmall(x, y, vx, vy, o),
};
public static readonly BulletDef LaserMedium = new()
{
Kind = BulletKind.LaserMedium,
Width = LaserBulletMedium.Width,
Height = LaserBulletMedium.Height,
Create = (x, y, vx, vy, o) => new LaserBulletMedium(x, y, vx, vy, o),
};
public static readonly BulletDef LaserLarge = new()
{
Kind = BulletKind.LaserLarge,
Width = LaserBulletLarge.Width,
Height = LaserBulletLarge.Height,
Create = (x, y, vx, vy, o) => new LaserBulletLarge(x, y, vx, vy, o),
};
public static readonly BulletDef MinigunSmall = new()
{
Kind = BulletKind.MinigunSmall,
Width = MinigunBulletSmall.Width,
Height = MinigunBulletSmall.Height,
Create = (x, y, vx, vy, o) => new MinigunBulletSmall(x, y, vx, vy, o),
};
public static readonly BulletDef MinigunMedium = new()
{
Kind = BulletKind.MinigunMedium,
Width = MinigunBulletMedium.Width,
Height = MinigunBulletMedium.Height,
Create = (x, y, vx, vy, o) => new MinigunBulletMedium(x, y, vx, vy, o),
};
public static readonly BulletDef MinigunLarge = new()
{
Kind = BulletKind.MinigunLarge,
Width = MinigunBulletLarge.Width,
Height = MinigunBulletLarge.Height,
Create = (x, y, vx, vy, o) => new MinigunBulletLarge(x, y, vx, vy, o),
};
public static readonly BulletDef PlasmaSmall = new()
{
Kind = BulletKind.PlasmaSmall,
Width = PlasmaBulletSmall.Width,
Height = PlasmaBulletSmall.Height,
Create = (x, y, vx, vy, o) => new PlasmaBulletSmall(x, y, vx, vy, o),
};
public static readonly BulletDef PlasmaMedium = new()
{
Kind = BulletKind.PlasmaMedium,
Width = PlasmaBulletMedium.Width,
Height = PlasmaBulletMedium.Height,
Create = (x, y, vx, vy, o) => new PlasmaBulletMedium(x, y, vx, vy, o),
};
public static readonly BulletDef PlasmaLarge = new()
{
Kind = BulletKind.PlasmaLarge,
Width = PlasmaBulletLarge.Width,
Height = PlasmaBulletLarge.Height,
Create = (x, y, vx, vy, o) => new PlasmaBulletLarge(x, y, vx, vy, o),
};
public static readonly BulletDef ProtonSmall = new()
{
Kind = BulletKind.ProtonSmall,
Width = ProtonBulletSmall.Width,
Height = ProtonBulletSmall.Height,
Create = (x, y, vx, vy, o) => new ProtonBulletSmall(x, y, vx, vy, o),
};
public static readonly BulletDef ProtonMedium = new()
{
Kind = BulletKind.ProtonMedium,
Width = ProtonBulletMedium.Width,
Height = ProtonBulletMedium.Height,
Create = (x, y, vx, vy, o) => new ProtonBulletMedium(x, y, vx, vy, o),
};
public static readonly BulletDef ProtonLarge = new()
{
Kind = BulletKind.ProtonLarge,
Width = ProtonBulletLarge.Width,
Height = ProtonBulletLarge.Height,
Create = (x, y, vx, vy, o) => new ProtonBulletLarge(x, y, vx, vy, o),
};
}

View File

@@ -4,7 +4,7 @@ using System;
namespace AlienAttack.MonoGame.Things.Bullets;
internal class LaserBulletLarge : Bullet
public class LaserBulletLarge : Bullet
{
public const int Width = 8;
public const int Height = 28;

View File

@@ -4,7 +4,7 @@ using System;
namespace AlienAttack.MonoGame.Things.Bullets;
internal class LaserBulletMedium : Bullet
public class LaserBulletMedium : Bullet
{
public const int Width = 7;
public const int Height = 21;

View File

@@ -4,7 +4,7 @@ using System;
namespace AlienAttack.MonoGame.Things.Bullets;
internal class LaserBulletSmall : Bullet
public class LaserBulletSmall : Bullet
{
public const int Width = 6;
public const int Height = 18;

View File

@@ -4,7 +4,7 @@ using System;
namespace AlienAttack.MonoGame.Things.Bullets;
internal class MinigunBulletLarge : Bullet
public class MinigunBulletLarge : Bullet
{
public const int Width = 9;
public const int Height = 27;

View File

@@ -4,7 +4,7 @@ using System;
namespace AlienAttack.MonoGame.Things.Bullets;
internal class MinigunBulletMedium : Bullet
public class MinigunBulletMedium : Bullet
{
public const int Width = 11;
public const int Height = 21;

View File

@@ -4,7 +4,7 @@ using System;
namespace AlienAttack.MonoGame.Things.Bullets;
internal class MinigunBulletSmall : Bullet
public class MinigunBulletSmall : Bullet
{
public const int Width = 11;
public const int Height = 21;

View File

@@ -4,7 +4,7 @@ using System;
namespace AlienAttack.MonoGame.Things.Bullets;
internal class PlasmaBulletLarge : Bullet
public class PlasmaBulletLarge : Bullet
{
public const int Width = 9;
public const int Height = 31;

View File

@@ -4,7 +4,7 @@ using System;
namespace AlienAttack.MonoGame.Things.Bullets;
internal class PlasmaBulletMedium : Bullet
public class PlasmaBulletMedium : Bullet
{
public const int Width = 8;
public const int Height = 26;

View File

@@ -4,7 +4,7 @@ using System;
namespace AlienAttack.MonoGame.Things.Bullets;
internal class PlasmaBulletSmall : Bullet
public class PlasmaBulletSmall : Bullet
{
public const int Width = 7;
public const int Height = 24;

View File

@@ -4,7 +4,7 @@ using System;
namespace AlienAttack.MonoGame.Things.Bullets;
internal class ProtonBulletLarge : Bullet
public class ProtonBulletLarge : Bullet
{
public const int Width = 13;
public const int Height = 13;

View File

@@ -4,7 +4,7 @@ using System;
namespace AlienAttack.MonoGame.Things.Bullets;
internal class ProtonBulletMedium : Bullet
public class ProtonBulletMedium : Bullet
{
public const int Width = 10;
public const int Height = 10;

View File

@@ -4,7 +4,7 @@ using System;
namespace AlienAttack.MonoGame.Things.Bullets;
internal class ProtonBulletSmall : Bullet
public class ProtonBulletSmall : Bullet
{
public const int Width = 6;
public const int Height = 6;

View File

@@ -2,16 +2,12 @@
using AlienAttack.MonoGame.Things.Items;
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 static System.Runtime.InteropServices.JavaScript.JSType;
namespace AlienAttack.MonoGame.Things.Enemies;
internal class GreenEnemy : EnemyShip
public class GreenEnemy : EnemyShip
{
//Enemy01_Green_Frame_1_png_processed

View File

@@ -1,11 +1,10 @@
using AlienAttack.MonoGame.Things.Bullets;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
namespace AlienAttack.MonoGame.Things.Enemies;
internal class RedEnemy : EnemyShip
public class RedEnemy : EnemyShip
{
protected int FireThreshold => 20;
protected int CurrentFireThreshold { get; set; } = 20;

View File

@@ -1,11 +1,10 @@
using AlienAttack.MonoGame.Things.Bullets;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
namespace AlienAttack.MonoGame.Things.Enemies;
internal class TealEnemy : EnemyShip
public class TealEnemy : EnemyShip
{
protected int FireThreshold => 20;
protected int CurrentFireThreshold { get; set; } = 20;

View File

@@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Graphics;
namespace AlienAttack.MonoGame.Things;
internal class Explosion(int x, int y, float xVel, float yVel) : Sprite(x, y)
public class Explosion(int x, int y, float xVel, float yVel) : Sprite(x, y)
{
protected int CurrentFrame { get; private set; } = 1;
protected int MaxFrames => 9;

View File

@@ -1,6 +1,6 @@
namespace AlienAttack.MonoGame.Things.Items;
internal class Ammo : Item
public class Ammo : Item
{
protected override PickupKind Kind => PickupKind.Ammo;
@@ -8,9 +8,4 @@ internal class Ammo : Item
{
TextureName = @$"Sprites\Powerup_Ammo";
}
protected override void ApplyEffect(Player player)
{
}
}

View File

@@ -1,6 +1,6 @@
namespace AlienAttack.MonoGame.Things.Items;
internal class Energy : Item
public class Energy : Item
{
protected override PickupKind Kind => PickupKind.Plasma;
@@ -8,9 +8,4 @@ internal class Energy : Item
{
TextureName = @$"Sprites\Powerup_Energy";
}
protected override void ApplyEffect(Player player)
{
}
}

View File

@@ -1,6 +1,6 @@
namespace AlienAttack.MonoGame.Things.Items;
internal class Health : Item
public class Health : Item
{
protected override PickupKind Kind => PickupKind.Health;
@@ -8,9 +8,4 @@ internal class Health : Item
{
TextureName = @$"Sprites\Powerup_Health";
}
protected override void ApplyEffect(Player player)
{
}
}

View File

@@ -4,7 +4,7 @@ using System;
namespace AlienAttack.MonoGame.Things.Items;
internal abstract class Item : MoveableSprite
public abstract class Item : MoveableSprite
{
private Vector2 _anchor; // the "center" the item orbits around
private float _t; // radians
@@ -59,13 +59,10 @@ internal abstract class Item : MoveableSprite
public override void OnCollision(SpriteCollisionContext context)
{
if (context.Sprite is Player player)
if (context.Sprite is Player)
{
IsDead = true;
ApplyEffect(player);
context.AudioManager.PlayPickup(Kind);
}
}
protected abstract void ApplyEffect(Player player);
}

View File

@@ -1,6 +1,6 @@
namespace AlienAttack.MonoGame.Things.Items;
internal class Rockets : Item
public class Rockets : Item
{
protected override PickupKind Kind => PickupKind.Rockets;
@@ -8,9 +8,4 @@ internal class Rockets : Item
{
TextureName = @$"Sprites\Powerup_Rockets";
}
protected override void ApplyEffect(Player player)
{
}
}

View File

@@ -1,6 +1,6 @@
namespace AlienAttack.MonoGame.Things.Items;
internal class Shields : Item
public class Shields : Item
{
protected override PickupKind Kind => PickupKind.Shield;
@@ -8,9 +8,4 @@ internal class Shields : Item
{
TextureName = @$"Sprites\Powerup_Shields";
}
protected override void ApplyEffect(Player player)
{
}
}

View File

@@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Graphics;
namespace AlienAttack.MonoGame.Things;
internal class MiniExplosion(int x, int y, float xVel, float yVel) : Sprite(x, y)
public class MiniExplosion(int x, int y, float xVel, float yVel) : Sprite(x, y)
{
protected int CurrentFrame { get; private set; } = 1;
protected int MaxFrames => 9;

View File

@@ -8,10 +8,11 @@ using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
namespace AlienAttack.MonoGame.Things;
internal enum PlayerHorizontalMoveState
public enum PlayerHorizontalMoveState
{
None,
Left,
@@ -19,7 +20,7 @@ internal enum PlayerHorizontalMoveState
}
[Flags]
internal enum MoveFlag
public enum MoveFlag
{
None = 0,
Left = 1,
@@ -34,7 +35,7 @@ public class DrwaState
}
internal class Player : MoveableSprite
public class Player : MoveableSprite
{
protected PlayerHorizontalMoveState MoveState = PlayerHorizontalMoveState.None;
protected MoveFlag MoveFlags;
@@ -61,10 +62,64 @@ internal class Player : MoveableSprite
{
BoundBox = new Rectangle(0, 0, 64, 64);
//ActiveWeapons.Add(new Minigun());
ActiveWeapons.Add(new MinigunTripleSpreadFast());
//ActiveWeapons.Add(new MinigunTripleSpreadFast());
//ActiveWeapons.Add(new MinigunSpread());
Health = 100;
Shield = 100;
// Weapon 1
WeaponDef weaponDef = new()
{
Name = "Weapon 1",
Bullet = BulletDefinitions.LaserLarge,
Pattern = ShotPatterns.TripleSpread,
FireSfxKey = ""
};
WeaponState weasponState = new()
{
FireThreshold = 15,
SpeedMultiplier = 1,
DamageBonus = 0
};
ConfigurableWeapon configurableWeapon = new(weaponDef, weasponState);
// Weapon 2
//WeaponDef weaponDef2 = new()
//{
// Name = "Weapon 2",
// Bullet = BulletDefinitions.MinigunSmall,
// Pattern = ShotPatterns.Single,
// FireSfxKey = ""
//};
//WeaponState weasponState2 = new()
//{
// FireThreshold = 20,
// SpeedMultiplier = 2f,
// DamageBonus = 0
//};
//ConfigurableWeapon configurableWeapon2 = new(weaponDef2, weasponState2);
ActiveWeapons.Add(configurableWeapon);
//ActiveWeapons.Add(configurableWeapon2);
}
public Vector2 GetMuzzleLocal(MuzzleId muzzle)
{
switch (muzzle)
{
case MuzzleId.Center:
return Muzzles[0].LocalPx;
case MuzzleId.Left:
return Muzzles[1].LocalPx;
case MuzzleId.Right:
return Muzzles[2].LocalPx;
}
return new();
}
//Texture2D texture = Game

View File

@@ -0,0 +1,36 @@
using AlienAttack.MonoGame.Things.Muzzles;
namespace AlienAttack.MonoGame.Things.Weapons;
public class ConfigurableWeapon(WeaponDef def, WeaponState state) : Weapon
{
public override int FireThreshold => state.FireThreshold;
public override void Fire(Sprite owner, SpriteUpdateContext context)
{
if (owner is not Player player) return;
foreach (var shot in def.Pattern.Shots)
{
// muzzle local -> world
var muzzleLocal = player.GetMuzzleLocal(shot.Muzzle); // make a fast lookup
var muzzleWorld = MuzzleMath.GetMuzzleWorld(player, muzzleLocal);
// speed upgrade
float vx = shot.Velocity.X * state.SpeedMultiplier;
float vy = shot.Velocity.Y * state.SpeedMultiplier;
// center bullet on muzzle + pattern offset
var pos = MuzzleMath.CenterBulletOn(muzzleWorld, def.Bullet.Width, def.Bullet.Height) + shot.Offset;
var bullet = def.Bullet.Create(pos.X, pos.Y, vx, vy, owner);
// optional: apply damage bonus generically
//bullet.Damage += state.DamageBonus;
context.SpawnSprite(bullet);
}
context.AudioManager.PlayPlayerFire(); // or use _def.FireSfxKey -> audio routing
}
}

View File

@@ -5,7 +5,7 @@ using System;
namespace AlienAttack.MonoGame.Things.Weapons;
internal record FireBulletContext
public record FireBulletContext
{
public Sprite Owner { get; init; }
public SpriteUpdateContext SpriteUpdateContext { get; init; }
@@ -24,7 +24,7 @@ public record Shot
public float YVelocity { get; init; }
}
internal record FireBulletFromMuzzleContext
public record FireBulletFromMuzzleContext
{
public Sprite Owner { get; init; }
public SpriteUpdateContext SpriteUpdateContext { get; init; }

View File

@@ -3,7 +3,7 @@ using AlienAttack.MonoGame.Things.Muzzles;
namespace AlienAttack.MonoGame.Things.Weapons;
internal class Minigun : Weapon
public class Minigun : Weapon
{
public override int FireThreshold => 15;
@@ -26,7 +26,7 @@ internal class Minigun : Weapon
}
}
internal abstract class MinigunSingle : Weapon
public abstract class MinigunSingle : Weapon
{
public override void Fire(Sprite owner, SpriteUpdateContext context)
{
@@ -50,23 +50,23 @@ internal abstract class MinigunSingle : Weapon
}
}
internal class MinigunSingleSlow : MinigunSingle
public class MinigunSingleSlow : MinigunSingle
{
public override int FireThreshold => 20;
}
internal class MinigunSingleAverage : MinigunSingle
public class MinigunSingleAverage : MinigunSingle
{
public override int FireThreshold => 15;
}
internal class MinigunSingleFast : MinigunSingle
public class MinigunSingleFast : MinigunSingle
{
public override int FireThreshold => 10;
}
internal abstract class MinigunDouble : Weapon
public abstract class MinigunDouble : Weapon
{
public override void Fire(Sprite owner, SpriteUpdateContext context)
{
@@ -91,23 +91,22 @@ internal abstract class MinigunDouble : Weapon
}
}
internal class MinigunDoubleSlow : MinigunDouble
public class MinigunDoubleSlow : MinigunDouble
{
public override int FireThreshold => 30;
}
internal class MinigunDoubleAverage : MinigunDouble
public class MinigunDoubleAverage : MinigunDouble
{
public override int FireThreshold => 20;
}
internal class MinigunDoubleFast : MinigunDouble
public class MinigunDoubleFast : MinigunDouble
{
public override int FireThreshold => 15;
}
internal abstract class MinigunTriple : Weapon
public abstract class MinigunTriple : Weapon
{
public override void Fire(Sprite owner, SpriteUpdateContext context)
{
@@ -133,23 +132,22 @@ internal abstract class MinigunTriple : Weapon
}
}
internal class MinigunTripleSlow : MinigunTriple
public class MinigunTripleSlow : MinigunTriple
{
public override int FireThreshold => 30;
}
internal class MinigunTripleAverage : MinigunTriple
public class MinigunTripleAverage : MinigunTriple
{
public override int FireThreshold => 20;
}
internal class MinigunTripleFast : MinigunTriple
public class MinigunTripleFast : MinigunTriple
{
public override int FireThreshold => 15;
}
internal abstract class MinigunTripleSpread : Weapon
public abstract class MinigunTripleSpread : Weapon
{
public override void Fire(Sprite owner, SpriteUpdateContext context)
{
@@ -175,18 +173,18 @@ internal abstract class MinigunTripleSpread : Weapon
}
}
internal class MinigunTripleSpreadSlow : MinigunTripleSpread
public class MinigunTripleSpreadSlow : MinigunTripleSpread
{
public override int FireThreshold => 30;
}
internal class MinigunTripleSpreadAverage : MinigunTripleSpread
public class MinigunTripleSpreadAverage : MinigunTripleSpread
{
public override int FireThreshold => 20;
}
internal class MinigunTripleSpreadFast : MinigunTripleSpread
public class MinigunTripleSpreadFast : MinigunTripleSpread
{
public override int FireThreshold => 15;
}

View File

@@ -0,0 +1,6 @@
using AlienAttack.MonoGame.Things.Muzzles;
using Microsoft.Xna.Framework;
namespace AlienAttack.MonoGame.Things.Weapons;
public readonly record struct Shot2(MuzzleId Muzzle, Vector2 Velocity, Vector2 Offset);

View File

@@ -0,0 +1,6 @@
namespace AlienAttack.MonoGame.Things.Weapons;
public sealed class ShotPattern
{
public required Shot2[] Shots { get; init; }
}

View File

@@ -0,0 +1,43 @@
using AlienAttack.MonoGame.Things.Muzzles;
using Microsoft.Xna.Framework;
namespace AlienAttack.MonoGame.Things.Weapons;
public static class ShotPatterns
{
public static readonly ShotPattern Single = new()
{
Shots =
[
new Shot2(MuzzleId.Center, new Vector2(0, -6), new Vector2(4, 0))
]
};
public static readonly ShotPattern Double = new()
{
Shots =
[
new Shot2(MuzzleId.Left, new Vector2(0, -6), new Vector2(4, 0)),
new Shot2(MuzzleId.Right, new Vector2(0, -6), new Vector2(4, 0))
]
};
public static readonly ShotPattern DoubleSpread = new()
{
Shots =
[
new Shot2(MuzzleId.Left, new Vector2(-1, -6), new Vector2(4, 0)),
new Shot2(MuzzleId.Right, new Vector2(1, -6), new Vector2(4, 0))
]
};
public static readonly ShotPattern TripleSpread = new()
{
Shots =
[
new Shot2(MuzzleId.Left, new Vector2(-1, -6), new Vector2(4, 0)),
new Shot2(MuzzleId.Center, new Vector2(0, -6), new Vector2(4, 0)),
new Shot2(MuzzleId.Right, new Vector2(1, -6), new Vector2(4, 0))
]
};
}

View File

@@ -1,12 +1,11 @@
using AlienAttack.MonoGame.Things.Bullets;
using AlienAttack.MonoGame.Things.Muzzles;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
namespace AlienAttack.MonoGame.Things.Weapons;
internal abstract class Weapon : IWeapon
public abstract class Weapon : IWeapon
{
public abstract int FireThreshold { get; }
public int CurrentFireThreshold { get; private set; }

View File

@@ -0,0 +1,11 @@
using AlienAttack.MonoGame.Things.Bullets;
namespace AlienAttack.MonoGame.Things.Weapons;
public sealed class WeaponDef
{
public required string Name { get; init; }
public required ShotPattern Pattern { get; init; }
public required BulletDef Bullet { get; init; }
public required string FireSfxKey { get; init; } = "player_fire";
}

View File

@@ -0,0 +1,8 @@
namespace AlienAttack.MonoGame.Things.Weapons;
public sealed class WeaponState
{
public int FireThreshold { get; set; }
public float SpeedMultiplier { get; set; } = 1f;
public int DamageBonus { get; set; } = 0;
}