Add project files.

This commit is contained in:
2026-09-06 18:01:31 -04:00
parent 0142942887
commit ebcfd9cd39
93 changed files with 3332 additions and 0 deletions

570
FreedomFighter/Entities.cs Normal file
View File

@@ -0,0 +1,570 @@
using System;
using Microsoft.Xna.Framework.Graphics;
namespace FreedomFighter;
// Entity classes ported 1:1 from GameU.pas, including quirks
// (e.g. TEnemy moves inside Draw, missiles can target stray sprites).
public class Player : Sprite
{
public int Health;
public int FireRate;
public int Power;
public Weapon Weapon;
public Player(SpriteManager m) : base(m) { }
public override void Move(float moveCount)
{
var input = G.Input;
if (input.Button5Clicked) { G.Music.Songs[6].Play(); Weapon = Weapon.Reg; }
if (input.Button6Clicked) { G.Music.Songs[6].Play(); Weapon = Weapon.Super; }
if (input.Button7Clicked) { G.Music.Songs[6].Play(); Weapon = Weapon.Multi; }
if (FireRate > 1)
FireRate--;
if (input.Button4 && FireRate == 1 && Weapon == Weapon.Reg)
Bullet.Spawn(X + Width / 2 - 16, Y - 34, 0, 3, 0);
else if (input.Button4 && FireRate == 1 && Weapon == Weapon.Super)
{
Bullet.Spawn(X + Width / 2 - 16 - 12, Y - 34, 0, 3, 0);
Bullet.Spawn(X + Width / 2 - 16 + 12, Y - 34, 0, 3, 0);
}
if (input.Button4 && FireRate == 1 && Weapon == Weapon.Multi)
{
Bullet.Spawn(X + Width / 2 - 16, Y - 34, 2, 3, -35);
Bullet.Spawn(X + Width / 2 - 16, Y - 34, 0, 3, 0);
Bullet.Spawn(X + Width / 2 - 16, Y - 34, -2, 3, 35);
}
if (input.Button8 && FireRate == 1)
{
G.Music.Songs[13].Play();
Missile.Spawn(X + Width / 2, Y, 2);
FireRate = 20;
}
if (input.Left && input.Up) { X -= 3; Y -= 3; }
else if (input.Left && input.Down) { X -= 3; Y += 3; }
else if (input.Right && input.Up) { X += 3; Y -= 3; }
else if (input.Right && input.Down) { X += 3; Y += 3; }
else if (input.Left) X -= 3;
else if (input.Right) X += 3;
else if (input.Up) Y -= 3;
else if (input.Down) Y += 3;
if (X + Width > 800) X = 800 - Width;
if (X < 0) X = 0;
if (Y + Height > 600) Y = 600 - Height;
if (Y < 0) Y = 0;
}
}
public class DemoShip : Sprite
{
public DemoShip(SpriteManager m) : base(m) { }
}
public class Enemy : Sprite
{
public float YVel;
public int Health;
public string MoveDir = "";
public AI AI;
public bool Charging;
public int HitTimer;
public int FireRate;
public int ExplodeTime;
public int AnimTicks;
public Enemy(SpriteManager m) : base(m) { }
public override void Move(float moveCount)
{
if (HitTimer < 255)
{
HitTimer += 15;
SetBlue(HitTimer);
SetGreen(HitTimer);
}
switch (AI)
{
case AI.Normal:
case AI.Hunt:
if (G.Random(1000) < 5)
EnemyBullet.Spawn(X, Y, 3, 0);
break;
case AI.Tactical:
Y += YVel;
if (!Charging && Y > 200)
{
YVel = 1;
G.TFCounter++;
if (G.TFCounter > 16)
{
G.TFCounter = 0;
if (ImageIndex < 4)
ImageIndex++;
else
{
Charging = true;
YVel = -2;
}
}
}
if (Charging && Y < -100)
{
ImageIndex = 0;
YVel = 5;
}
break;
case AI.Mine:
if (AnimTicks > ExplodeTime)
{
Kill();
Explosion.Spawn(X, Y, false, false);
for (int angle = 360; angle >= 0; angle -= 45)
EnemyBullet.Spawn(X, Y, 2, angle);
}
break;
case AI.Turret:
if (FireRate < 0)
{
EnemyBullet.Spawn(X + 16, Y + 16, 3, Rotation);
FireRate = 40;
}
break;
}
if (Y > G.ScreenHeight)
Kill();
}
public override void OnCollision(Sprite sprite, int colX, int colY)
{
if (sprite is Player)
{
G.Music.Songs[11].Play();
Kill();
G.Player.Health--;
Explosion.Spawn(X, Y, false, false);
}
if (sprite is Bullet)
{
Health--;
sprite.Kill();
if (Health < 1)
{
Explosion.Spawn(X, Y, false, false);
G.Music.Songs[11].Play();
Kill();
G.ShipsKilled++;
if (G.Player.Power < 18) G.Player.Power++;
G.Score += 10;
}
else
{
HitTimer = 0;
G.Music.Songs[10].Volume = 60;
G.Music.Songs[10].Play();
}
}
if (sprite is Missile)
{
Health -= 5;
sprite.Kill();
G.Music.Songs[10].Volume = 60;
G.Music.Songs[10].Play();
if (Health < 1)
{
Explosion.Spawn(X, Y, false, false);
G.Music.Songs[11].Play();
Kill();
G.ShipsKilled++;
if (G.Player.Power < 18) G.Player.Power++;
G.Score += 10;
}
else
HitTimer = 0;
}
}
// The original moved enemies inside Draw; preserved for identical behavior.
public override void Draw(SpriteBatch sb)
{
base.Draw(sb);
switch (AI)
{
case AI.Normal:
case AI.None:
Y += YVel;
break;
case AI.Hunt:
Y += YVel;
if (Y > 200)
{
Y += YVel; // double speed
if (G.Player.X > X) X++;
if (G.Player.X < X) X--;
}
break;
case AI.Mine:
Y += 1.5f;
Rotation += 0.5f;
if (Rotation > 359) Rotation = 0;
AnimTicks++;
break;
case AI.Turret:
Rotation = (float)(180 / Math.PI * Math.Atan2(Y - G.Player.Y, X - G.Player.X)) + 90;
if (Rotation > 360) Rotation -= 360;
else if (Rotation < 0) Rotation += 360;
FireRate--;
Y += YVel;
break;
}
if (Y > G.ScreenHeight)
Kill();
}
}
public class Boss : Sprite
{
public float YVel;
public int Health;
public string MoveDir = "";
public int HitTimer;
public AI AI;
public int Timer;
public int RandX;
public Boss(SpriteManager m) : base(m) { }
public override void Move(float moveCount)
{
if (HitTimer < 255)
{
HitTimer += 15;
SetBlue(HitTimer);
SetGreen(HitTimer);
}
if (AI == AI.Boss)
{
if (G.Random(1000) < 10)
{
EnemyBullet.Spawn(X, Y + 40, 3, 0);
EnemyBullet.Spawn(X + 32, Y + 40, 3, 0);
EnemyBullet.Spawn(X + 64, Y + 40, 3, 0);
}
Y += YVel;
if (Y > 300)
{
Y = 300;
if (MoveDir == "right") X++;
if (MoveDir == "left") X--;
if (X > G.ScreenWidth - 98 && MoveDir == "right")
{
X = G.ScreenWidth - 98;
MoveDir = "left";
}
if (X < 0 && MoveDir == "left")
{
MoveDir = "right";
X = 0;
}
}
}
else if (AI == AI.Boss2)
{
Timer++;
if (Timer > 200)
{
RandX = G.Random(10);
Timer = 0;
}
if (MoveDir == "down" && Timer % 25 == 0)
{
EnemyBullet.Spawn(X + 40, Y + Height, 3, 0);
EnemyBullet.Spawn(X + 50, Y + Height, 3, 0);
EnemyBullet.Spawn(X + 60, Y + Height, 3, 0);
EnemyBullet.Spawn(X + 70, Y + Height, 3, 0);
}
if (RandX > 7) X++;
else if (RandX > 2) { /* hold */ }
else X--;
if (MoveDir == "down") Y++;
if (MoveDir == "up") Y -= 2;
if (Y > 300 && MoveDir == "down") { Y = 300; MoveDir = "up"; }
if (Y < -100 && MoveDir == "up") { MoveDir = "down"; Y = -100; }
if (X > 800 - Width) { X = 800 - Width; RandX = 0; }
if (X < 0) { X = 0; RandX = 8; }
}
}
public override void OnCollision(Sprite sprite, int colX, int colY)
{
if (sprite is Bullet)
{
sprite.Kill();
Health--;
HitTimer = 0;
if (Health == 0)
{
Kill();
Explosion.Spawn(X, Y, true, true);
G.Score += 1000;
}
}
if (sprite is Missile)
{
sprite.Kill();
Health -= 5;
HitTimer = 0;
if (Health < 1)
{
Kill();
Explosion.Spawn(X, Y, true, true);
G.Score += 1000;
}
}
}
}
public class Bullet : Sprite
{
public float XVel, YVel;
public Bullet(SpriteManager m) : base(m) { }
public static void Spawn(float x, float y, float xVel, float yVel, float rotation)
{
var b = new Bullet(G.Sprites)
{
Width = 34,
Height = 34,
X = x,
Y = y,
XVel = xVel,
YVel = yVel,
Rotation = rotation,
Image = G.Images.IL1[2],
ImageIndex = 0,
CenterX = 0.5f,
CenterY = 0.5f,
DoCollision = true,
DoPixelCheck = true,
};
G.Player.FireRate = 20;
}
public override void Move(float moveCount)
{
X -= XVel;
Y -= YVel;
if (Y > 600 || Y < 0 || X > 800 || X < 0)
Kill();
}
}
public class EnemyBullet : Sprite
{
public float XVel, YVel;
public EnemyBullet(SpriteManager m) : base(m) { }
public static void Spawn(float x, float y, float vel, float rotation)
{
double rad = Math.PI / 180 * (rotation + 90);
_ = new EnemyBullet(G.Sprites)
{
Name = "EnemyBullet",
X = x,
Y = y,
Rotation = rotation,
CenterX = 0.5f,
CenterY = 0.5f,
XVel = vel * (float)Math.Cos(rad),
YVel = vel * (float)Math.Sin(rad),
Width = 32,
Height = 32,
Image = G.Images.IL1[8],
ImageIndex = 0,
DoCollision = true,
DoPixelCheck = true,
};
}
public override void Move(float moveCount)
{
X += XVel;
Y += YVel;
if (X > G.ScreenWidth || X < 0 || Y > G.ScreenHeight || Y < 0)
Kill();
}
public override void OnCollision(Sprite sprite, int colX, int colY)
{
if (sprite is Player)
{
Kill();
G.Player.Health--;
}
}
}
public class Missile : Sprite
{
public float XVel, YVel;
public Sprite? Target;
public Missile(SpriteManager m) : base(m) { }
public static void Spawn(float x, float y, float yVel)
{
var missile = new Missile(G.Sprites)
{
Name = "Missile",
X = x,
Y = y,
Rotation = 180,
YVel = yVel,
Width = 16,
Height = 16,
Image = G.Images.IL1[19],
ImageIndex = 0,
DoCollision = true,
DoPixelCheck = true,
};
missile.Target = missile.GetTarget();
}
public Sprite? GetTarget()
{
Sprite? target = null;
float prevDist = 800;
foreach (var s in G.Sprites.Items)
{
float dx = X - s.X;
float dy = Y - s.Y;
float distance = (float)Math.Sqrt(dx * dx + dy * dy);
// faithful to the original: filters by Name only, so unnamed sprites
// (fighters, but also player bullets and explosions) are all fair game
if (distance < prevDist && s.Name != "Player" && s.Name != "EnemyBullet" && s.Name != "Missile")
{
target = s;
prevDist = distance;
}
}
return target;
}
public override void Move(float moveCount)
{
if (Target != null)
{
if (Y > Target.Y)
{
Rotation = (float)(180 / Math.PI * Math.Atan2(Y - Target.Y, X - Target.X)) + 90;
if (Rotation > 360) Rotation -= 360;
else if (Rotation < 0) Rotation += 360;
}
double rad = Math.PI / 180 * (Rotation + 90);
X += (float)Math.Cos(rad);
Y += YVel * (float)Math.Sin(rad);
}
else
{
double rad = Math.PI / 180 * (Rotation + 90);
Y += YVel * (float)Math.Sin(rad);
X += (float)Math.Cos(rad);
Target = GetTarget();
}
if (Y < 0)
Kill();
if (X < 0 || X > G.ScreenWidth)
Kill();
}
}
public class Explosion : Sprite
{
public int MaxFrames;
public int ECounter;
public bool IsBoss;
public Explosion(SpriteManager m) : base(m) { }
public static void Spawn(float x, float y, bool big, bool boss)
{
int frames = big ? 6 : 5;
int imageIdx = big ? 11 : 3;
int tileWidth = big ? 65 : 32;
int tileHeight = big ? 65 : 34;
_ = new Explosion(G.Sprites)
{
X = x,
Y = y,
Width = tileWidth,
Height = tileHeight,
Image = G.Images.IL1[imageIdx],
ImageIndex = 0,
DoCollision = false,
MaxFrames = frames,
ECounter = 0,
IsBoss = boss,
};
}
public override void Move(float moveCount)
{
Y += 1;
ECounter++;
if (ECounter > 6)
{
ImageIndex++;
ECounter = 0;
}
if (ImageIndex > MaxFrames)
{
if (IsBoss)
G.Done = true;
Kill();
}
}
}