using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System.Collections.Generic; using System.Diagnostics; using System.IO; namespace OpenVIII.Fields { public partial class Background { #region Classes private class Tile { #region Fields public const int size = 16; /// /// Size of Texture Segment /// public static readonly Vector2 TextureSize = new Vector2(fourBitTexturePageWidth, fourBitTexturePageWidth); public byte AnimationID = 0xFF; public byte AnimationState; public byte blend1; public BlendMode BlendMode = BlendMode.none; public byte Depth; /// /// Layer ID, Used to control which parts draw. /// public byte LayerID; /// /// Gets +1 if Overlaps() == true; /// public byte OverLapID = 0; public byte PaletteID; /// /// for outputting the source tiles to texture pages. some tiles have the same source rectangle. So skip. /// public bool Skip = false; /// /// Source X from Texture Data /// public ushort SourceX; //[6-10] /// /// Source Y from Texture Data /// public ushort SourceY; public byte TextureID; public int TileID; /// /// Distance from left side /// public short X; /// /// Distance from top side /// public short Y; /// /// Larger number is farther away. So OrderByDecending for draw order. /// public ushort Z; //public byte[] PaletteID4Bit => new byte[] { }; private uint pupuID; #endregion Fields #region Properties public bool Is4Bit => !Is8Bit; public bool Is8Bit => Depth >= 4; /// /// Pupu goes in a loop and +1 the id when it detects an overlap. /// There are some wasted bits /// public uint PupuID { get { if (pupuID == 0) pupuID = ((uint)(LayerID) << 24) + (((uint)BlendMode & 0x2) << 20) + ((uint)AnimationID << 12) + ((uint)(AnimationState & 0xF) << 4); return pupuID; } set => pupuID = value; } public byte SourceOverLapID { get; internal set; } /// /// TopLeft UV /// public Vector2 UV => new Vector2(SourceX, SourceY) / TextureSize; // 4 bits public float Zfloat => Z / 4096f; #endregion Properties #region Methods /// /// TopLeft Cord /// /// public static implicit operator Vector2(Tile @in) => new Vector2(@in.SourceX, @in.SourceY); public static implicit operator Vector3(Tile @in) => new Vector3(@in.X, @in.Y, @in.Zfloat); public static Tile Load(BinaryReader pbsmap, int id, byte type) { long p = pbsmap.BaseStream.Position; Tile t = new Tile { X = pbsmap.ReadInt16() }; if (t.X == 0x7FFF) return null; t.Y = pbsmap.ReadInt16(); if (type == 1) { t.Z = pbsmap.ReadUInt16();// (ushort)(4096 - pbsmap.ReadUShort()); byte texIdBuffer = pbsmap.ReadByte(); t.TextureID = (byte)(texIdBuffer & 0xF); // pbsmap.BaseStream.Seek(-1, SeekOrigin.Current); pbsmap.BaseStream.Seek(1, SeekOrigin.Current); t.PaletteID = (byte)((pbsmap.ReadInt16() >> 6) & 0xF); t.SourceX = pbsmap.ReadByte(); t.SourceY = pbsmap.ReadByte(); t.LayerID = (byte)(pbsmap.ReadByte() >> 1/*& 0x7F*/); t.BlendMode = (BlendMode)pbsmap.ReadByte(); t.AnimationID = pbsmap.ReadByte(); t.AnimationState = pbsmap.ReadByte(); t.blend1 = (byte)((texIdBuffer >> 4) & 0x1); t.Depth = (byte)(texIdBuffer >> 5); t.TileID = id; } else if (type == 2) { t.SourceX = pbsmap.ReadUInt16(); t.SourceY = pbsmap.ReadUInt16(); t.Z = pbsmap.ReadUInt16(); byte texIdBuffer = pbsmap.ReadByte(); t.TextureID = (byte)(texIdBuffer & 0xF); pbsmap.BaseStream.Seek(1, SeekOrigin.Current); t.PaletteID = (byte)((pbsmap.ReadInt16() >> 6) & 0xF); t.AnimationID = pbsmap.ReadByte(); t.AnimationState = pbsmap.ReadByte(); t.blend1 = (byte)((texIdBuffer >> 4) & 0x1); t.Depth = (byte)(texIdBuffer >> 5); t.TileID = id; } Debug.Assert(p - pbsmap.BaseStream.Position == -16); return t; } public Rectangle GetRectangle() => new Rectangle(X, Y, size, size); public bool Intersect(Tile tile, bool rev = false) { bool flip = !rev && tile.Intersect(this, !rev); bool ret = flip || X >= tile.X && X < tile.X + size && Y >= tile.Y && Y < tile.Y + size && Z == tile.Z && LayerID == tile.LayerID && BlendMode == tile.BlendMode && AnimationID == tile.AnimationID && AnimationState == tile.AnimationState; return ret; } public bool SourceIntersect(Tile tile, bool rev = false) { bool flip = !rev && tile.SourceIntersect(this, !rev); if (!flip && tile.TextureID == TextureID) { return Rectangle.Intersect(SourceRectangle(), tile.SourceRectangle()) != Rectangle.Empty; } return flip; } public Rectangle SourceRectangle() => new Rectangle(SourceX, SourceY, size, size); private List GetCorners(float scale) { Vector2 sizeVertex = new Vector2(size,size)/scale; Vector2 sizeUV = new Vector2(size) / TextureSize; List r = new List(4); for (int i = 0; i < r.Capacity; i++) { VertexPositionTexture vpt = new VertexPositionTexture(((Vector3)this) / scale, UV); switch (i) { case 0://top left break; case 1://top right vpt.Position.X += sizeVertex.X; break; case 2://bottom right vpt.Position.X += sizeVertex.X; vpt.Position.Y += sizeVertex.Y; break; case 3://bottom left vpt.Position.Y += sizeVertex.Y; break; } switch (i) { case 0://top left break; case 1://top right vpt.TextureCoordinate.X += sizeUV.X; break; case 2://bottom right vpt.TextureCoordinate.X += sizeUV.X; vpt.TextureCoordinate.Y += sizeUV.Y; break; case 3://bottom left vpt.TextureCoordinate.Y += sizeUV.Y; break; } Vector3 vectorflip = new Vector3(-1, -1, 1); vpt.Position *= vectorflip; r.Add(vpt); } return r; } public VertexPositionTexture[] GetQuad(float scale) { List vpts = GetCorners(scale); // 4 unique corners. //create 2 triangles List r = new List { vpts[3], vpts[1], vpts[0], vpts[3], vpts[2], vpts[1], }; return r.ToArray(); } #endregion Methods } #endregion Classes } }