diff --git a/NHSE.Core/Save/Files/MainSave.cs b/NHSE.Core/Save/Files/MainSave.cs index 67447a7..7ae578e 100644 --- a/NHSE.Core/Save/Files/MainSave.cs +++ b/NHSE.Core/Save/Files/MainSave.cs @@ -62,9 +62,12 @@ public void SetAcreBytes(byte[] data) data.CopyTo(Data, Offsets.Acres); } - public TerrainTile[] GetTerrain() => TerrainTile.GetArray(Data.Slice(Offsets.Terrain, MapGrid.MapTileCount * TerrainTile.SIZE)); + public TerrainTile[] GetTerrain() => TerrainTile.GetArray(Data.Slice(Offsets.Terrain, MapGrid.MapTileCount16x16 * TerrainTile.SIZE)); public void SetTerrain(IReadOnlyList array) => TerrainTile.SetArray(array).CopyTo(Data, Offsets.Terrain); + public FieldItem[] GetFieldItems() => FieldItem.GetArray(Data.Slice(Offsets.FieldItem, MapGrid.MapTileCount32x32 * FieldItem.SIZE)); + public void SetFieldItems(IReadOnlyList array) => FieldItem.SetArray(array).CopyTo(Data, Offsets.FieldItem); + public uint PlazaX { get => BitConverter.ToUInt32(Data, Offsets.Acres + AcreSizeAll + 4); diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets.cs index 9ae42d3..c8ac014 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets.cs @@ -22,6 +22,7 @@ public abstract class MainSaveOffsets public abstract int TurnipExchange { get; } + public abstract int FieldItem { get; } public abstract int Acres { get; } public abstract int Terrain { get; } diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets10.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets10.cs index bb9ac2f..e8cb65d 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets10.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets10.cs @@ -7,6 +7,7 @@ public class MainSaveOffsets10 : MainSaveOffsets { public override int Villager => 0x110; public override int Patterns => 0x1D72F0; + public override int FieldItem => 0x2018FC; public override int Terrain => Buildings - 0x24C00; // dunno where exactly it starts??? public override int Buildings => 0x2D0EFC; public override int Acres => 0x2D1294; diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets11.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets11.cs index e5b82c2..4bb9ba3 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets11.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets11.cs @@ -7,6 +7,7 @@ public class MainSaveOffsets11 : MainSaveOffsets { public override int Villager => 0x120; public override int Patterns => 0x1D7310; // +0x20 from v1.0 + public override int FieldItem => 0x20191C; // +0x20 from v1.0 public override int Terrain => Buildings - 0x24C00; // dunno where exactly it starts??? // +0x20 from v1.0 public override int Buildings => 0x2D0F1C; // +0x20 from v1.0 public override int Acres => 0x2D12B4; // +0x20 from v1.0 diff --git a/NHSE.Core/Structures/Acres/Terrain/FieldItemLayer.cs b/NHSE.Core/Structures/Acres/Terrain/FieldItemLayer.cs new file mode 100644 index 0000000..0af6c09 --- /dev/null +++ b/NHSE.Core/Structures/Acres/Terrain/FieldItemLayer.cs @@ -0,0 +1,25 @@ +using System.Diagnostics; + +namespace NHSE.Core +{ + public class FieldItemLayer : MapGrid + { + public readonly FieldItem[] Tiles; + + public FieldItemLayer(FieldItem[] tiles) : base(32, 32) + { + Tiles = tiles; + Debug.Assert(MapTileCount == tiles.Length); + } + + public FieldItem GetTile(int x, int y) => this[GetTileIndex(x, y)]; + public FieldItem GetTile(int acreX, int acreY, int gridX, int gridY) => this[GetTileIndex(acreX, acreY, gridX, gridY)]; + public FieldItem GetAcreTile(int acreIndex, int tileIndex) => this[GetAcreTileIndex(acreIndex, tileIndex)]; + + public FieldItem this[int index] + { + get => Tiles[index]; + set => Tiles[index] = value; + } + } +} diff --git a/NHSE.Core/Structures/Acres/Terrain/FieldItemManager.cs b/NHSE.Core/Structures/Acres/Terrain/FieldItemManager.cs new file mode 100644 index 0000000..2179c27 --- /dev/null +++ b/NHSE.Core/Structures/Acres/Terrain/FieldItemManager.cs @@ -0,0 +1,36 @@ +using System; + +namespace NHSE.Core +{ + public class FieldItemManager + { + public readonly FieldItemLayer Layer1; + public readonly FieldItemLayer Layer2; + + public FieldItemManager(FieldItem[] layer1, FieldItem[] layer2) + { + Layer1 = new FieldItemLayer(layer1); + Layer2 = new FieldItemLayer(layer2); + } + + public FieldItemManager(FieldItem[] items, int layers = 2) // Two halves; split and assign. + : this(items.Slice(0, items.Length / 2), items.SliceEnd(items.Length / 2)) + { + if (layers != 2) + throw new ArgumentOutOfRangeException(nameof(layers)); + } + + public FieldItem GetTile(int layer, int x, int y) => GetLayer(layer).GetTile(x, y); + public FieldItem GetTile(int layer, int acreX, int acreY, int gridX, int gridY) => GetLayer(layer).GetTile(acreX, acreY, gridX, gridY); + public FieldItem GetAcreTile(int layer, int acreIndex, int tileIndex) => GetLayer(layer).GetAcreTile(acreIndex, tileIndex); + + public FieldItemLayer GetLayer(int layer) + { + if ((uint) layer >= 2) + throw new ArgumentOutOfRangeException(nameof(layer)); + return layer == 0 ? Layer1 : Layer2; + } + + public bool IsOccupied(int x, int y) => !Layer1.GetTile(x, y).IsNone || !Layer2.GetTile(x, y).IsNone; + } +} diff --git a/NHSE.Core/Structures/Acres/Terrain/MapGrid.cs b/NHSE.Core/Structures/Acres/Terrain/MapGrid.cs index ffb1e68..4f57903 100644 --- a/NHSE.Core/Structures/Acres/Terrain/MapGrid.cs +++ b/NHSE.Core/Structures/Acres/Terrain/MapGrid.cs @@ -9,27 +9,37 @@ public abstract class MapGrid { public static readonly AcreCoordinate[] Acres = AcreCoordinate.GetGrid(AcreWidth, AcreHeight); - public const int GridWidth = 16; - public const int GridHeight = 16; + protected MapGrid(int gw, int gh) + { + GridWidth = gw; + GridHeight = gh; + } + + public readonly int GridWidth; + public readonly int GridHeight; public const int AcreWidth = 7; public const int AcreHeight = 6; - public const int AcreTileCount = GridWidth * GridHeight; + public const int AcreCount = AcreWidth * AcreHeight; - public const int MapHeight = AcreHeight * GridHeight; // 96 - public const int MapWidth = AcreWidth * GridWidth; // 112 - public const int MapTileCount = MapWidth * MapHeight; // 0x2A00 bytes for + public int AcreTileCount => GridWidth * GridHeight; + public int MapHeight => AcreHeight * GridHeight; // 96 (when 16x16) + public int MapWidth => AcreWidth * GridWidth; // 112 (when 16x16) + public int MapTileCount => MapWidth * MapHeight; // 0x2A00 bytes (when 16x16) - protected static int GetTileIndex(int x, int y) => (MapHeight * x) + y; + public const int MapTileCount16x16 = 16 * 16 * AcreCount; + public const int MapTileCount32x32 = 32 * 32 * AcreCount; - protected static int GetTileIndex(int acreX, int acreY, int gridX, int gridY) + protected int GetTileIndex(int x, int y) => (MapHeight * x) + y; + + protected int GetTileIndex(int acreX, int acreY, int gridX, int gridY) { var x = (acreX * GridWidth) + gridX; var y = (acreY * GridHeight) + gridY; return GetTileIndex(x, y); } - protected static int GetAcreTileIndex(int acreIndex, int tileIndex) + protected int GetAcreTileIndex(int acreIndex, int tileIndex) { var acre = Acres[acreIndex]; var x = (tileIndex % GridWidth); @@ -37,9 +47,9 @@ protected static int GetAcreTileIndex(int acreIndex, int tileIndex) return GetTileIndex(acre.X, acre.Y, x, y); } - public static int GetAcre(int x, int y) => (x / GridWidth) + ((y / GridHeight) * AcreWidth); + public int GetAcre(int x, int y) => (x / GridWidth) + ((y / GridHeight) * AcreWidth); - public static void GetViewAnchorCoordinates(ref int x, ref int y, bool centerReticle) + public void GetViewAnchorCoordinates(ref int x, ref int y, bool centerReticle) { // If we aren't snapping the reticle to the nearest acre // we want to put the middle of the reticle rectangle where the cursor is. @@ -53,13 +63,13 @@ public static void GetViewAnchorCoordinates(ref int x, ref int y, bool centerRet // Clamp to viewport dimensions, and center to nearest acre if desired. // Clamp to boundaries so that we always have 16x16 to view. - const int maxX = ((AcreWidth - 1) * GridWidth); - const int maxY = ((AcreHeight - 1) * GridHeight); + int maxX = ((AcreWidth - 1) * GridWidth); + int maxY = ((AcreHeight - 1) * GridHeight); x = Math.Max(0, Math.Min(x, maxX)); y = Math.Max(0, Math.Min(y, maxY)); } - public static void GetViewAnchorCoordinates(int acre, out int x, out int y) + public void GetViewAnchorCoordinates(int acre, out int x, out int y) { x = (acre % AcreWidth) * GridWidth; y = (acre / AcreWidth) * GridHeight; diff --git a/NHSE.Core/Structures/Acres/Terrain/TerrainManager.cs b/NHSE.Core/Structures/Acres/Terrain/TerrainManager.cs index f80414a..269e4dd 100644 --- a/NHSE.Core/Structures/Acres/Terrain/TerrainManager.cs +++ b/NHSE.Core/Structures/Acres/Terrain/TerrainManager.cs @@ -6,7 +6,7 @@ public class TerrainManager : MapGrid { public readonly TerrainTile[] Tiles; - public TerrainManager(TerrainTile[] tiles) + public TerrainManager(TerrainTile[] tiles) : base(16, 16) { Tiles = tiles; Debug.Assert(MapTileCount == tiles.Length); @@ -32,7 +32,7 @@ public byte[] DumpAllAcres() public byte[] DumpAcre(int acre) { - const int count = AcreTileCount; + int count = AcreTileCount; var result = new byte[TerrainTile.SIZE * count]; for (int i = 0; i < count; i++) { @@ -52,7 +52,7 @@ public void ImportAllAcres(byte[] data) public void ImportAcre(int acre, byte[] data) { - const int count = AcreTileCount; + int count = AcreTileCount; var tiles = TerrainTile.GetArray(data); for (int i = 0; i < count; i++) { diff --git a/NHSE.Sprites/Field/FieldItemSpriteDrawer.cs b/NHSE.Sprites/Field/FieldItemSpriteDrawer.cs index 8634159..944982c 100644 --- a/NHSE.Sprites/Field/FieldItemSpriteDrawer.cs +++ b/NHSE.Sprites/Field/FieldItemSpriteDrawer.cs @@ -9,9 +9,12 @@ public class FieldItemSpriteDrawer public int Width { get; set; } = 8; public int Height { get; set; } = 8; + public Bitmap GetImage(FieldItem item) => FieldItemSprite.GetImage(item, Width, Height); + + public Bitmap GetBitmapLayer(FieldItemLayer layer) => GetItemArray(layer.Tiles, layer.MapHeight); + public Bitmap GetItemArray(IReadOnlyList items, int height) { - //var items = FieldItem.GetArray(SAV.Main.Data.Slice(0x20191C, 0xA8000)); var width = items.Count / height; var png = new Bitmap(width * Width, height * Height); diff --git a/NHSE.Sprites/Field/TerrainSprite.cs b/NHSE.Sprites/Field/TerrainSprite.cs index 80d0796..f6fcff7 100644 --- a/NHSE.Sprites/Field/TerrainSprite.cs +++ b/NHSE.Sprites/Field/TerrainSprite.cs @@ -41,10 +41,10 @@ public static string GetTileName(TerrainTile tile) public static Bitmap CreateMap(TerrainManager mgr) { - var bmp = new Bitmap(MapGrid.MapWidth, MapGrid.MapHeight); - for (int x = 0; x < MapGrid.MapWidth; x++) + var bmp = new Bitmap(mgr.MapWidth, mgr.MapHeight); + for (int x = 0; x < mgr.MapWidth; x++) { - for (int y = 0; y < MapGrid.MapHeight; y++) + for (int y = 0; y < mgr.MapHeight; y++) { var tile = mgr.GetTile(x, y); var color = GetTileColor(tile); @@ -59,7 +59,7 @@ public static Bitmap CreateMap(TerrainManager mgr, int scale, int x, int y) { var img = CreateMap(mgr); var map = ImageUtil.ResizeImage(img, img.Width * scale, img.Height * scale); - return DrawReticle(map, x, y, scale); + return DrawReticle(map, mgr, x, y, scale); } public static Bitmap CreateMap(TerrainManager mgr, int scale, int acreIndex = -1) @@ -71,19 +71,19 @@ public static Bitmap CreateMap(TerrainManager mgr, int scale, int acreIndex = -1 return map; var acre = MapGrid.Acres[acreIndex]; - var x = acre.X * MapGrid.GridWidth; - var y = acre.Y * MapGrid.GridHeight; + var x = acre.X * mgr.GridWidth; + var y = acre.Y * mgr.GridHeight; - return DrawReticle(map, x, y, scale); + return DrawReticle(map, mgr, x, y, scale); } - private static Bitmap DrawReticle(Bitmap map, int x, int y, int scale) + private static Bitmap DrawReticle(Bitmap map, TerrainManager mgr, int x, int y, int scale) { using var gfx = Graphics.FromImage(map); using var pen = new Pen(Color.Red); - int w = MapGrid.GridWidth * scale; - int h = MapGrid.GridHeight * scale; + int w = mgr.GridWidth * scale; + int h = mgr.GridHeight * scale; gfx.DrawRectangle(pen, x * scale, y * scale, w, h); return map; } @@ -93,15 +93,15 @@ public static Bitmap GetMapWithBuildings(TerrainManager mgr, IReadOnlyList buildings, Font f, int scale, int index = -1) + private static void DrawBuildings(this Graphics gfx, MapGrid g, IReadOnlyList buildings, Font f, int scale, int index = -1) { var selected = Brushes.Red; var others = Brushes.Yellow; @@ -121,7 +121,7 @@ private static void DrawBuildings(this Graphics gfx, IReadOnlyList bui var b = buildings[i]; if (b.BuildingType == 0) continue; - GetBuildingCoordinate(b.X, b.Y, scale, out var x, out var y); + GetBuildingCoordinate(g,b.X, b.Y, scale, out var x, out var y); var pen = index == i ? selected : others; gfx.FillRectangle(pen, x - scale, y - scale, scale * 2, scale * 2); @@ -131,11 +131,11 @@ private static void DrawBuildings(this Graphics gfx, IReadOnlyList bui } } - private static void GetBuildingCoordinate(ushort bx, ushort by, int scale, out int x, out int y) + private static void GetBuildingCoordinate(MapGrid g, ushort bx, ushort by, int scale, out int x, out int y) { // Although there is terrain in the Top Row and Left Column, no buildings can be placed there. // Adjust the building coordinates down-right by an acre. - const int buildingShift = MapGrid.GridWidth; + int buildingShift = g.GridWidth; x = (int) (((bx / 2f) - buildingShift) * scale); y = (int) (((by / 2f) - buildingShift) * scale); } diff --git a/NHSE.WinForms/Subforms/TerrainEditor.cs b/NHSE.WinForms/Subforms/TerrainEditor.cs index c39db34..da30a7d 100644 --- a/NHSE.WinForms/Subforms/TerrainEditor.cs +++ b/NHSE.WinForms/Subforms/TerrainEditor.cs @@ -14,8 +14,8 @@ public partial class TerrainEditor : Form private readonly Button[] Grid; private readonly TerrainManager Terrain; - private const int GridWidth = MapGrid.GridWidth; - private const int GridHeight = MapGrid.GridHeight; + private int GridWidth => Terrain.GridWidth; + private int GridHeight => Terrain.GridHeight; private const int SquareSize = 50; private const int MapScale = 2; @@ -45,7 +45,7 @@ public TerrainEditor(MainSave sav) private void ChangeViewToAcre(int acre) { - MapGrid.GetViewAnchorCoordinates(acre, out X, out Y); + Terrain.GetViewAnchorCoordinates(acre, out X, out Y); LoadGrid(X, Y); UpdateArrowVisibility(acre); } @@ -261,7 +261,7 @@ private void B_ImportAcre_Click(object sender, EventArgs e) var path = ofd.FileName; var fi = new FileInfo(path); - const int expect = MapGrid.AcreTileCount * TerrainTile.SIZE; + int expect = Terrain.AcreTileCount * TerrainTile.SIZE; if (fi.Length != expect) { WinFormsUtil.Error($"Expected size (0x{expect:X}) != Input size (0x{fi.Length:X}", path); @@ -287,7 +287,7 @@ private void B_ImportAllAcres_Click(object sender, EventArgs e) var path = ofd.FileName; var fi = new FileInfo(path); - const int expect = MapGrid.MapTileCount * TerrainTile.SIZE; + int expect = Terrain.AcreTileCount * TerrainTile.SIZE; if (fi.Length != expect) { WinFormsUtil.Error($"Expected size (0x{expect:X}) != Input size (0x{fi.Length:X}", path); @@ -331,7 +331,7 @@ private void ClickMapAt(MouseEventArgs e, bool skipLagCheck) bool centerReticle = CHK_SnapToAcre.Checked; GetViewAnchorCoordinates(mX, mY, out var x, out var y, centerReticle); - var acre = MapGrid.GetAcre(x, y); + var acre = Terrain.GetAcre(x, y); bool sameAcre = AcreIndex == acre; if (!skipLagCheck) { @@ -366,10 +366,10 @@ private static void GetCursorCoordinates(int mX, int mY, out int x, out int y) y = mY / MapScale; } - private static void GetViewAnchorCoordinates(int mX, int mY, out int x, out int y, bool centerReticle) + private void GetViewAnchorCoordinates(int mX, int mY, out int x, out int y, bool centerReticle) { GetCursorCoordinates(mX, mY, out x, out y); - MapGrid.GetViewAnchorCoordinates(ref x, ref y, centerReticle); + Terrain.GetViewAnchorCoordinates(ref x, ref y, centerReticle); } private void PB_Map_MouseMove(object sender, MouseEventArgs e)