121 lines
3.8 KiB
C#
121 lines
3.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Microsoft.Xna.Framework;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
|
|
namespace FreedomFighter;
|
|
|
|
/// <summary>
|
|
/// Port of the Omega TSprite base class. Position is the pattern's top-left;
|
|
/// CenterX/CenterY only affect the rotation pivot. Width/Height define the
|
|
/// logical collision box, independent of the drawn pattern size.
|
|
/// </summary>
|
|
public abstract class Sprite
|
|
{
|
|
public string Name = "";
|
|
public float X, Y;
|
|
public int Width, Height;
|
|
public float Rotation; // degrees
|
|
public float CenterX, CenterY; // 0..1 fraction of pattern
|
|
public float ScaleX = 1, ScaleY = 1;
|
|
public int Red = 255, Green = 255, Blue = 255, Alpha = 255;
|
|
public OmegaImage? Image;
|
|
public int ImageIndex;
|
|
public bool DoCollision;
|
|
public bool DoPixelCheck;
|
|
public bool IsDead;
|
|
|
|
protected Sprite(SpriteManager manager) => manager.Add(this);
|
|
|
|
public void SetGreen(int v) => Green = Math.Clamp(v, 0, 255);
|
|
public void SetBlue(int v) => Blue = Math.Clamp(v, 0, 255);
|
|
|
|
/// <summary>Marks the sprite for removal (Omega's Dead).</summary>
|
|
public void Kill() => IsDead = true;
|
|
|
|
public virtual void Move(float moveCount) { }
|
|
|
|
public virtual void Draw(SpriteBatch sb)
|
|
{
|
|
Image?.Draw(sb, (int)Math.Round(X), (int)Math.Round(Y), Rotation,
|
|
CenterX, CenterY, ScaleX, ScaleY, Red, Green, Blue, Alpha, ImageIndex);
|
|
}
|
|
|
|
public virtual void OnCollision(Sprite sprite, int colX, int colY) { }
|
|
|
|
public Rectangle Bounds => new((int)X, (int)Y, Width, Height);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Port of TOmegaSprite: owns all sprites, runs movement, collision
|
|
/// (bounding box + optional pixel check), removal, and drawing.
|
|
/// </summary>
|
|
public class SpriteManager
|
|
{
|
|
public readonly List<Sprite> Items = new();
|
|
|
|
public int Count => Items.Count;
|
|
|
|
public void Add(Sprite s) => Items.Add(s);
|
|
|
|
public void KillAll()
|
|
{
|
|
foreach (var s in Items) s.Kill();
|
|
}
|
|
|
|
public void Move(float moveCount)
|
|
{
|
|
// like the Pascal for-loop, sprites spawned during iteration are not moved this frame
|
|
int n = Items.Count;
|
|
for (int i = 0; i < n; i++)
|
|
Items[i].Move(moveCount);
|
|
}
|
|
|
|
public void Collision()
|
|
{
|
|
int n = Items.Count;
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
var a = Items[i];
|
|
if (!a.DoCollision || a.IsDead) continue;
|
|
for (int j = i + 1; j < n; j++)
|
|
{
|
|
var b = Items[j];
|
|
if (!b.DoCollision || b.IsDead) continue;
|
|
|
|
var overlap = Rectangle.Intersect(a.Bounds, b.Bounds);
|
|
if (overlap.IsEmpty) continue;
|
|
|
|
if ((a.DoPixelCheck || b.DoPixelCheck) && !PixelOverlap(a, b, overlap)) continue;
|
|
|
|
a.OnCollision(b, overlap.X, overlap.Y);
|
|
b.OnCollision(a, overlap.X, overlap.Y);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Pixel test on the unrotated pattern masks (rotation ignored, like a cheap approximation).</summary>
|
|
static bool PixelOverlap(Sprite a, Sprite b, Rectangle overlap)
|
|
{
|
|
if (a.Image == null || b.Image == null) return true;
|
|
for (int y = overlap.Top; y < overlap.Bottom; y++)
|
|
for (int x = overlap.Left; x < overlap.Right; x++)
|
|
{
|
|
bool pa = a.Image.IsOpaque(a.ImageIndex, x - (int)a.X, y - (int)a.Y);
|
|
bool pb = b.Image.IsOpaque(b.ImageIndex, x - (int)b.X, y - (int)b.Y);
|
|
if (pa && pb) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void RemoveDead() => Items.RemoveAll(s => s.IsDead);
|
|
|
|
public void Draw(SpriteBatch sb)
|
|
{
|
|
int n = Items.Count;
|
|
for (int i = 0; i < n; i++)
|
|
if (!Items[i].IsDead)
|
|
Items[i].Draw(sb);
|
|
}
|
|
}
|