using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace FreedomFighter;
///
/// Equivalent of a TOmegaImageList item: a texture subdivided into equally-sized
/// patterns (frames), drawn by pattern index. Also builds alpha masks for
/// pixel-perfect collision (Omega's DoPixelCheck).
///
public class OmegaImage
{
public string Name = "";
public Texture2D Texture = null!;
public int TileWidth;
public int TileHeight;
public int Columns;
public int PatternCount;
public bool[] OpaqueMask = null!; // per-pixel alpha>0, whole texture, row-major
public static OmegaImage Load(GraphicsDevice gd, string file, int tileW, int tileH, string name)
{
using var fs = File.OpenRead(file);
var tex = Texture2D.FromStream(gd, fs);
int tw = tileW > 0 ? tileW : tex.Width;
int th = tileH > 0 ? tileH : tex.Height;
var pixels = new Color[tex.Width * tex.Height];
tex.GetData(pixels);
var mask = new bool[pixels.Length];
for (int i = 0; i < pixels.Length; i++)
mask[i] = pixels[i].A > 0;
return new OmegaImage
{
Name = name,
Texture = tex,
TileWidth = tw,
TileHeight = th,
Columns = Math.Max(1, tex.Width / tw),
PatternCount = Math.Max(1, tex.Width / tw) * Math.Max(1, tex.Height / th),
OpaqueMask = mask,
};
}
public Rectangle PatternRect(int pattern)
{
if (pattern < 0 || pattern >= PatternCount) pattern = 0;
return new Rectangle(pattern % Columns * TileWidth, pattern / Columns * TileHeight, TileWidth, TileHeight);
}
/// Is the pixel at (px,py) within the given pattern opaque? Coordinates are pattern-local.
public bool IsOpaque(int pattern, int px, int py)
{
if (px < 0 || py < 0 || px >= TileWidth || py >= TileHeight) return false;
var r = PatternRect(pattern);
int x = r.X + px, y = r.Y + py;
if (x >= Texture.Width || y >= Texture.Height) return false;
return OpaqueMask[y * Texture.Width + x];
}
///
/// Full Omega draw. X,Y is the top-left of the pattern; rotation (degrees, clockwise)
/// pivots around (centerX, centerY) as a fraction of the pattern size.
///
public void Draw(SpriteBatch sb, float x, float y, float rotation = 0,
float centerX = 0, float centerY = 0, float scaleX = 1, float scaleY = 1,
int red = 255, int green = 255, int blue = 255, int alpha = 255, int pattern = 0)
{
var src = PatternRect(pattern);
var origin = new Vector2(centerX * TileWidth, centerY * TileHeight);
var pos = new Vector2(x + origin.X * scaleX, y + origin.Y * scaleY);
var color = new Color(red, green, blue, alpha);
sb.Draw(Texture, pos, src, color, MathHelper.ToRadians(rotation), origin,
new Vector2(scaleX, scaleY), SpriteEffects.None, 0);
}
}
/// Loads the image lists extracted from GameU.dfm via gfx/manifest.json.
public class ImageLists
{
readonly Dictionary> _lists = new();
public List IL1 => _lists["OmegaImageList1"];
public List IL2 => _lists["OmegaImageList2"];
public List IL3 => _lists["OmegaImageList3"];
public List IL4 => _lists["OmegaImageList4"];
public List IL5 => _lists["OmegaImageList5"];
record ManifestItem(int Index, string Name, string File, int TileWidth, int TileHeight);
public ImageLists(GraphicsDevice gd, string contentDir)
{
string gfxDir = Path.Combine(contentDir, "gfx");
var manifest = JsonSerializer.Deserialize>>(
File.ReadAllText(Path.Combine(gfxDir, "manifest.json")))!;
foreach (var (listName, items) in manifest)
{
var list = new List();
foreach (var item in items)
list.Add(OmegaImage.Load(gd, Path.Combine(gfxDir, item.File), item.TileWidth, item.TileHeight, item.Name));
_lists[listName] = list;
}
}
}