working on it, untested.
decoupled 99% of the logic, need to ensure the rendering uses the correct item grid which now includes deepsea L+R sides for 3.0.0
This commit is contained in:
Kurt 2026-01-16 00:51:19 -06:00
parent d276527404
commit 766e58ffec
24 changed files with 300 additions and 291 deletions

View File

@ -7,10 +7,12 @@ namespace NHSE.Core;
/// <summary>
/// Converts <see cref="Item"/> into columns of writable Item tiles.
/// </summary>
public static class FieldItemDropper
/// <param name="AcreWidth">Field item acre width. 7 on pre-3.0.0 saves, 9 on 3.0.0+</param>
/// <param name="AcreHeight">Always 6.</param>
public sealed record FieldItemDropper(int AcreWidth, int AcreHeight = 6)
{
private const int MapHeight = FieldItemLayer.FieldItemHeight;
private const int MapWidth = FieldItemLayer.FieldItemWidth;
private int MapHeight => AcreWidth * FieldItemLayer.TilesPerAcreDim;
private int MapWidth => AcreHeight * FieldItemLayer.TilesPerAcreDim;
// Each dropped item is a 2x2 square, with the top left tile being the root node, and the other 3 being extensions pointing back to the root.
@ -21,7 +23,7 @@ public static class FieldItemDropper
/// <param name="yCount">Count of items tall the overall spawn-rectangle is.</param>
/// <param name="borderX">Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped.</param>
/// <param name="borderY">Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped.</param>
public static bool CanFitDropped(int x, int y, int totalCount, int yCount, int borderX, int borderY)
public bool CanFitDropped(int x, int y, int totalCount, int yCount, int borderX, int borderY)
{
return CanFitDropped(x, y, totalCount, yCount, borderX, borderX, borderY, borderY);
}
@ -39,7 +41,7 @@ public static bool CanFitDropped(int x, int y, int totalCount, int yCount, int b
/// <param name="topY">Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped.</param>
/// <param name="botY">Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped.</param>
/// <returns>True if can fit, false if not.</returns>
public static bool CanFitDropped(int x, int y, int totalCount, int yCount, int leftX, int rightX, int topY, int botY)
public bool CanFitDropped(int x, int y, int totalCount, int yCount, int leftX, int rightX, int topY, int botY)
{
var xCount = totalCount / yCount;
if (x < leftX || (x + (xCount * 2)) > MapWidth - rightX)
@ -50,13 +52,13 @@ public static bool CanFitDropped(int x, int y, int totalCount, int yCount, int l
return totalCount < (MapHeight * MapWidth / 32);
}
public static IReadOnlyList<FieldItemColumn> InjectItemsAsDropped(int mapX, int mapY, IReadOnlyList<Item> item)
public IReadOnlyList<FieldItemColumn> InjectItemsAsDropped(int mapX, int mapY, IReadOnlyList<Item> item)
{
int yStride = (item.Count > 16) ? 16 : item.Count;
return InjectItemsAsDropped(mapX, mapY, item, yStride);
}
public static IReadOnlyList<FieldItemColumn> InjectItemsAsDropped(int mapX, int mapY, IReadOnlyList<Item> item, int yStride)
public IReadOnlyList<FieldItemColumn> InjectItemsAsDropped(int mapX, int mapY, IReadOnlyList<Item> item, int yStride)
{
var xStride = item.Count / yStride;
List<FieldItemColumn> result = new(yStride * xStride);
@ -108,10 +110,7 @@ private static byte[] GetColumnExtension(ReadOnlySpan<Item> items)
return Item.SetArray(col);
}
private static int GetTileOffset(int x, int y)
{
return Item.SIZE * (y + (x * MapHeight));
}
private int GetTileOffset(int x, int y) => Item.SIZE * (y + (x * MapHeight));
private static Item GetDroppedItem(Item item)
{

View File

@ -157,7 +157,8 @@ public Museum Museum
set => value.Data.CopyTo(Data[Offsets.Museum..]);
}
public const int AcreWidth = 7 + (2 * 1); // 1 on each side cannot be traversed
// Acre Layout/Selection of which baselayer is selected for an acre.
private const int AcreWidth = 7 + (2 * 1); // 1 on each side cannot be traversed
private const int AcreHeight = 6 + (2 * 1); // 1 on each side cannot be traversed
private const int AcreMax = AcreWidth * AcreHeight;
private const int AcreSizeAll = AcreMax * 2;
@ -185,7 +186,14 @@ public void SetAcreBytes(ReadOnlySpan<byte> data)
data.CopyTo(Data[Offsets.OutsideField..]);
}
public TerrainTile[] GetTerrainTiles() => TerrainTile.GetArray(Data.Slice(Offsets.LandMakingMap, MapGrid.MapTileCount16x16 * TerrainTile.SIZE));
public byte FieldItemAcreWidth => Offsets.FieldItemAcreWidth; // 3.0.0 updated from 7 => 9
public byte FieldItemAcreHeight => 6; // always 6
private int FieldItemAcreCount => FieldItemAcreWidth * FieldItemAcreHeight;
private const int TotalTerrainTileCount = TerrainLayer.TilesPerAcreDim * TerrainLayer.TilesPerAcreDim * (7 * 6);
private int TotalFieldItemTileCount => FieldItemLayer.TilesPerAcreDim * FieldItemLayer.TilesPerAcreDim * FieldItemAcreCount;
public TerrainTile[] GetTerrainTiles() => TerrainTile.GetArray(Data.Slice(Offsets.LandMakingMap, TotalTerrainTileCount * TerrainTile.SIZE));
public void SetTerrainTiles(IReadOnlyList<TerrainTile> array) => TerrainTile.SetArray(array).CopyTo(Data[Offsets.LandMakingMap..]);
public const int MapDesignNone = 0xF800;
@ -202,8 +210,8 @@ public void SetMapDesignTiles(ReadOnlySpan<ushort> value)
cast.CopyTo(Data[Offsets.MyDesignMap..]);
}
private const int FieldItemLayerSize = MapGrid.MapTileCount32x32 * Item.SIZE;
private const int FieldItemFlagSize = MapGrid.MapTileCount32x32 / 8; // bitflags
private int FieldItemLayerSize => TotalFieldItemTileCount * Item.SIZE;
private int FieldItemFlagSize => TotalFieldItemTileCount / sizeof(byte); // bitflags
private int FieldItemLayer1 => Offsets.FieldItem;
private int FieldItemLayer2 => Offsets.FieldItem + FieldItemLayerSize;

View File

@ -54,7 +54,7 @@ public abstract class MainSaveOffsets
public abstract int PlayerHouseSize { get; }
public abstract int PlayerRoomSize { get; }
public virtual int AcreColumnCount => 7;
public virtual byte FieldItemAcreWidth => 7;
public abstract IVillager ReadVillager(Memory<byte> data);
public abstract IVillagerHouse ReadVillagerHouse(Memory<byte> data);

View File

@ -33,7 +33,7 @@ public class MainSaveOffsets30 : MainSaveOffsets
public const int GSaveMainFieldStart = GSaveLandStart + 0x22f3f0;
// Map size increased to accommodate the Hotel, by adding 2 columns of acres!
public override int AcreColumnCount => 9; // from 7 to 9
public override byte FieldItemAcreWidth => 9; // from 7 to 9
// does this actually impact anything? We'll eventually find out if so; I hope it is just 2 columns of unused.
// Layer0: 54000 => 6C000

View File

@ -0,0 +1,23 @@
namespace NHSE.Core;
/// <summary>
/// Basic logic implementation for interacting with the manipulatable map grid.
/// </summary>
public abstract record AcreSelectionGrid(TileGridViewport TileInfo)
{
protected int GetAcreTileIndex(int acreIndex, int tileIndex)
{
var acre = AcreCoordinate.Acres[acreIndex];
var x = (tileIndex % TileInfo.ViewWidth);
var y = (tileIndex / TileInfo.ViewHeight);
return TileInfo.GetTileIndex(acre.X, acre.Y, x, y);
}
public int GetAcre(int x, int y) => (x / TileInfo.ViewWidth) + ((y / TileInfo.ViewHeight) * TileInfo.Columns);
public void GetViewAnchorCoordinates(int acre, out int x, out int y)
{
x = (acre % TileInfo.Columns) * TileInfo.ViewWidth;
y = (acre / TileInfo.Columns) * TileInfo.ViewHeight;
}
}

View File

@ -3,22 +3,19 @@
namespace NHSE.Core;
public class FieldItemLayer : ItemLayer
public sealed record FieldItemLayer(Item[] Tiles, byte AcreWidth, byte AcreHeight)
: ItemLayer(Tiles, GetViewport(AcreWidth, AcreHeight))
{
private static TileGridViewport GetViewport(byte width, byte height) => new(TilesPerAcreDim, TilesPerAcreDim, width, height);
public const int TilesPerAcreDim = 32;
public const int FieldItemWidth = TilesPerAcreDim * AcreWidth;
public const int FieldItemHeight = TilesPerAcreDim * AcreHeight;
public FieldItemLayer(Item[] tiles) : base(tiles, FieldItemWidth, FieldItemHeight, TilesPerAcreDim, TilesPerAcreDim)
{
}
public Item GetTile(int acreX, int acreY, int gridX, int gridY) => this[GetTileIndex(acreX, acreY, gridX, gridY)];
public Item GetTile(int acreX, int acreY, int gridX, int gridY) => this[TileInfo.GetTileIndex(acreX, acreY, gridX, gridY)];
public Item GetAcreTile(int acreIndex, int tileIndex) => this[GetAcreTileIndex(acreIndex, tileIndex)];
public byte[] DumpAcre(int acre)
{
int count = GridTileCount;
int count = TileInfo.ViewCount;
var result = new byte[Item.SIZE * count];
for (int i = 0; i < count; i++)
{
@ -31,7 +28,7 @@ public byte[] DumpAcre(int acre)
public void ImportAcre(int acre, ReadOnlySpan<byte> data)
{
int count = GridTileCount;
int count = TileInfo.ViewCount;
var tiles = Item.GetArray(data);
for (int i = 0; i < count; i++)
{
@ -40,10 +37,10 @@ public void ImportAcre(int acre, ReadOnlySpan<byte> data)
}
}
public int ClearFieldPlanted(Func<FieldItemKind, bool> criteria) => ClearFieldPlanted(0, 0, MaxWidth, MaxHeight, criteria);
public int RemoveAll(Func<Item, bool> criteria) => RemoveAll(0, 0, MaxWidth, MaxHeight, criteria);
public int RemoveAll(HashSet<ushort> items) => RemoveAll(0, 0, MaxWidth, MaxHeight, z => items.Contains(z.DisplayItemId));
public int RemoveAll(ushort item) => RemoveAll(0, 0, MaxWidth, MaxHeight, z => z.DisplayItemId == item);
public int ClearFieldPlanted(Func<FieldItemKind, bool> criteria) => ClearFieldPlanted(0, 0, TileInfo.TotalWidth, TileInfo.TotalHeight, criteria);
public int RemoveAll(Func<Item, bool> criteria) => RemoveAll(0, 0, TileInfo.TotalWidth, TileInfo.TotalHeight, criteria);
public int RemoveAll(HashSet<ushort> items) => RemoveAll(0, 0, TileInfo.TotalWidth, TileInfo.TotalHeight, z => items.Contains(z.DisplayItemId));
public int RemoveAll(ushort item) => RemoveAll(0, 0, TileInfo.TotalWidth, TileInfo.TotalHeight, z => z.DisplayItemId == item);
public int ClearFieldPlanted(int xmin, int ymin, int width, int height, Func<FieldItemKind, bool> criteria)
{

View File

@ -1,23 +1,24 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
namespace NHSE.Core;
public abstract class ItemLayer : MapGrid
public abstract record ItemLayer : AcreSelectionGrid
{
public readonly Item[] Tiles;
protected ItemLayer(Item[] tiles, int w, int h) : this(tiles, w, h, w, h)
protected ItemLayer(Item[] tiles, [ConstantExpected] byte w, [ConstantExpected] byte h) : this(tiles, new(w, h, w, h))
{
}
protected ItemLayer(Item[] tiles, int w, int h, int gw, int gh) : base(gw, gh, w, h)
protected ItemLayer(Item[] tiles, TileGridViewport tileTileInfo) : base(tileTileInfo)
{
Tiles = tiles;
Debug.Assert(MaxWidth * MaxHeight == tiles.Length);
Debug.Assert(TileInfo.TotalWidth * TileInfo.TotalHeight == tiles.Length);
}
public Item GetTile(in int x, in int y) => this[GetTileIndex(x, y)];
public Item GetTile(in int x, in int y) => this[TileInfo.GetTileIndex(x, y)];
public Item this[int index]
{
@ -101,10 +102,10 @@ private void GetTileWidthHeight(Item tile, int x, int y, out int w, out int h)
(w, h) = (h, w);
// Clamp to grid bounds
if (x + w - 1 >= MaxWidth)
w = MaxWidth - x;
if (y + h - 1 >= MaxHeight)
h = MaxHeight - y;
if (x + w - 1 >= TileInfo.TotalWidth)
w = TileInfo.TotalWidth - x;
if (y + h - 1 >= TileInfo.TotalHeight)
h = TileInfo.TotalHeight - y;
}
/// <summary>
@ -120,9 +121,9 @@ public PlacedItemPermission IsOccupied(Item tile, in int x, in int y)
if ((tile.Rotation & 1) == 1)
(w, h) = (h, w);
if (x + w - 1 >= MaxWidth)
if (x + w - 1 >= TileInfo.TotalWidth)
return PlacedItemPermission.OutOfBounds;
if (y + h - 1 >= MaxHeight)
if (y + h - 1 >= TileInfo.TotalHeight)
return PlacedItemPermission.OutOfBounds;
for (byte ix = 0; ix < w; ix++)

View File

@ -3,11 +3,11 @@
namespace NHSE.Core;
public class RoomItemLayer : ItemLayer
public sealed record RoomItemLayer : ItemLayer
{
public const int SIZE = Width * Height * Item.SIZE;
private const int Width = 20;
private const int Height = 20;
private const byte Width = 20;
private const byte Height = 20;
public RoomItemLayer(ReadOnlySpan<byte> data) : this(Item.GetArray(data)) { }
public RoomItemLayer(Item[] tiles) : base(tiles, Width, Height) { }

View File

@ -7,20 +7,27 @@ namespace NHSE.Core;
/// <summary>
/// Grid of <see cref="TerrainTile"/>
/// </summary>
public class TerrainLayer : MapGrid
public sealed record TerrainLayer : AcreSelectionGrid
{
public TerrainTile[] Tiles { get; init; }
public byte[] BaseAcres { get; init; }
public Memory<byte> BaseAcres { get; init; }
public TerrainLayer(TerrainTile[] tiles, byte[] acres) : base(16, 16, AcreWidth * 16, AcreHeight * 16)
public const byte TilesPerAcreDim = 16;
private const byte CountAcreWidth = 7;
private const byte CountAcreHeight = 6;
private static TileGridViewport Viewport => new(TilesPerAcreDim, TilesPerAcreDim, CountAcreWidth, CountAcreHeight);
public TerrainLayer(TerrainTile[] tiles, Memory<byte> acres) : base(Viewport)
{
BaseAcres = acres;
Tiles = tiles;
Debug.Assert(MaxTileCount == tiles.Length);
Debug.Assert(TileInfo.TotalCount == tiles.Length);
}
public TerrainTile GetTile(int x, int y) => this[GetTileIndex(x, y)];
public TerrainTile GetTile(int acreX, int acreY, int gridX, int gridY) => this[GetTileIndex(acreX, acreY, gridX, gridY)];
public TerrainTile GetTile(int x, int y) => this[TileInfo.GetTileIndex(x, y)];
public TerrainTile GetTile(int acreX, int acreY, int gridX, int gridY) => this[TileInfo.GetTileIndex(acreX, acreY, gridX, gridY)];
public TerrainTile GetAcreTile(int acreIndex, int tileIndex) => this[GetAcreTileIndex(acreIndex, tileIndex)];
public TerrainTile this[int index]
@ -39,7 +46,7 @@ public byte[] DumpAll()
public byte[] DumpAcre(int acre)
{
int count = GridTileCount;
int count = TileInfo.ViewCount;
var result = new byte[TerrainTile.SIZE * count];
for (int i = 0; i < count; i++)
{
@ -60,7 +67,7 @@ public void ImportAll(ReadOnlySpan<byte> data)
public void ImportAcre(int acre, ReadOnlySpan<byte> data)
{
int count = GridTileCount;
int count = TileInfo.ViewCount;
var tiles = TerrainTile.GetArray(data);
for (int i = 0; i < count; i++)
{
@ -74,10 +81,10 @@ public void SetAll(TerrainTile tile, in bool interiorOnly)
if (interiorOnly)
{
// skip outermost ring of tiles
int xmin = GridWidth;
int ymin = GridHeight;
int xmax = MaxWidth - GridWidth;
int ymax = MaxHeight - GridHeight;
int xmin = TileInfo.ViewWidth;
int ymin = TileInfo.ViewHeight;
int xmax = TileInfo.TotalWidth - TileInfo.ViewWidth;
int ymax = TileInfo.TotalHeight - TileInfo.ViewHeight;
for (int x = xmin; x < xmax; x++)
{
for (int y = ymin; y < ymax; y++)
@ -96,10 +103,10 @@ public void SetAllRoad(TerrainTile tile, in bool interiorOnly)
if (interiorOnly)
{
// skip outermost ring of tiles
int xmin = GridWidth;
int ymin = GridHeight;
int xmax = MaxWidth - GridWidth;
int ymax = MaxHeight - GridHeight;
int xmin = TileInfo.ViewWidth;
int ymin = TileInfo.ViewHeight;
int xmax = TileInfo.TotalWidth - TileInfo.ViewWidth;
int ymax = TileInfo.TotalHeight - TileInfo.ViewHeight;
for (int x = xmin; x < xmax; x++)
{
for (int y = ymin; y < ymax; y++)
@ -117,7 +124,7 @@ public void GetBuildingCoordinate(ushort bx, ushort by, int scale, out int x, ou
{
// 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.
int buildingShift = GridWidth;
int buildingShift = TileInfo.ViewWidth;
x = (int)(((bx / 2f) - buildingShift) * scale);
y = (int)(((by / 2f) - buildingShift) * scale);
}
@ -131,10 +138,10 @@ public void GetBuildingRelativeCoordinates(int topX, int topY, int acreScale, us
public bool IsWithinGrid(int acreScale, int relX, int relY)
{
if ((uint)relX >= GridWidth * acreScale)
if ((uint)relX >= TileInfo.ViewWidth * acreScale)
return false;
if ((uint)relY >= GridHeight * acreScale)
if ((uint)relY >= TileInfo.ViewHeight * acreScale)
return false;
return true;
@ -159,8 +166,8 @@ private ushort GetTileAcre(int x, int y)
var acreX = 1 + (x / 16);
var acreY = 1 + (y / 16);
var acreIndex = ((AcreWidth + 2) * acreY) + acreX;
var acreIndex = ((CountAcreWidth + 2) * acreY) + acreX;
var ofs = acreIndex * 2;
return ReadUInt16LittleEndian(BaseAcres.AsSpan(ofs));
return ReadUInt16LittleEndian(BaseAcres.Span[ofs..]);
}
}

View File

@ -23,11 +23,22 @@ public class FieldItemManager
/// </summary>
public readonly MainSave SAV;
public readonly int FieldAcreWidth;
public readonly int FieldAcreHeight;
public readonly int FieldItemWidth;
public readonly int FieldItemHeight;
public FieldItemManager(MainSave sav)
{
Layer1 = new FieldItemLayer(sav.GetFieldItemLayer1());
Layer2 = new FieldItemLayer(sav.GetFieldItemLayer2());
var (aWidth, aHeight) = (sav.FieldItemAcreWidth, sav.FieldItemAcreHeight);
Layer1 = new FieldItemLayer(sav.GetFieldItemLayer1(), aWidth, aHeight);
Layer2 = new FieldItemLayer(sav.GetFieldItemLayer2(), aWidth, aHeight);
SAV = sav;
FieldAcreWidth = aWidth;
FieldAcreHeight = aHeight;
FieldItemWidth = Layer1.TileInfo.TotalWidth;
FieldItemHeight = Layer1.TileInfo.TotalHeight;
}
/// <summary>
@ -46,12 +57,12 @@ public void Save()
/// Lists out all coordinates of tiles present in <see cref="Layer2"/> that don't have anything underneath in <see cref="Layer1"/> to support them.
/// </summary>
/// <returns></returns>
public List<string> GetUnsupportedTiles()
public List<string> GetUnsupportedTiles(int totalWidth, int totalHeight)
{
var result = new List<string>();
for (int x = 0; x < FieldItemLayer.FieldItemWidth; x++)
for (int x = 0; x < totalWidth; x++)
{
for (int y = 0; y < FieldItemLayer.FieldItemHeight; y++)
for (int y = 0; y < totalHeight; y++)
{
var tile = Layer2.GetTile(x, y);
if (tile.IsNone)
@ -73,9 +84,9 @@ private void SetTileActiveFlags(ItemLayer tiles, int ofs)
// Although the Tiles are arranged y-column (y-x) based, the 'isActive' flags are arranged x-row (x-y) based.
// We can turn the isActive flag off if the item is not a root or the item cannot be animated.
for (int x = 0; x < FieldItemLayer.FieldItemWidth; x++)
for (int x = 0; x < FieldItemWidth; x++)
{
for (int y = 0; y < FieldItemLayer.FieldItemHeight; y++)
for (int y = 0; y < FieldItemHeight; y++)
{
var tile = tiles.GetTile(x, y);
var isActive = GetIsActive(ofs, x, y);
@ -92,8 +103,8 @@ private void SetTileActiveFlags(ItemLayer tiles, int ofs)
public bool GetIsActive(bool baseLayer, int x, int y) => GetIsActive(baseLayer ? SAV.FieldItemFlag1 : SAV.FieldItemFlag2, x, y);
public void SetIsActive(bool baseLayer, int x, int y, bool value) => SetIsActive(baseLayer ? SAV.FieldItemFlag1 : SAV.FieldItemFlag2, x, y, value);
private bool GetIsActive(int ofs, int x, int y) => FlagUtil.GetFlag(SAV.Data, ofs, (y * FieldItemLayer.FieldItemWidth) + x);
private void SetIsActive(int ofs, int x, int y, bool value) => FlagUtil.SetFlag(SAV.Data, ofs, (y * FieldItemLayer.FieldItemWidth) + x, value);
private bool GetIsActive(int ofs, int x, int y) => FlagUtil.GetFlag(SAV.Data, ofs, (y * FieldItemWidth) + x);
private void SetIsActive(int ofs, int x, int y, bool value) => FlagUtil.SetFlag(SAV.Data, ofs, (y * FieldItemWidth) + x, value);
public bool IsOccupied(int x, int y) => !Layer1.GetTile(x, y).IsNone || !Layer2.GetTile(x, y).IsNone;
}

View File

@ -35,9 +35,9 @@ public List<string> GetUnsupportedTiles()
var lBase = Layers[(int)RoomLayerSurface.Floor];
var lSupport = Layers[(int)RoomLayerSurface.FloorSupported];
var result = new List<string>();
for (int x = 0; x < lBase.MaxWidth; x++)
for (int x = 0; x < lBase.TileInfo.TotalWidth; x++)
{
for (int y = 0; y < lBase.MaxHeight; y++)
for (int y = 0; y < lBase.TileInfo.TotalHeight; y++)
{
var tile = lSupport.GetTile(x, y);
if (tile.IsNone)

View File

@ -1,41 +0,0 @@
namespace NHSE.Core;
/// <summary>
/// Basic logic implementation for interacting with the manipulatable map grid.
/// </summary>
public abstract class MapGrid : TileGrid
{
public static readonly AcreCoordinate[] Acres = AcreCoordinate.GetGrid(AcreWidth, AcreHeight);
public const int AcreWidth = 7;
public const int AcreHeight = 6;
public const int AcreCount = AcreWidth * AcreHeight;
public const int MapTileCount16x16 = 16 * 16 * AcreCount;
public const int MapTileCount32x32 = 32 * 32 * AcreCount;
protected MapGrid(int gw, int gh, int mw, int mh) : base(gw, gh, mw, mh) { }
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 int GetAcreTileIndex(int acreIndex, int tileIndex)
{
var acre = Acres[acreIndex];
var x = (tileIndex % GridWidth);
var y = (tileIndex / GridHeight);
return GetTileIndex(acre.X, acre.Y, x, y);
}
public int GetAcre(int x, int y) => (x / GridWidth) + ((y / GridHeight) * AcreWidth);
public void GetViewAnchorCoordinates(int acre, out int x, out int y)
{
x = (acre % AcreWidth) * GridWidth;
y = (acre / AcreWidth) * GridHeight;
}
}

View File

@ -1,41 +1,52 @@
namespace NHSE.Core;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
namespace NHSE.Core;
/// <summary>
/// Navigation metadata for acre coordinates.
/// </summary>
public class AcreCoordinate
[DebuggerDisplay("{Name} ({X}, {Y})")]
public readonly record struct AcreCoordinate(char XChar, char YChar, byte X, byte Y)
{
public readonly string Name;
public readonly int X, Y;
public const int CountTerrainAcreWidth = 7;
public const int CountTerrainAcreHeight = 6;
public const int CountAcreExteriorWidth = 9;
public const int CountAcreExteriorHeight = 8;
public AcreCoordinate(int x, int y) : this((char)('0' + x), (char)('A' + y), x, y) { }
/// <summary>
/// Terrain Acre grid coordinates.
/// </summary>
public static readonly AcreCoordinate[] Acres = GetGrid(CountTerrainAcreWidth, CountTerrainAcreHeight);
public AcreCoordinate(char xName, char yName, int x, int y)
{
Name = $"{yName}{xName}";
X = x;
Y = y;
}
/// <summary>
/// Entire grid including exterior acre coordinates (bordered by acres of deep sea->shoreline=>terrain).
/// </summary>
public static readonly AcreCoordinate[] Exterior = GetGridWithExterior(CountAcreExteriorWidth, CountAcreExteriorHeight);
public static AcreCoordinate[] GetGrid(int width, int height)
public string Name => $"{YChar}{XChar}";
private AcreCoordinate(byte x, byte y) : this((char)('0' + x), (char)('A' + y), x, y) { }
private static AcreCoordinate[] GetGrid([ConstantExpected] byte width, [ConstantExpected] byte height)
{
var result = new AcreCoordinate[width * height];
int i = 0;
for (int y = 0; y < height; y++)
for (byte y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
for (byte x = 0; x < width; x++)
result[i++] = new AcreCoordinate(x, y);
}
return result;
}
public static AcreCoordinate[] GetGridWithExterior(int width, int height)
private static AcreCoordinate[] GetGridWithExterior([ConstantExpected] int width, [ConstantExpected] int height)
{
var result = new AcreCoordinate[width * height];
int i = 0;
for (int y = 0; y < height; y++)
for (byte y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
for (byte x = 0; x < width; x++)
{
var xn = (x == 0) ? '<' : x == width - 1 ? '>' : (char)('0' + x - 1);
var yn = (y == 0) ? '^' : y == height - 1 ? 'V' : (char)('A' + y - 1);

View File

@ -1,76 +0,0 @@
using System;
namespace NHSE.Core;
/// <summary>
/// Basic logic implementation for interacting with the manipulatable tile grid.
/// </summary>
/// <remarks>
/// Certain <see cref="TileGrid"/> use this as a viewport on a subsection of the entire tile-set.
/// </remarks>
public abstract class TileGrid
{
/// <summary> Amount of viewable tiles wide </summary>
public readonly int GridWidth;
/// <summary> Amount of viewable tiles high </summary>
public readonly int GridHeight;
/// <summary> Max amount of tiles wide the entire grid is </summary>
public readonly int MaxWidth;
/// <summary> Max amount of tiles high the entire grid is </summary>
public readonly int MaxHeight;
protected TileGrid(in int gw, in int gh, in int mw, in int mh)
{
GridWidth = gw;
GridHeight = gh;
MaxWidth = mw;
MaxHeight = mh;
}
/// <summary>
/// Amount of tiles present in the grid.
/// </summary>
public int GridTileCount => GridWidth * GridHeight;
/// <summary>
/// Amount of ALL tiles present in the entire grid (including the grid).
/// </summary>
public int MaxTileCount => MaxWidth * MaxHeight;
protected int GetTileIndex(in int x, in int y) => (MaxHeight * x) + y;
public void ClampCoordinatesInsideGrid(ref int x, ref int y) => ClampCoordinatesTo(ref x, ref y, MaxWidth - 1, MaxHeight - 1);
public void ClampCoordinatesTopLeft(ref int x, ref int y)
{
int maxX = MaxWidth - GridWidth;
int maxY = MaxHeight - GridHeight;
ClampCoordinatesTo(ref x, ref y, maxX, maxY);
}
private static void ClampCoordinatesTo(ref int x, ref int y, int maxX, int maxY)
{
x = Math.Max(0, Math.Min(x, maxX));
y = Math.Max(0, Math.Min(y, maxY));
}
public void GetViewAnchorCoordinates(ref int x, ref int y, in 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.
// Adjust the view coordinate
if (!centerReticle)
{
// Reticle size is GridWidth, center = /2
x -= GridWidth / 2;
y -= GridWidth / 2;
}
// Clamp to viewport dimensions, and center to nearest acre if desired.
// Clamp to boundaries so that we always have 16x16 to view.
ClampCoordinatesTopLeft(ref x, ref y);
}
}

View File

@ -0,0 +1,82 @@
using System;
using System.Diagnostics.CodeAnalysis;
namespace NHSE.Core;
/// <summary>
/// Basic configuration of a narrow view for interacting with the larger manipulatable tile grid.
/// </summary>
/// <param name="ViewWidth">Viewable amount of viewable tiles wide</param>
/// <param name="ViewHeight">Viewable amount of viewable tiles high</param>
/// <param name="Columns">Columns of view available</param>
/// <param name="Rows">Rows of view available</param>
public readonly record struct TileGridViewport([ConstantExpected] byte ViewWidth, [ConstantExpected] byte ViewHeight, byte Columns, byte Rows)
{
/// <summary>
/// Total width of the entire grid (including the view).
/// </summary>
public int TotalWidth => Columns * ViewWidth;
/// <summary>
/// Total height of the entire grid (including the view).
/// </summary>
public int TotalHeight => Rows * ViewHeight;
/// <summary>
/// Amount of tiles present in the grid.
/// </summary>
public int ViewCount => ViewWidth * ViewHeight;
/// <summary>
/// Amount of ALL tiles present in the entire grid (including the grid).
/// </summary>
public int TotalCount => TotalWidth * TotalHeight;
public int GetTileIndex(int acreX, int acreY, int gridX, int gridY)
{
var x = (acreX * ViewWidth) + gridX;
var y = (acreY * ViewHeight) + gridY;
return GetTileIndex(x, y);
}
/// <summary>
/// Gets the absolute index of the absolute tile in the grid based on the x/y coordinates.
/// </summary>
/// <param name="x">Absolute x coordinate of the tile in the grid</param>
/// <param name="y">Absolute y coordinate of the tile in the grid</param>
/// <returns>Absolute index of the tile in the grid</returns>
public int GetTileIndex(in int x, in int y) => (TotalHeight * x) + y;
public void ClampInside(ref int x, ref int y) => ClampCoordinatesTo(ref x, ref y, TotalWidth - 1, TotalHeight - 1);
private static void ClampCoordinatesTo(ref int x, ref int y, int maxX, int maxY)
{
x = Math.Clamp(x, 0, maxX);
y = Math.Clamp(y, 0, maxY);
}
public void GetViewAnchorCoordinates(ref int x, ref int y, in 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.
// Adjust the view coordinate
if (!centerReticle)
{
// Reticle size is GridWidth, center = /2
x -= ViewWidth / 2;
y -= ViewWidth / 2;
}
// Clamp to viewport dimensions, and center to nearest acre if desired.
// Clamp to boundaries so that we always have a full grid to view.
SetTopLeftNearest(ref x, ref y);
}
private void SetTopLeftNearest(ref int x, ref int y)
{
int maxX = TotalWidth - ViewWidth;
int maxY = TotalHeight - ViewHeight;
ClampCoordinatesTo(ref x, ref y, maxX, maxY);
}
}

View File

@ -4,17 +4,12 @@
namespace NHSE.Core;
public class MapManager : MapTerrainStructure
public class MapManager(MainSave sav) : MapTerrainStructure(sav)
{
public readonly FieldItemManager Items;
public readonly FieldItemManager Items = new(sav);
public int MapLayer { get; set; } // 0 or 1
public MapManager(MainSave sav) : base(sav)
{
Items = new FieldItemManager(sav);
}
public FieldItemLayer CurrentLayer => MapLayer == 0 ? Items.Layer1 : Items.Layer2;
public static void ClearDesignTiles(MainSave sav)
@ -41,19 +36,11 @@ public static void ImportDesignTiles(MainSave sav, ReadOnlySpan<byte> result)
}
}
public class MapTerrainStructure
public class MapTerrainStructure(MainSave sav)
{
public readonly TerrainLayer Terrain;
public readonly IReadOnlyList<Building> Buildings;
public readonly TerrainLayer Terrain = new(sav.GetTerrainTiles(), sav.GetAcreBytes());
public readonly IReadOnlyList<Building> Buildings = sav.Buildings;
public uint PlazaX { get; set; }
public uint PlazaY { get; set; }
public MapTerrainStructure(MainSave sav)
{
Terrain = new TerrainLayer(sav.GetTerrainTiles(), sav.GetAcreBytes());
Buildings = sav.Buildings;
PlazaX = sav.EventPlazaLeftUpX;
PlazaY = sav.EventPlazaLeftUpZ;
}
public uint PlazaX { get; set; } = sav.EventPlazaLeftUpX;
public uint PlazaY { get; set; } = sav.EventPlazaLeftUpZ;
}

View File

@ -22,9 +22,9 @@ protected MapView(MapManager m, int scale = 16)
}
public bool CanUp => Y != 0;
public bool CanDown => Y < Map.CurrentLayer.MaxHeight - Map.CurrentLayer.GridHeight;
public bool CanDown => Y < Map.CurrentLayer.TileInfo.TotalHeight - Map.CurrentLayer.TileInfo.ViewHeight;
public bool CanLeft => X != 0;
public bool CanRight => X < Map.CurrentLayer.MaxWidth - Map.CurrentLayer.GridWidth;
public bool CanRight => X < Map.CurrentLayer.TileInfo.TotalWidth - Map.CurrentLayer.TileInfo.ViewWidth;
public bool ArrowUp()
{
@ -44,7 +44,7 @@ public bool ArrowLeft()
public bool ArrowRight()
{
if (X >= Map.CurrentLayer.MaxWidth - 2)
if (X >= Map.CurrentLayer.TileInfo.TotalWidth - 2)
return false;
X += ViewInterval;
return true;
@ -52,7 +52,7 @@ public bool ArrowRight()
public bool ArrowDown()
{
if (Y >= Map.CurrentLayer.MaxHeight - ViewInterval)
if (Y >= Map.CurrentLayer.TileInfo.TotalHeight - ViewInterval)
return false;
Y += ViewInterval;
return true;
@ -61,8 +61,8 @@ public bool ArrowDown()
public bool SetViewTo(in int x, in int y)
{
var info = Map.CurrentLayer;
var newX = Math.Max(0, Math.Min(x, info.MaxWidth - info.GridWidth));
var newY = Math.Max(0, Math.Min(y, info.MaxHeight - info.GridHeight));
var newX = Math.Max(0, Math.Min(x, info.TileInfo.TotalWidth - info.TileInfo.ViewWidth));
var newY = Math.Max(0, Math.Min(y, info.TileInfo.TotalHeight - info.TileInfo.ViewHeight));
bool diff = X != newX || Y != newY;
X = newX;
Y = newY;
@ -80,16 +80,16 @@ public int ModifyFieldItems(Func<int, int, int, int, int> action, in bool wholeM
{
var layer = Map.CurrentLayer;
return wholeMap
? action(0, 0, layer.MaxWidth, layer.MaxHeight)
: action(X, Y, layer.GridWidth, layer.GridHeight);
? action(0, 0, layer.TileInfo.TotalWidth, layer.TileInfo.TotalHeight)
: action(X, Y, layer.TileInfo.ViewWidth, layer.TileInfo.ViewHeight);
}
public int ReplaceFieldItems(Item oldItem, Item newItem, in bool wholeMap)
{
var layer = Map.CurrentLayer;
return wholeMap
? layer.ReplaceAll(oldItem, newItem, 0, 0, layer.MaxWidth, layer.MaxHeight)
: layer.ReplaceAll(oldItem, newItem, X, Y, layer.GridWidth, layer.GridHeight);
? layer.ReplaceAll(oldItem, newItem, 0, 0, layer.TileInfo.TotalWidth, layer.TileInfo.TotalHeight)
: layer.ReplaceAll(oldItem, newItem, X, Y, layer.TileInfo.ViewWidth, layer.TileInfo.ViewHeight);
}
public void GetCursorCoordinates(in int mX, in int mY, out int x, out int y)
@ -102,6 +102,6 @@ public void GetViewAnchorCoordinates(int mX, int mY, out int x, out int y, bool
{
GetCursorCoordinates(mX, mY, out x, out y);
var layer = Map.Items.Layer1;
layer.GetViewAnchorCoordinates(ref x, ref y, centerReticle);
layer.TileInfo.GetViewAnchorCoordinates(ref x, ref y, centerReticle);
}
}

View File

@ -9,7 +9,7 @@ public static class ItemLayerSprite
public static Bitmap GetBitmapItemLayer(ItemLayer layer)
{
var items = layer.Tiles;
var height = layer.MaxHeight;
var height = layer.TileInfo.TotalHeight;
var width = items.Length / height;
var bmpData = new int[width * height];
@ -34,7 +34,7 @@ private static void LoadBitmapLayer(ReadOnlySpan<Item> items, Span<int> bmpData,
private static void LoadPixelsFromLayer(ItemLayer layer, int x0, int y0, int width, Span<int> bmpData)
{
var stride = layer.GridWidth;
var stride = layer.TileInfo.ViewWidth;
for (int y = 0; y < stride; y++)
{
@ -52,8 +52,8 @@ private static void LoadPixelsFromLayer(ItemLayer layer, int x0, int y0, int wid
// non-allocation image generator
public static Bitmap GetBitmapItemLayerViewGrid(ItemLayer layer, int x0, int y0, int scale, Span<int> acre1, int[] acreScale, Bitmap dest, int transparency = -1, int gridlineColor = 0)
{
var w = layer.GridWidth;
var h = layer.GridHeight;
int w = layer.TileInfo.ViewWidth;
int h = layer.TileInfo.ViewHeight;
LoadPixelsFromLayer(layer, x0, y0, w, acre1);
w *= scale;
h *= scale;
@ -75,9 +75,9 @@ public static Bitmap GetBitmapItemLayerViewGrid(ItemLayer layer, int x0, int y0,
private static void DrawDirectionals(Span<int> data, ItemLayer layer, int w, int x0, int y0, int scale)
{
for (int x = x0; x < x0 + layer.GridWidth; x++)
for (int x = x0; x < x0 + layer.TileInfo.ViewWidth; x++)
{
for (int y = y0; y < y0 + layer.GridHeight; y++)
for (int y = y0; y < y0 + layer.TileInfo.ViewHeight; y++)
{
var tile = layer.GetTile(x, y);
if (tile.IsNone)
@ -234,20 +234,20 @@ public static void DrawGrid(Span<int> data, int w, int h, int scale, int gridlin
public static Bitmap GetBitmapItemLayer(ItemLayer layer, int x, int y, int[] data, Bitmap dest, int transparency = -1)
{
LoadBitmapLayer(layer.Tiles, data, layer.MaxWidth, layer.MaxHeight);
LoadBitmapLayer(layer.Tiles, data, layer.TileInfo.TotalWidth, layer.TileInfo.TotalHeight);
if (transparency >>> 24 != 0xFF)
ImageUtil.ClampAllTransparencyTo(data, transparency);
ImageUtil.SetBitmapData(dest, data);
return DrawViewReticle(dest, layer, x, y);
return DrawViewReticle(dest, layer.TileInfo, x, y);
}
private static Bitmap DrawViewReticle(Bitmap map, TileGrid g, int x, int y, int scale = 1)
private static Bitmap DrawViewReticle(Bitmap map, TileGridViewport g, int x, int y, int scale = 1)
{
using var gfx = Graphics.FromImage(map);
using var pen = new Pen(Color.Red);
int w = g.GridWidth * scale;
int h = g.GridHeight * scale;
int w = g.ViewWidth * scale;
int h = g.ViewHeight * scale;
gfx.DrawRectangle(pen, x * scale, y * scale, w, h);
return map;
}

View File

@ -23,12 +23,13 @@ public sealed class MapViewer : MapView, IDisposable
public MapViewer(MapManager m, int scale) : base(m, scale)
{
var l1 = m.Items.Layer1;
PixelsItemAcre1 = new int[l1.GridWidth * l1.GridHeight];
var info = l1.TileInfo;
PixelsItemAcre1 = new int[info.ViewWidth * info.ViewHeight];
PixelsItemAcreX = new int[PixelsItemAcre1.Length * AcreScale * AcreScale];
ScaleAcre = new Bitmap(l1.GridWidth * AcreScale, l1.GridHeight * AcreScale);
ScaleAcre = new Bitmap(info.ViewWidth * AcreScale, info.ViewHeight * AcreScale);
PixelsItemMap = new int[l1.MaxWidth * l1.MaxHeight * MapScale * MapScale];
MapReticle = new Bitmap(l1.MaxWidth * MapScale, l1.MaxHeight * MapScale);
PixelsItemMap = new int[info.TotalWidth * info.TotalHeight * MapScale * MapScale];
MapReticle = new Bitmap(info.TotalWidth * MapScale, info.TotalHeight * MapScale);
PixelsBackgroundAcre1 = new int[(int)Math.Pow(16, 4)];
PixelsBackgroundAcreX = new int[PixelsItemAcreX.Length];

View File

@ -20,9 +20,9 @@ public static class TerrainSprite
public static void CreateMap(TerrainLayer mgr, Span<int> pixels)
{
int i = 0;
for (int y = 0; y < mgr.MaxHeight; y++)
for (int y = 0; y < mgr.TileInfo.TotalHeight; y++)
{
for (int x = 0; x < mgr.MaxWidth; x++, i++)
for (int x = 0; x < mgr.TileInfo.TotalWidth; x++, i++)
{
pixels[i] = mgr.GetTileColor(x, y, x, y);
}
@ -38,20 +38,20 @@ public static Bitmap CreateMap(TerrainLayer mgr, Span<int> scale1, int[] scaleX,
if (acreIndex < 0)
return map;
var acre = MapGrid.Acres[acreIndex];
var x = acre.X * mgr.GridWidth;
var y = acre.Y * mgr.GridHeight;
var acre = AcreCoordinate.Acres[acreIndex];
var x = acre.X * mgr.TileInfo.ViewWidth;
var y = acre.Y * mgr.TileInfo.ViewHeight;
return DrawReticle(map, mgr, x, y, scale);
return DrawReticle(map, mgr.TileInfo, x, y, scale);
}
private static Bitmap DrawReticle(Bitmap map, TileGrid mgr, int x, int y, int scale)
private static Bitmap DrawReticle(Bitmap map, TileGridViewport mgr, int x, int y, int scale)
{
using var gfx = Graphics.FromImage(map);
using var pen = new Pen(Color.Red);
int w = mgr.GridWidth * scale;
int h = mgr.GridHeight * scale;
int w = mgr.ViewWidth * scale;
int h = mgr.ViewHeight * scale;
gfx.DrawRectangle(pen, x * scale, y * scale, w, h);
return map;
}

View File

@ -116,7 +116,7 @@ private static int SpawnItems(ItemLayer layer, IReadOnlyList<Item> items, int x,
var permission = layer.IsOccupied(item, x, y);
switch (permission)
{
case PlacedItemPermission.OutOfBounds when y >= layer.MaxHeight:
case PlacedItemPermission.OutOfBounds when y >= layer.TileInfo.TotalHeight:
return ctr;
case PlacedItemPermission.OutOfBounds:
case PlacedItemPermission.Collision when noOverwrite:

View File

@ -57,11 +57,9 @@ public FieldItemEditor(MainSave sav)
private void LoadComboBoxes()
{
foreach (var acre in MapGrid.Acres)
foreach (var acre in AcreCoordinate.Acres)
CB_Acre.Items.Add(acre.Name);
var exterior = AcreCoordinate.GetGridWithExterior(9, 8);
foreach (var acre in exterior)
foreach (var acre in AcreCoordinate.Exterior)
CB_MapAcre.Items.Add(acre.Name);
CB_MapAcreSelect.DisplayMember = nameof(ComboItem.Text);
@ -349,8 +347,8 @@ private void ViewTile(Item tile, int x, int y)
if (CHK_RedirectExtensionLoad.Checked && tile.IsExtension)
{
var l = Map.CurrentLayer;
var rx = Math.Max(0, Math.Min(l.MaxWidth - 1, x - tile.ExtensionX));
var ry = Math.Max(0, Math.Min(l.MaxHeight - 1, y - tile.ExtensionY));
var rx = Math.Max(0, Math.Min(l.TileInfo.TotalWidth - 1, x - tile.ExtensionX));
var ry = Math.Max(0, Math.Min(l.TileInfo.TotalHeight - 1, y - tile.ExtensionY));
var redir = l.GetTile(rx, ry);
if (redir.IsRoot && redir.ItemId == tile.ExtensionItemId)
tile = redir;
@ -511,7 +509,8 @@ private void DeleteTile(TerrainTile tile)
private void B_Save_Click(object sender, EventArgs e)
{
var unsupported = Map.Items.GetUnsupportedTiles();
var view = View.Map.Items.Layer1.TileInfo;
var unsupported = Map.Items.GetUnsupportedTiles(view.TotalWidth, view.TotalHeight);
if (unsupported.Count != 0)
{
var err = MessageStrings.MsgFieldItemUnsupportedLayer2Tile;
@ -524,7 +523,7 @@ private void B_Save_Click(object sender, EventArgs e)
Map.Items.Save();
SAV.SetTerrainTiles(Map.Terrain.Tiles);
SAV.SetAcreBytes(Map.Terrain.BaseAcres);
SAV.SetAcreBytes(Map.Terrain.BaseAcres.Span);
SAV.OutsideFieldTemplateUniqueId = (ushort)NUD_MapAcreTemplateOutside.Value;
SAV.MainFieldParamUniqueID = (ushort)NUD_MapAcreTemplateField.Value;
@ -616,7 +615,7 @@ private void Menu_Activate_Click(object sender, EventArgs e)
private void B_Up_Click(object sender, EventArgs e)
{
if (ModifierKeys == Keys.Shift)
CB_Acre.SelectedIndex = Math.Max(0, CB_Acre.SelectedIndex - MapGrid.AcreWidth);
CB_Acre.SelectedIndex = Math.Max(0, CB_Acre.SelectedIndex - View.Map.Items.Layer1.TileInfo.Columns);
else if (View.ArrowUp())
LoadItemGridAcre();
}
@ -640,7 +639,7 @@ private void B_Right_Click(object sender, EventArgs e)
private void B_Down_Click(object sender, EventArgs e)
{
if (ModifierKeys == Keys.Shift)
CB_Acre.SelectedIndex = Math.Min(CB_Acre.SelectedIndex + MapGrid.AcreWidth, CB_Acre.Items.Count - 1);
CB_Acre.SelectedIndex = Math.Min(CB_Acre.SelectedIndex + View.Map.Items.Layer1.TileInfo.Columns, CB_Acre.Items.Count - 1);
else if (View.ArrowDown())
LoadItemGridAcre();
}
@ -989,7 +988,7 @@ private void NUD_BuildingType_ValueChanged(object sender, EventArgs e)
private void CB_MapAcre_SelectedIndexChanged(object sender, EventArgs e)
{
var acre = Map.Terrain.BaseAcres[CB_MapAcre.SelectedIndex * 2];
var acre = Map.Terrain.BaseAcres.Span[CB_MapAcre.SelectedIndex * 2];
CB_MapAcreSelect.SelectedValue = (int)acre;
// Jump view if available
@ -1005,7 +1004,7 @@ private void CB_MapAcreSelect_SelectedValueChanged(object sender, EventArgs e)
var index = CB_MapAcre.SelectedIndex;
var value = WinFormsUtil.GetIndex(CB_MapAcreSelect);
var span = Map.Terrain.BaseAcres.AsSpan(index * 2, 2);
var span = Map.Terrain.BaseAcres.Span.Slice(index * 2, 2);
var oldValue = span[0];
if (value == oldValue)
return;
@ -1016,7 +1015,7 @@ private void CB_MapAcreSelect_SelectedValueChanged(object sender, EventArgs e)
private void B_DumpMapAcres_Click(object sender, EventArgs e)
{
if (!MapDumpHelper.DumpMapAcresAll(Map.Terrain.BaseAcres))
if (!MapDumpHelper.DumpMapAcresAll(Map.Terrain.BaseAcres.Span))
return;
ReloadBuildingsTerrain();
System.Media.SystemSounds.Asterisk.Play();
@ -1024,7 +1023,7 @@ private void B_DumpMapAcres_Click(object sender, EventArgs e)
private void B_ImportMapAcres_Click(object sender, EventArgs e)
{
if (!MapDumpHelper.ImportMapAcresAll(Map.Terrain.BaseAcres))
if (!MapDumpHelper.ImportMapAcresAll(Map.Terrain.BaseAcres.Span))
return;
ReloadBuildingsTerrain();
System.Media.SystemSounds.Asterisk.Play();
@ -1108,7 +1107,7 @@ private void Menu_Bulk_Click(object sender, EventArgs e)
{
var editor = new BatchEditor(SpawnLayer.Tiles, ItemEdit.SetItem(new Item()));
editor.ShowDialog();
SpawnLayer.ClearDanglingExtensions(0, 0, SpawnLayer.MaxWidth, SpawnLayer.MaxHeight);
SpawnLayer.ClearDanglingExtensions(0, 0, SpawnLayer.TileInfo.TotalWidth, SpawnLayer.TileInfo.TotalHeight);
LoadItemGridAcre();
}

View File

@ -19,7 +19,7 @@ public static bool ImportToLayerAcreSingle(FieldItemLayer layer, int acreIndex,
var path = ofd.FileName;
var fi = new FileInfo(path);
int expect = layer.GridTileCount * Item.SIZE;
int expect = layer.TileInfo.ViewCount * Item.SIZE;
if (fi.Length != expect)
{
WinFormsUtil.Error(string.Format(MessageStrings.MsgDataSizeMismatchImport, fi.Length, expect));
@ -42,7 +42,7 @@ public static bool ImportToLayerAcreAll(FieldItemLayer layer)
var path = ofd.FileName;
var fi = new FileInfo(path);
int expect = layer.MaxTileCount * Item.SIZE;
int expect = layer.TileInfo.TotalCount * Item.SIZE;
if (fi.Length != expect)
{
WinFormsUtil.Error(string.Format(MessageStrings.MsgDataSizeMismatchImport, fi.Length, expect));
@ -91,7 +91,7 @@ public static bool ImportTerrainAcre(TerrainLayer m, int acreIndex, string acre)
var path = ofd.FileName;
var fi = new FileInfo(path);
int expect = m.GridTileCount * TerrainTile.SIZE;
int expect = m.TileInfo.ViewCount * TerrainTile.SIZE;
if (fi.Length != expect)
{
WinFormsUtil.Error(string.Format(MessageStrings.MsgDataSizeMismatchImport, fi.Length, expect));
@ -114,7 +114,7 @@ public static bool ImportTerrainAll(TerrainLayer m)
var path = ofd.FileName;
var fi = new FileInfo(path);
int expect = m.MaxTileCount * TerrainTile.SIZE;
int expect = m.TileInfo.TotalCount * TerrainTile.SIZE;
if (fi.Length != expect)
{
WinFormsUtil.Error(string.Format(MessageStrings.MsgDataSizeMismatchImport, fi.Length, expect));

View File

@ -135,7 +135,7 @@ private void SetHoveredItem(MouseEventArgs e)
GetCoordinates(e, out HoverX, out HoverY);
// Mouse event may fire with a slightly too large x/y; clamp just in case.
Manager.Layers[0].ClampCoordinatesInsideGrid(ref HoverX, ref HoverY);
Manager.Layers[0].TileInfo.ClampInside(ref HoverX, ref HoverY);
}
private static void GetCoordinates(MouseEventArgs e, out int x, out int y)
@ -164,8 +164,8 @@ private void ReloadManager(IPlayerHouse house)
private void DrawRoom(ItemLayer layer)
{
var w = layer.MaxWidth;
var h = layer.MaxHeight;
var w = layer.TileInfo.TotalWidth;
var h = layer.TileInfo.TotalHeight;
Span<int> scale1 = stackalloc int[w * h];
int[] scaleX = new int[scale * scale * scale1.Length];
var bmp = new Bitmap(scale * w, scale * h);
@ -240,8 +240,8 @@ private void ViewTile(Item tile, int x, int y)
if (CHK_RedirectExtensionLoad.Checked && tile.IsExtension)
{
var l = CurrentLayer;
var rx = Math.Max(0, Math.Min(l.MaxWidth - 1, x - tile.ExtensionX));
var ry = Math.Max(0, Math.Min(l.MaxHeight - 1, y - tile.ExtensionY));
var rx = Math.Max(0, Math.Min(l.TileInfo.TotalWidth - 1, x - tile.ExtensionX));
var ry = Math.Max(0, Math.Min(l.TileInfo.TotalHeight - 1, y - tile.ExtensionY));
var redir = l.GetTile(rx, ry);
if (redir.IsRoot && redir.ItemId == tile.ExtensionItemId)
tile = redir;