968 lines
32 KiB
C#
968 lines
32 KiB
C#
using System;
|
|
using System.IO;
|
|
using Microsoft.Xna.Framework;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
using Microsoft.Xna.Framework.Input;
|
|
|
|
namespace FreedomFighter;
|
|
|
|
/// <summary>
|
|
/// Port of GameU.pas. The original ran all logic inside the OmegaTimer render
|
|
/// callback, so this port keeps that structure: everything happens in Draw(),
|
|
/// paced by a 120 Hz fixed timestep (the original's MaxFPS).
|
|
/// </summary>
|
|
public class Game1 : Game
|
|
{
|
|
readonly GraphicsDeviceManager _graphics;
|
|
SpriteBatch _sb = null!;
|
|
SpriteFont _font12 = null!;
|
|
SpriteFont _font20 = null!;
|
|
RenderTarget2D _virtualScreen = null!; // fixed 800x600 scene, stretched to the window
|
|
|
|
bool _paused;
|
|
string _enteredName = "";
|
|
DemoShip? _demoShip;
|
|
|
|
// debug screenshot support (--shot)
|
|
public string? ShotFile;
|
|
public int ShotFrames = 30;
|
|
public int StartLevel;
|
|
|
|
// FPS measurement (the original drew OmegaTimer1.FPS)
|
|
int _fps, _fpsFrames;
|
|
double _fpsTimer;
|
|
|
|
public Game1()
|
|
{
|
|
_graphics = new GraphicsDeviceManager(this)
|
|
{
|
|
PreferredBackBufferWidth = G.ScreenWidth,
|
|
PreferredBackBufferHeight = G.ScreenHeight,
|
|
SynchronizeWithVerticalRetrace = false,
|
|
};
|
|
Content.RootDirectory = "Content";
|
|
IsMouseVisible = true;
|
|
IsFixedTimeStep = true;
|
|
TargetElapsedTime = TimeSpan.FromSeconds(1 / 120.0);
|
|
Window.Title = "Freedom Fighter";
|
|
Window.AllowUserResizing = true;
|
|
Window.ClientSizeChanged += (_, _) =>
|
|
{
|
|
int w = Window.ClientBounds.Width, h = Window.ClientBounds.Height;
|
|
if (w > 0 && h > 0 &&
|
|
(w != _graphics.PreferredBackBufferWidth || h != _graphics.PreferredBackBufferHeight))
|
|
{
|
|
_graphics.PreferredBackBufferWidth = w;
|
|
_graphics.PreferredBackBufferHeight = h;
|
|
_graphics.ApplyChanges();
|
|
}
|
|
};
|
|
}
|
|
|
|
protected override void LoadContent()
|
|
{
|
|
_sb = new SpriteBatch(GraphicsDevice);
|
|
_font12 = Content.Load<SpriteFont>("Verdana12");
|
|
_font20 = Content.Load<SpriteFont>("Verdana20");
|
|
_virtualScreen = new RenderTarget2D(GraphicsDevice, G.ScreenWidth, G.ScreenHeight);
|
|
|
|
// the Delphi form's Width/Height were the OUTER window size (800x600, sizeable border);
|
|
// as a DPI-unaware app it was also stretched by Windows, so scale by the current DPI
|
|
if (System.Windows.Forms.Control.FromHandle(Window.Handle) is System.Windows.Forms.Form form)
|
|
form.Size = new System.Drawing.Size(
|
|
G.ScreenWidth * form.DeviceDpi / 96, G.ScreenHeight * form.DeviceDpi / 96);
|
|
|
|
string contentDir = Path.Combine(AppContext.BaseDirectory, "Content");
|
|
|
|
G.Game = this;
|
|
G.Sprites = new SpriteManager();
|
|
G.Images = new ImageLists(GraphicsDevice, contentDir);
|
|
G.Music = new MusicManager();
|
|
G.Music.Load(contentDir);
|
|
G.Map = new GameMap(GraphicsDevice, contentDir);
|
|
G.Input = new OmegaInput();
|
|
G.HighScores = new HighScores();
|
|
G.HighScores.Load();
|
|
|
|
// OmegaScreen1Init: bitmap font setup
|
|
G.BitmapFont1 = new BitmapFont(G.Images.IL2[0], "0123456789");
|
|
G.BitmapFont2 = new BitmapFont(G.Images.IL5[0], "ABCDEFGHIJKLMNOPQRSTUVWXYZ ");
|
|
|
|
// FormCreate
|
|
G.GameTextObj = new GameText("", 60, G.BitmapFont2);
|
|
G.BitmapFont2.Red = 0;
|
|
G.BitmapFont2.Green = 200;
|
|
G.BitmapFont2.Blue = 200;
|
|
|
|
G.State = GameState.Idle;
|
|
G.MainMenu = [0, 0, 0, 0, 1];
|
|
G.Prev = 4;
|
|
|
|
G.Camera.Width = G.ScreenWidth;
|
|
G.Camera.Height = G.ScreenHeight;
|
|
|
|
G.Music.Songs[G.MUS_TITLE].Volume = 25;
|
|
G.Music.Songs[G.MUS_TITLE].Play();
|
|
|
|
if (StartLevel > 0) // debug: jump straight into a level
|
|
{
|
|
ResetAll();
|
|
G.Level = StartLevel;
|
|
G.State = GameState.Intermission;
|
|
G.Counter = 495;
|
|
}
|
|
}
|
|
|
|
protected override void Update(GameTime gameTime)
|
|
{
|
|
// all logic runs in Draw, matching the original's timer callback
|
|
base.Update(gameTime);
|
|
}
|
|
|
|
protected override void Draw(GameTime gameTime)
|
|
{
|
|
_fpsFrames++;
|
|
_fpsTimer += gameTime.ElapsedGameTime.TotalSeconds;
|
|
if (_fpsTimer >= 1) { _fps = _fpsFrames; _fpsFrames = 0; _fpsTimer -= 1; }
|
|
|
|
G.Input.Update();
|
|
|
|
if (G.Input.PauseClicked)
|
|
_paused = !_paused;
|
|
|
|
if (!_paused)
|
|
{
|
|
GraphicsDevice.SetRenderTarget(_virtualScreen);
|
|
GraphicsDevice.Clear(Color.Black);
|
|
_sb.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp);
|
|
|
|
G.Sprites.Move(0);
|
|
|
|
switch (G.State)
|
|
{
|
|
case GameState.Intro: DoIntro(); break;
|
|
case GameState.Idle: DrawMenu(); break;
|
|
case GameState.Intermission: DoIntermission(); break;
|
|
case GameState.Playing: GameLoop(); break;
|
|
case GameState.Stats: DoStats(); break;
|
|
case GameState.Shop: DoShop(); break;
|
|
case GameState.GameOver: DoGameOver(); break;
|
|
case GameState.Credits: DoCredits(); break;
|
|
case GameState.HighScore: DoHighScore(); break;
|
|
case GameState.EnterName: DoEnterName(); break;
|
|
}
|
|
|
|
G.Sprites.Collision();
|
|
G.Sprites.RemoveDead();
|
|
G.Sprites.Draw(_sb);
|
|
|
|
_sb.End();
|
|
GraphicsDevice.SetRenderTarget(null);
|
|
}
|
|
|
|
// stretch the 800x600 scene to the window, as DirectX presentation did
|
|
GraphicsDevice.Clear(Color.Black);
|
|
_sb.Begin(SpriteSortMode.Deferred, BlendState.Opaque, SamplerState.LinearClamp);
|
|
_sb.Draw(_virtualScreen, new Rectangle(0, 0,
|
|
GraphicsDevice.PresentationParameters.BackBufferWidth,
|
|
GraphicsDevice.PresentationParameters.BackBufferHeight), Color.White);
|
|
_sb.End();
|
|
|
|
base.Draw(gameTime);
|
|
|
|
if (ShotFile != null && --ShotFrames <= 0)
|
|
{
|
|
SaveScreenshot(ShotFile);
|
|
ShotFile = null;
|
|
Exit();
|
|
}
|
|
}
|
|
|
|
void SaveScreenshot(string file)
|
|
{
|
|
// capture the virtual screen so debug shots are always 800x600
|
|
using var fs = File.Create(file);
|
|
_virtualScreen.SaveAsPng(fs, _virtualScreen.Width, _virtualScreen.Height);
|
|
}
|
|
|
|
// ================================================================ helpers
|
|
|
|
void PrintFps(int x, int y) =>
|
|
_sb.DrawString(_font12, "FPS: " + _fps, new Vector2(x, y), Color.White);
|
|
|
|
void ResetAll()
|
|
{
|
|
G.Change = 0;
|
|
G.Score = 0;
|
|
G.Lives = 3;
|
|
}
|
|
|
|
void ResetPlayer()
|
|
{
|
|
var p = new Player(G.Sprites) { Name = "Player" };
|
|
// original computed X before assigning Width (Width was still 0)
|
|
p.X = G.ScreenWidth / 2 - p.Width;
|
|
p.Y = G.ScreenHeight - 80;
|
|
p.Health = 16;
|
|
p.FireRate = 20;
|
|
p.Width = 73;
|
|
p.Height = 45;
|
|
p.Image = G.Images.IL1[0];
|
|
p.Weapon = Weapon.Reg;
|
|
p.ImageIndex = 0;
|
|
p.DoPixelCheck = true;
|
|
p.DoCollision = true;
|
|
p.CenterX = 0.5f;
|
|
p.CenterY = 0.5f;
|
|
G.Player = p;
|
|
}
|
|
|
|
void ClipCamera(float x, float y, int width, int height)
|
|
{
|
|
if (x < G.Camera.Width / 2f)
|
|
G.NewCamX = 0;
|
|
else
|
|
G.NewCamX = (int)(x + width / 2f - G.Camera.Width / 2f);
|
|
|
|
if (y < G.Camera.Height / 2f)
|
|
G.NewCamY = 0;
|
|
else
|
|
G.NewCamY = (int)(y + height / 2f - G.Camera.Height / 2f);
|
|
|
|
// the min/max clips were commented out in the original, leaving these unconditional
|
|
G.NewCamX = G.Map.WidthCount * 32 - G.Camera.Width;
|
|
G.NewCamY = G.Map.HeightCount * 32 - G.Camera.Height;
|
|
|
|
int frameCamSpeed = 1;
|
|
if (G.NewCamX > G.Camera.X)
|
|
{
|
|
G.Camera.X += frameCamSpeed;
|
|
if (G.NewCamX < G.Camera.X) G.Camera.X = G.NewCamX;
|
|
}
|
|
else if (G.NewCamX < G.Camera.X)
|
|
{
|
|
G.Camera.X -= frameCamSpeed;
|
|
if (G.NewCamX > G.Camera.X) G.Camera.X = G.NewCamX;
|
|
}
|
|
// Y seek was commented out in the original
|
|
}
|
|
|
|
void DrawLayer1()
|
|
{
|
|
var tileset = G.Map.Tileset;
|
|
if (tileset == null) return;
|
|
|
|
for (int a = 0; a <= G.Camera.Width / 32 + 1; a++)
|
|
for (int b = 0; b <= G.Camera.Height / 32 + 1; b++)
|
|
{
|
|
int xid = (a * 32 + G.Camera.X) / 32;
|
|
int yid = (b * 32 + G.Camera.Y) / 32;
|
|
int drawX = xid * 32 - G.Camera.X;
|
|
int drawY = yid * 32 - G.Camera.Y;
|
|
|
|
if (xid < G.Map.WidthCount && yid < G.Map.HeightCount && xid >= 0 && yid >= 0)
|
|
{
|
|
int id = G.Map.Layer1[xid][yid];
|
|
if (id != -1)
|
|
tileset.Draw(_sb, drawX, drawY, 0, 0.5f, 0.5f, 1, 1, 255, 255, 255, 255, id);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MoveSelection(int curSelect)
|
|
{
|
|
G.MainMenu[G.Prev] = 0;
|
|
G.MainMenu[curSelect] = 1;
|
|
G.Prev = curSelect;
|
|
}
|
|
|
|
// ================================================================ spawning
|
|
|
|
void SpawnObject(ObjectType type, float xPos, float yPos)
|
|
{
|
|
switch (type)
|
|
{
|
|
case ObjectType.Fighter:
|
|
_ = new Enemy(G.Sprites)
|
|
{
|
|
X = xPos, Y = yPos, YVel = 1.5f, Width = 34, Height = 34,
|
|
Image = G.Images.IL1[1], ImageIndex = 0,
|
|
DoCollision = true, DoPixelCheck = true,
|
|
AI = AI.Normal, Health = 2, HitTimer = 255,
|
|
};
|
|
break;
|
|
|
|
case ObjectType.Kamikaze:
|
|
_ = new Enemy(G.Sprites)
|
|
{
|
|
X = xPos, Y = yPos, YVel = 2, Width = 32, Height = 32,
|
|
Image = G.Images.IL1[7], ImageIndex = 0,
|
|
DoCollision = true, DoPixelCheck = true,
|
|
AI = AI.Hunt, Health = 3, HitTimer = 255,
|
|
};
|
|
break;
|
|
|
|
case ObjectType.Tactical:
|
|
_ = new Enemy(G.Sprites)
|
|
{
|
|
X = xPos, Y = yPos, YVel = 3, Charging = false, Width = 32, Height = 32,
|
|
Image = G.Images.IL1[15], ImageIndex = 0,
|
|
DoCollision = true, DoPixelCheck = true,
|
|
AI = AI.Tactical, Health = 5, HitTimer = 255,
|
|
};
|
|
break;
|
|
|
|
case ObjectType.Mine:
|
|
_ = new Enemy(G.Sprites)
|
|
{
|
|
X = xPos, Y = yPos, CenterX = 0.5f, CenterY = 0.5f, YVel = 0.5f,
|
|
AnimTicks = 0, ExplodeTime = G.Random(100) + 200, Width = 32, Height = 48,
|
|
Image = G.Images.IL1[17], ImageIndex = 0,
|
|
DoCollision = true, DoPixelCheck = true,
|
|
Health = 10, AI = AI.Mine, MoveDir = "right",
|
|
};
|
|
break;
|
|
|
|
case ObjectType.MissileBase:
|
|
_ = new Enemy(G.Sprites)
|
|
{
|
|
Name = "Missile Base",
|
|
X = xPos, Y = yPos, CenterX = 0.5f, CenterY = 0.5f, YVel = 1,
|
|
Width = 32, Height = 32,
|
|
Image = G.Images.IL1[18], ImageIndex = 1,
|
|
DoCollision = false, DoPixelCheck = true,
|
|
Health = 1, AI = AI.None,
|
|
};
|
|
break;
|
|
|
|
case ObjectType.MissileTurret:
|
|
_ = new Enemy(G.Sprites)
|
|
{
|
|
Name = "Missile Turret",
|
|
X = xPos, Y = yPos, YVel = 1, CenterX = 0.5f, CenterY = 0.5f,
|
|
Width = 32, Height = 32,
|
|
Image = G.Images.IL1[18], ImageIndex = 0,
|
|
DoCollision = true, DoPixelCheck = true,
|
|
Health = 4, FireRate = 1, AI = AI.Turret,
|
|
};
|
|
break;
|
|
|
|
case ObjectType.Scanner:
|
|
_ = new Enemy(G.Sprites)
|
|
{
|
|
Name = "Scanner",
|
|
X = xPos, Y = yPos, YVel = 1, CenterX = 0.5f, CenterY = 0.5f,
|
|
Width = 32, Height = 32,
|
|
Image = G.Images.IL1[20], ImageIndex = 0,
|
|
DoCollision = true, DoPixelCheck = true,
|
|
Health = 10, AI = AI.None,
|
|
};
|
|
break;
|
|
|
|
case ObjectType.Boss:
|
|
_ = new Boss(G.Sprites)
|
|
{
|
|
X = xPos, Y = yPos, YVel = 1, Width = 98, Height = 98,
|
|
Image = G.Images.IL1[9], ImageIndex = 0,
|
|
DoCollision = true, DoPixelCheck = true,
|
|
MoveDir = "right", Health = 50, HitTimer = 255, AI = AI.Boss,
|
|
};
|
|
break;
|
|
|
|
case ObjectType.Boss2:
|
|
_ = new Boss(G.Sprites)
|
|
{
|
|
X = xPos, Y = yPos, YVel = 1, Width = 98, Height = 98,
|
|
Image = G.Images.IL1[13], ImageIndex = 0,
|
|
DoCollision = true, DoPixelCheck = true,
|
|
MoveDir = "down", Health = 200, HitTimer = 255, AI = AI.Boss2,
|
|
Timer = 0, RandX = 4,
|
|
};
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CreateSquad(ObjectType ship, int pattern)
|
|
{
|
|
int randomX = G.Random(G.ScreenWidth - 140) + 64;
|
|
|
|
switch (pattern)
|
|
{
|
|
case 0:
|
|
SpawnObject(ship, randomX, -32);
|
|
SpawnObject(ship, randomX - 32, -64);
|
|
SpawnObject(ship, randomX + 32, -64);
|
|
break;
|
|
case 1:
|
|
SpawnObject(ship, randomX, -32);
|
|
SpawnObject(ship, randomX - 32, -64);
|
|
SpawnObject(ship, randomX + 32, -64);
|
|
SpawnObject(ship, randomX + 64, -96);
|
|
SpawnObject(ship, randomX - 64, -96);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ================================================================ states
|
|
|
|
void DrawMenu()
|
|
{
|
|
PrintFps(10, 10);
|
|
|
|
// water background
|
|
for (int a = 0; a <= G.ScreenWidth / 32; a++)
|
|
for (int b = 0; b <= G.ScreenHeight / 32; b++)
|
|
G.Images.IL1[4].Draw(_sb, a * 32, b * 32, 0, 0, 0, 1, 1, 255, 255, 255, 200, 0);
|
|
|
|
G.Images.IL2[3].Draw(_sb, G.ScreenWidth / 2 - 180, 50, pattern: 0);
|
|
G.Images.IL2[4].Draw(_sb, G.ScreenWidth / 2 - 180, 200, pattern: G.MainMenu[4]);
|
|
G.Images.IL2[5].Draw(_sb, G.ScreenWidth / 2 - 180, 250, pattern: G.MainMenu[3]);
|
|
G.Images.IL2[6].Draw(_sb, G.ScreenWidth / 2 - 180, 300, pattern: G.MainMenu[2]);
|
|
G.Images.IL2[7].Draw(_sb, G.ScreenWidth / 2 - 180, 350, pattern: G.MainMenu[1]);
|
|
G.Images.IL2[8].Draw(_sb, G.ScreenWidth / 2 - 180, 400, pattern: G.MainMenu[0]);
|
|
|
|
var versionSize = _font12.MeasureString(G.Version);
|
|
_sb.DrawString(_font12, G.Version, new Vector2(G.ScreenWidth / 2 - versionSize.X / 2, 100), Color.White);
|
|
|
|
if (G.Input.UpClicked)
|
|
{
|
|
G.Music.Songs[7].Play();
|
|
G.CurSelection++;
|
|
if (G.CurSelection > 4) G.CurSelection = 0;
|
|
if (G.CurSelection < 0) G.CurSelection = 4;
|
|
MoveSelection(G.CurSelection);
|
|
}
|
|
|
|
if (G.Input.DownClicked)
|
|
{
|
|
G.Music.Songs[7].Play();
|
|
G.CurSelection--;
|
|
if (G.CurSelection > 4) G.CurSelection = 0;
|
|
if (G.CurSelection < 0) G.CurSelection = 4;
|
|
MoveSelection(G.CurSelection);
|
|
}
|
|
|
|
if (G.Input.Button3Clicked)
|
|
{
|
|
switch (G.CurSelection)
|
|
{
|
|
case 0:
|
|
Exit();
|
|
break;
|
|
case 1:
|
|
G.Music.Songs[4].Stop();
|
|
G.Music.Songs[9].Play();
|
|
G.Sprites.KillAll();
|
|
G.Sprites.RemoveDead();
|
|
|
|
G.BitmapFont2.Alpha = 0;
|
|
G.Counter = 0;
|
|
G.TextNum = 0;
|
|
|
|
G.TitleText = new GameText("DESIGN AND PROGRAMMING", 30, G.BitmapFont2);
|
|
G.TitleText.SetSpeed(3);
|
|
G.IdText = new GameText("BRIAN BICKNELL", 30, G.BitmapFont2);
|
|
G.IdText.SetSpeed(3);
|
|
G.State = GameState.Credits;
|
|
break;
|
|
case 2:
|
|
G.State = GameState.HighScore;
|
|
break;
|
|
case 3:
|
|
G.Music.Songs[8].Play();
|
|
break;
|
|
case 4:
|
|
G.Sprites.KillAll();
|
|
G.Sprites.RemoveDead();
|
|
ResetAll();
|
|
G.Level = 1;
|
|
G.State = GameState.Intermission;
|
|
G.Music.Songs[4].Stop();
|
|
G.Music.Songs[7].Play();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (G.Input.Button6Clicked) // '2' starts the demo intro
|
|
{
|
|
G.Sprites.KillAll();
|
|
G.Sprites.RemoveDead();
|
|
ResetAll();
|
|
|
|
G.Map.LoadMap("intro.omap");
|
|
G.Camera.Y = G.Map.HeightCount * 32 - G.ScreenHeight;
|
|
|
|
var demoShip = new DemoShip(G.Sprites) { Name = "DemoShip" };
|
|
demoShip.X = G.ScreenWidth / 2 - demoShip.Width; // Width still 0, like the original
|
|
demoShip.Y = G.ScreenHeight;
|
|
demoShip.Width = 73;
|
|
demoShip.Height = 45;
|
|
demoShip.Image = G.Images.IL1[0];
|
|
demoShip.ImageIndex = 0;
|
|
demoShip.DoPixelCheck = true;
|
|
demoShip.DoCollision = true;
|
|
demoShip.CenterX = 0.5f;
|
|
demoShip.CenterY = 0.5f;
|
|
_demoShip = demoShip;
|
|
|
|
G.Music.Songs[4].Stop();
|
|
G.Music.Songs[14].Play();
|
|
|
|
G.TitleText = new GameText("BICK SOFTWARE", 30, G.BitmapFont2);
|
|
G.IdText = new GameText("PRESENTS", 30, G.BitmapFont2);
|
|
G.TextCreated = false;
|
|
G.BossMode = false;
|
|
|
|
G.State = GameState.Intro;
|
|
}
|
|
|
|
if (G.Input.Button2Clicked)
|
|
Exit();
|
|
}
|
|
|
|
void DoIntro()
|
|
{
|
|
if (_demoShip != null && _demoShip.Y > 300)
|
|
_demoShip.Y -= 0.2f;
|
|
|
|
ClipCamera(G.Camera.X, G.Camera.Y, 32, 32);
|
|
DrawLayer1();
|
|
|
|
PrintFps(10, 20);
|
|
|
|
if (!G.TextCreated)
|
|
{
|
|
G.TitleText.SetText("BICK SOFTWARE PRESENTS");
|
|
G.TextCreated = true;
|
|
G.BitmapFont2.Alpha = 0;
|
|
}
|
|
|
|
if (G.Counter == 5)
|
|
{
|
|
G.TitleText.FadeOut(_sb, 200, 200, 500);
|
|
if (G.BitmapFont2.Alpha == 0)
|
|
{
|
|
G.TitleText.SetText("OMEGA SQUADREN");
|
|
G.Counter = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
G.TitleText.FadeIn(_sb, 200, 200, 500);
|
|
if (G.BitmapFont2.Alpha == 255)
|
|
G.Counter = 5;
|
|
}
|
|
|
|
if (G.Music.Songs[14].Position > 36)
|
|
{
|
|
if (G.Random(50) == 0)
|
|
SpawnObject(ObjectType.Fighter, G.Random(G.ScreenWidth), -32);
|
|
}
|
|
|
|
if (G.Camera.Y <= 0)
|
|
{
|
|
G.Camera.Y = 0;
|
|
if (!G.BossMode)
|
|
{
|
|
SpawnObject(ObjectType.Boss, 300, -G.ScreenHeight + 98);
|
|
G.BossMode = true;
|
|
}
|
|
}
|
|
else
|
|
G.Camera.Y--;
|
|
}
|
|
|
|
void DoIntermission()
|
|
{
|
|
G.BossMode = false;
|
|
G.Done = false;
|
|
|
|
if (G.Level > 5)
|
|
{
|
|
G.Music.Songs[12].Stop();
|
|
G.Music.Songs[3].Play();
|
|
G.State = GameState.GameOver;
|
|
}
|
|
|
|
string label = "Level " + G.Level;
|
|
var size = _font20.MeasureString("Level" + G.Level);
|
|
_sb.DrawString(_font20, label, new Vector2(G.ScreenWidth / 2 - size.X / 2, G.ScreenHeight / 2), Color.White);
|
|
|
|
G.Counter++;
|
|
G.ShipsKilled = 0;
|
|
|
|
if (G.Counter > 500)
|
|
{
|
|
switch (G.Level)
|
|
{
|
|
case 1: G.Map.LoadMap("test10.omap"); break;
|
|
case 2: G.Map.LoadMap("test7.omap"); break;
|
|
case 3: G.Map.LoadMap("test8.omap"); break;
|
|
case 4: G.Map.LoadMap("bonus.omap"); break;
|
|
case 5: G.Map.LoadMap("lvl5.omap"); break;
|
|
}
|
|
|
|
G.State = GameState.Playing;
|
|
ResetPlayer();
|
|
G.Camera.Y = G.Map.HeightCount * 32 - G.ScreenHeight;
|
|
|
|
if (G.Level > 1)
|
|
G.Music.Songs[G.Level - 2].Stop();
|
|
|
|
if (G.Level == 1)
|
|
{
|
|
G.Music.Songs[G.MUS_LEVEL1].Volume = 20;
|
|
G.Music.Songs[G.MUS_LEVEL1].Play();
|
|
}
|
|
else if (G.Level == 4)
|
|
G.Music.Songs[12].Play();
|
|
else if (G.Level == 5)
|
|
{
|
|
G.Music.Songs[12].Stop();
|
|
G.Music.Songs[4].Play();
|
|
}
|
|
else
|
|
G.Music.Songs[G.Level - 1].Play();
|
|
}
|
|
}
|
|
|
|
void GameLoop()
|
|
{
|
|
ClipCamera(G.Camera.X, G.Camera.Y, 32, 32);
|
|
DrawLayer1();
|
|
|
|
// level 4: boss chase with a wrapping camera
|
|
if (G.Level == 4)
|
|
{
|
|
if (!G.BossMode)
|
|
{
|
|
SpawnObject(ObjectType.Boss2, 300, -G.ScreenHeight + 98);
|
|
G.BossMode = true;
|
|
}
|
|
|
|
if (G.Camera.Y <= 0)
|
|
G.Camera.Y = G.Map.HeightCount * 32 - G.ScreenHeight;
|
|
else
|
|
G.Camera.Y -= 2;
|
|
}
|
|
|
|
int randomNum = G.Random(500);
|
|
if (randomNum == 0) CreateSquad(ObjectType.Fighter, 0);
|
|
if (randomNum == 1) CreateSquad(ObjectType.Kamikaze, 1);
|
|
if (randomNum == 5) SpawnObject(ObjectType.Mine, G.Random(G.ScreenWidth), -48);
|
|
|
|
if (G.Camera.Y <= 0)
|
|
{
|
|
G.Camera.Y = 0;
|
|
if (!G.BossMode)
|
|
{
|
|
SpawnObject(ObjectType.Boss, 300, -G.ScreenHeight + 98);
|
|
G.BossMode = true;
|
|
}
|
|
}
|
|
else
|
|
G.Camera.Y--;
|
|
|
|
GetStats();
|
|
|
|
G.Counter++;
|
|
if (G.Counter > 10)
|
|
{
|
|
G.Change++;
|
|
if (G.Change > 2) G.Change = 0;
|
|
G.Counter = 0;
|
|
}
|
|
|
|
PrintFps(10, 10);
|
|
_sb.DrawString(_font12, "Sprites: " + G.Sprites.Count, new Vector2(10, 30), Color.White);
|
|
|
|
if (G.Lives < 0)
|
|
{
|
|
G.Sprites.KillAll();
|
|
G.Sprites.RemoveDead();
|
|
G.Music.Songs[G.Level - 1].Stop();
|
|
G.State = GameState.GameOver;
|
|
}
|
|
|
|
if (G.Done)
|
|
{
|
|
G.Sprites.KillAll();
|
|
G.ShipsKilled++;
|
|
G.Level++;
|
|
G.State = GameState.Stats;
|
|
G.Counter = 0;
|
|
}
|
|
|
|
// spawn entities from map triggers at/below the camera
|
|
for (int a = 0; a < G.Map.WidthCount; a++)
|
|
for (int b = Math.Max(0, G.Camera.Y / 32); b < G.Map.HeightCount; b++)
|
|
{
|
|
string? action = G.Map.TriggerActions[a, b];
|
|
if (action == null) continue;
|
|
|
|
int tempX = a * 32;
|
|
int tempY = b - 128; // original quirk: tile row minus 128 pixels
|
|
|
|
switch (action)
|
|
{
|
|
case "fighter":
|
|
SpawnObject(ObjectType.Fighter, tempX, tempY);
|
|
G.Map.TriggerActions[a, b] = null;
|
|
break;
|
|
case "kamikaze":
|
|
SpawnObject(ObjectType.Kamikaze, tempX, tempY);
|
|
G.Map.TriggerActions[a, b] = null;
|
|
break;
|
|
case "tactical":
|
|
SpawnObject(ObjectType.Tactical, tempX, tempY);
|
|
G.Map.TriggerActions[a, b] = null;
|
|
break;
|
|
case "missileturret":
|
|
SpawnObject(ObjectType.MissileTurret, tempX, tempY);
|
|
SpawnObject(ObjectType.MissileBase, tempX, tempY);
|
|
G.Map.TriggerActions[a, b] = null;
|
|
break;
|
|
case "scanner":
|
|
SpawnObject(ObjectType.Scanner, tempX, tempY);
|
|
G.Map.TriggerActions[a, b] = null;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (G.Input.Button2Clicked)
|
|
{
|
|
G.Sprites.KillAll();
|
|
G.Sprites.RemoveDead();
|
|
G.Music.Songs[G.Level - 1].Stop();
|
|
G.Music.Songs[G.MUS_TITLE].Play();
|
|
G.State = GameState.Idle;
|
|
}
|
|
}
|
|
|
|
void GetStats()
|
|
{
|
|
int imageIdx = G.Player.Weapon switch
|
|
{
|
|
Weapon.Reg => 0,
|
|
Weapon.Super => 1,
|
|
Weapon.Multi => 2,
|
|
_ => -1,
|
|
};
|
|
|
|
G.Images.IL3[2].Draw(_sb, 300, 560, pattern: imageIdx);
|
|
|
|
int colWidth = -10;
|
|
for (int i = 0; i < G.Lives; i++)
|
|
{
|
|
G.Images.IL1[0].Draw(_sb, G.ScreenWidth - 800 + colWidth, G.ScreenHeight - 50,
|
|
0, 0.5f, 0.5f, 0.5f, 0.5f, 255, 255, 255, 255, 0);
|
|
colWidth += 30;
|
|
}
|
|
|
|
G.Images.IL1[6].Draw(_sb, 5, 50, pattern: 0);
|
|
G.BitmapFont1.Print(_sb, 75, 50, G.Score.ToString());
|
|
|
|
G.BitmapFont2.Print(_sb, G.ScreenWidth - 150, G.ScreenHeight - 30, "HEALTH");
|
|
G.Images.IL3[0].Draw(_sb, G.ScreenWidth - 150, G.ScreenHeight - 20, pattern: 0);
|
|
|
|
int barWidth = 0;
|
|
for (int i = 0; i < G.Player.Health; i++)
|
|
{
|
|
G.Images.IL3[1].Draw(_sb, G.ScreenWidth - 148 + barWidth, G.ScreenHeight - 18, pattern: 0);
|
|
barWidth += 8;
|
|
}
|
|
|
|
G.BitmapFont2.Print(_sb, G.ScreenWidth / 6, G.ScreenHeight - 30, "POWER");
|
|
G.Images.IL3[0].Draw(_sb, G.ScreenWidth / 6, G.ScreenHeight - 20, pattern: 0);
|
|
|
|
int energyWidth = 0;
|
|
for (int i = 0; i < G.Player.Power; i++)
|
|
{
|
|
G.Images.IL3[3].Draw(_sb, G.ScreenWidth / 6 + 2 + energyWidth, G.ScreenHeight - 18,
|
|
0, 0, 0, 1, 1, 0, 100 + energyWidth, 0, 255, 0);
|
|
energyWidth += 7;
|
|
}
|
|
|
|
if (G.Player.Health < 0)
|
|
{
|
|
G.Player.Kill();
|
|
G.Lives--;
|
|
ResetPlayer();
|
|
}
|
|
}
|
|
|
|
void DoStats()
|
|
{
|
|
if (G.Counter > 10 && G.CurCount < G.ShipsKilled)
|
|
{
|
|
G.Music.Songs[5].Play();
|
|
G.CurCount++;
|
|
G.Counter = 0;
|
|
}
|
|
|
|
if (G.Input.Button1Clicked && G.CurCount != G.ShipsKilled)
|
|
{
|
|
G.CurCount = G.ShipsKilled;
|
|
G.Counter = 0;
|
|
}
|
|
|
|
string text = "Ships Killed: " + G.CurCount;
|
|
var size = _font12.MeasureString(text);
|
|
_sb.DrawString(_font12, text, new Vector2(G.ScreenWidth / 2 - size.X / 2, G.ScreenHeight / 2), Color.White);
|
|
|
|
G.Counter++;
|
|
_sb.DrawString(_font12, G.Counter.ToString(), new Vector2(10, 10), Color.White);
|
|
|
|
if (G.Counter > 500)
|
|
{
|
|
G.Counter = 0;
|
|
G.CurCount = 0;
|
|
G.State = GameState.Shop;
|
|
}
|
|
}
|
|
|
|
void DoShop()
|
|
{
|
|
_sb.DrawString(_font12, "This is the blackmarket shop", new Vector2(10, 10), Color.White);
|
|
_sb.DrawString(_font12, "PRESS [SPACE] TO CONTINUE", new Vector2(10, 550), Color.White);
|
|
|
|
if (G.Input.Button1Clicked)
|
|
{
|
|
G.State = GameState.Intermission;
|
|
G.Music.Songs[G.Level - 2].Stop();
|
|
G.Music.Songs[7].Play();
|
|
}
|
|
}
|
|
|
|
void DoGameOver()
|
|
{
|
|
G.Images.IL2[2].Draw(_sb, G.ScreenWidth / 2 - 49, G.ScreenHeight / 2, pattern: 0);
|
|
G.Images.IL1[6].Draw(_sb, G.ScreenWidth / 2 - 34, G.ScreenHeight / 2 + 100, pattern: 0);
|
|
G.BitmapFont1.Print(_sb, G.ScreenWidth / 2 + 30, G.ScreenHeight / 2 + 100, G.Score.ToString());
|
|
|
|
if (G.Input.Button1Clicked)
|
|
{
|
|
G.Music.Songs[3].Stop();
|
|
G.Music.Songs[9].Play();
|
|
|
|
G.BitmapFont2.Alpha = 0;
|
|
G.Counter = 0;
|
|
G.TextNum = 0;
|
|
|
|
G.TitleText = new GameText("DESIGN AND PROGRAMMING", 30, G.BitmapFont2);
|
|
G.TitleText.SetSpeed(3);
|
|
G.IdText = new GameText("BRIAN BICKNELL", 30, G.BitmapFont2);
|
|
G.IdText.SetSpeed(3);
|
|
|
|
if (G.HighScores.Qualifies(G.Score))
|
|
{
|
|
_enteredName = "";
|
|
G.State = GameState.EnterName; // replaces the Form2 dialog
|
|
}
|
|
else
|
|
G.State = GameState.Credits;
|
|
}
|
|
}
|
|
|
|
void DoCredits()
|
|
{
|
|
G.TitleText.FadeIn(_sb,
|
|
G.ScreenWidth / 2 - G.TitleText.GetText().Length * G.BitmapFont2.CharWidth / 2,
|
|
G.ScreenHeight / 2 - 15, 5);
|
|
|
|
if (G.TitleText.IsDone)
|
|
G.IdText.TypePrint(_sb,
|
|
G.ScreenWidth / 2 - G.IdText.GetText().Length * G.BitmapFont2.CharWidth / 2,
|
|
G.ScreenHeight / 2 + 15);
|
|
|
|
if (G.IdText.IsDone)
|
|
{
|
|
G.Counter++;
|
|
if (G.Counter > 400)
|
|
{
|
|
G.TextNum++;
|
|
switch (G.TextNum)
|
|
{
|
|
case 1:
|
|
G.BitmapFont2.Alpha = 0;
|
|
G.TitleText.SetText("GRAPHICS BY");
|
|
G.IdText.SetText("BRIAN MAZUROWSKI");
|
|
break;
|
|
case 2:
|
|
G.BitmapFont2.Alpha = 0;
|
|
G.TitleText.SetText("MUSIC BY");
|
|
G.IdText.SetText("ADAM HAIRE");
|
|
break;
|
|
case 3:
|
|
G.BitmapFont2.Alpha = 0;
|
|
G.TitleText.SetText("SPECIAL THANKS TO");
|
|
G.IdText.SetText("THE OMEGA TEAM");
|
|
break;
|
|
case 4:
|
|
G.BitmapFont2.Alpha = 0;
|
|
G.TitleText.SetText("THANK YOU FROM");
|
|
G.IdText.SetText("BICK SOFTWARE");
|
|
break;
|
|
case 5:
|
|
G.State = GameState.HighScore;
|
|
break;
|
|
}
|
|
G.Counter = 0;
|
|
}
|
|
}
|
|
|
|
if (G.Input.Button2Clicked)
|
|
{
|
|
G.Music.Songs[9].Stop();
|
|
G.Music.Songs[G.MUS_TITLE].Play();
|
|
G.State = GameState.Idle;
|
|
}
|
|
}
|
|
|
|
void DoHighScore()
|
|
{
|
|
// The original printed via OmegaBitmapFont3, which had no image list assigned;
|
|
// here the letter and number fonts are combined so the table is actually visible.
|
|
int delta = 0;
|
|
int savedAlpha = G.BitmapFont2.Alpha;
|
|
G.BitmapFont2.Alpha = 255;
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
G.BitmapFont2.Print(_sb, 10, 10 + delta, G.HighScores.List[i].Name.ToUpperInvariant());
|
|
G.BitmapFont1.Print(_sb, 200, 10 + delta, G.HighScores.List[i].Score.ToString());
|
|
delta += 20;
|
|
}
|
|
G.BitmapFont2.Alpha = savedAlpha;
|
|
|
|
if (G.Input.Button1Clicked)
|
|
G.State = GameState.Idle;
|
|
}
|
|
|
|
void DoEnterName()
|
|
{
|
|
int savedAlpha = G.BitmapFont2.Alpha;
|
|
G.BitmapFont2.Alpha = 255;
|
|
G.BitmapFont2.Print(_sb, 200, 250, "NEW HIGH SCORE");
|
|
G.BitmapFont2.Print(_sb, 200, 280, "ENTER YOUR NAME");
|
|
G.BitmapFont2.Print(_sb, 200, 320, _enteredName);
|
|
G.BitmapFont2.Alpha = savedAlpha;
|
|
|
|
foreach (var key in G.Input.Current.GetPressedKeys())
|
|
{
|
|
if (!G.Input.Previous.IsKeyUp(key)) continue;
|
|
|
|
if (key >= Keys.A && key <= Keys.Z && _enteredName.Length < 20)
|
|
_enteredName += (char)('A' + (key - Keys.A));
|
|
else if (key == Keys.Space && _enteredName.Length < 20)
|
|
_enteredName += ' ';
|
|
else if (key == Keys.Back && _enteredName.Length > 0)
|
|
_enteredName = _enteredName[..^1];
|
|
else if (key == Keys.Enter && _enteredName.Length > 0)
|
|
{
|
|
G.HighScores.Enter(_enteredName, G.Score);
|
|
G.State = GameState.Credits;
|
|
}
|
|
}
|
|
}
|
|
}
|