Add project files.
This commit is contained in:
570
FreedomFighter/Entities.cs
Normal file
570
FreedomFighter/Entities.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user