Add more fielditem manipulation logic

32x32 because we have 2x2 metatiles -- items can be placed at half-coordinates relative to tile coordinates.
This commit is contained in:
Kurt 2020-04-07 11:25:27 -07:00
parent 2ce535c67c
commit 25a162c187
11 changed files with 125 additions and 45 deletions

View File

@ -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<TerrainTile> 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<FieldItem> array) => FieldItem.SetArray(array).CopyTo(Data, Offsets.FieldItem);
public uint PlazaX
{
get => BitConverter.ToUInt32(Data, Offsets.Acres + AcreSizeAll + 4);

View File

@ -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; }

View File

@ -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;

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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++)
{

View File

@ -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<FieldItem> 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);

View File

@ -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<Build
var map = CreateMap(mgr, scale);
using var gfx = Graphics.FromImage(map);
gfx.DrawPlaza(plazaX, plazaY, scale);
gfx.DrawBuildings(buildings, f, scale, index);
gfx.DrawPlaza(mgr, plazaX, plazaY, scale);
gfx.DrawBuildings(mgr, buildings, f, scale, index);
return map;
}
private static void DrawPlaza(this Graphics gfx, ushort px, ushort py, int scale)
private static void DrawPlaza(this Graphics gfx, MapGrid g, ushort px, ushort py, int scale)
{
var plaza = Brushes.RosyBrown;
GetBuildingCoordinate(px, py, scale, out var x, out var y);
GetBuildingCoordinate(g, px, py, scale, out var x, out var y);
var width = scale * 2 * 6;
var height = scale * 2 * 5;
@ -109,7 +109,7 @@ private static void DrawPlaza(this Graphics gfx, ushort px, ushort py, int scale
gfx.FillRectangle(plaza, x, y, width, height);
}
private static void DrawBuildings(this Graphics gfx, IReadOnlyList<Building> buildings, Font f, int scale, int index = -1)
private static void DrawBuildings(this Graphics gfx, MapGrid g, IReadOnlyList<Building> 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<Building> 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<Building> 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);
}

View File

@ -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)