diff --git a/NHSE.Core/Drawing/AcreTileColor.cs b/NHSE.Core/Drawing/AcreTileColor.cs index b422edb..dc59bdd 100644 --- a/NHSE.Core/Drawing/AcreTileColor.cs +++ b/NHSE.Core/Drawing/AcreTileColor.cs @@ -16,6 +16,6 @@ public static int GetAcreTileColor(ushort acre, int x, int y) var shift = (4 * ((y * 64) + x)); var ofs = baseOfs + shift; var tile = AcreTiles[ofs]; - return CollisionUtil.Dict[tile].ToArgb(); + return TileCollisionUtil.Dict[tile].ToArgb(); } } \ No newline at end of file diff --git a/NHSE.Core/Editing/Batch/ItemMutator.cs b/NHSE.Core/Editing/Batch/ItemMutator.cs index bfa3c35..2c7af56 100644 --- a/NHSE.Core/Editing/Batch/ItemMutator.cs +++ b/NHSE.Core/Editing/Batch/ItemMutator.cs @@ -7,7 +7,7 @@ namespace NHSE.Core; -public class ItemMutator : BatchMutator +public sealed class ItemMutator : BatchMutator { public readonly ItemReflection Reflect = ItemReflection.Default; private const char CONST_POINTER = '*'; diff --git a/NHSE.Core/Editing/Batch/ItemProcessor.cs b/NHSE.Core/Editing/Batch/ItemProcessor.cs index fb8dcae..5b54b48 100644 --- a/NHSE.Core/Editing/Batch/ItemProcessor.cs +++ b/NHSE.Core/Editing/Batch/ItemProcessor.cs @@ -4,7 +4,7 @@ namespace NHSE.Core; -public class ItemProcessor(BatchMutator mut) : BatchProcessor(mut) +public sealed class ItemProcessor(BatchMutator mut) : BatchProcessor(mut) { protected override bool CanModify(Item item) => true; protected override bool Finalize(Item item) => true; diff --git a/NHSE.Core/Editing/Batch/ItemReflection.cs b/NHSE.Core/Editing/Batch/ItemReflection.cs index 24aecc6..eee580b 100644 --- a/NHSE.Core/Editing/Batch/ItemReflection.cs +++ b/NHSE.Core/Editing/Batch/ItemReflection.cs @@ -5,7 +5,7 @@ namespace NHSE.Core; -public class ItemReflection +public sealed class ItemReflection { public static ItemReflection Default { get; } = new(); diff --git a/NHSE.Core/Editing/FieldItem/FieldItemColumn.cs b/NHSE.Core/Editing/FieldItem/FieldItemColumn.cs index c45f77f..45a1839 100644 --- a/NHSE.Core/Editing/FieldItem/FieldItemColumn.cs +++ b/NHSE.Core/Editing/FieldItem/FieldItemColumn.cs @@ -1,23 +1,13 @@ namespace NHSE.Core; -public sealed class FieldItemColumn -{ - /// X Coordinate within the Field Item Layer - public readonly int X; - - /// Y Coordinate within the Field Item Layer - public readonly int Y; - - /// Offset relative to the start of the Field Item Layer - public readonly int Offset; - - public readonly byte[] Data; - - public FieldItemColumn(int x, int y, int offset, byte[] data) - { - X = x; - Y = y; - Offset = offset; - Data = data; - } -} \ No newline at end of file +/// +/// Represents a list of item data tiles to be injected to a Field Item Layer. +/// +/// +/// Extension tiles underneath the actual item root are included; extension tiles to the right are not. +/// +/// X Coordinate within the Field Item Layer +/// Y Coordinate within the Field Item Layer +/// Offset relative to the start of the Field Item Layer +/// Data for this column +public sealed record FieldItemColumn(int RelativeX, int RelativeY, int Offset, byte[] Data); \ No newline at end of file diff --git a/NHSE.Core/Editing/FieldItem/FieldItemDropper.cs b/NHSE.Core/Editing/FieldItem/FieldItemDropper.cs index 021dd9c..ca85ff7 100644 --- a/NHSE.Core/Editing/FieldItem/FieldItemDropper.cs +++ b/NHSE.Core/Editing/FieldItem/FieldItemDropper.cs @@ -7,10 +7,12 @@ namespace NHSE.Core; /// /// Converts into columns of writable Item tiles. /// -public static class FieldItemDropper +/// Field item acre width. 7 on pre-3.0.0 saves, 9 on 3.0.0+ +/// Always 6. +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 * LayerFieldItem.TilesPerAcreDim; + private int MapWidth => AcreHeight * LayerFieldItem.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 /// Count of items tall the overall spawn-rectangle is. /// Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped. /// Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped. - 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 /// Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped. /// Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped. /// True if can fit, false if not. - 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 InjectItemsAsDropped(int mapX, int mapY, IReadOnlyList item) + public IReadOnlyList InjectItemsAsDropped(int mapX, int mapY, IReadOnlyList item) { int yStride = (item.Count > 16) ? 16 : item.Count; return InjectItemsAsDropped(mapX, mapY, item, yStride); } - public static IReadOnlyList InjectItemsAsDropped(int mapX, int mapY, IReadOnlyList item, int yStride) + public IReadOnlyList InjectItemsAsDropped(int mapX, int mapY, IReadOnlyList item, int yStride) { var xStride = item.Count / yStride; List result = new(yStride * xStride); @@ -108,10 +110,7 @@ private static byte[] GetColumnExtension(ReadOnlySpan items) return Item.SetArray(col); } - private static int GetTileOffset(int x, int y) - { - return Item.SIZE * (y + (x * MapHeight)); - } + private int GetTileOffset(int relX, int relY) => Item.SIZE * (relY + (relX * MapHeight)); private static Item GetDroppedItem(Item item) { diff --git a/NHSE.Core/Editing/FieldItem/FieldItemFlagUpgrade.cs b/NHSE.Core/Editing/FieldItem/FieldItemFlagUpgrade.cs new file mode 100644 index 0000000..1917122 --- /dev/null +++ b/NHSE.Core/Editing/FieldItem/FieldItemFlagUpgrade.cs @@ -0,0 +1,141 @@ +using System; + +namespace NHSE.Core; + +/// +/// Provides functionality to upgrade or downgrade field item flag data between different formats. +/// +public static class FieldItemFlagUpgrade +{ + private const int ColumnCountOld = 7; + private const int ColumnCountNew = 9; // +1 column on each side + private const int RowCount = 6; + + private const byte SizeDim = LayerFieldItem.TilesPerAcreDim; + + // Tile dimensions across the entire map + private const int TileRowCount = RowCount * SizeDim; // 192 tile rows + private const int TilesPerRowOld = ColumnCountOld * SizeDim; // 224 tiles per row + private const int TilesPerRowNew = ColumnCountNew * SizeDim; // 288 tiles per row + + // Bytes per tile-row (1 bit per tile, 8 tiles per byte) + private const int BytesPerRowOld = TilesPerRowOld / 8; // 28 bytes + private const int BytesPerRowNew = TilesPerRowNew / 8; // 36 bytes + + // Bytes for one acre column per row (32 tiles / 8 bits per byte = 4 bytes) + private const int BytesPerAcreColumnPerRow = SizeDim / 8; // 4 bytes + + // Total sizes + private const int FlagSizeOld = TileRowCount * BytesPerRowOld; // 5,376 bytes + private const int FlagSizeNew = TileRowCount * BytesPerRowNew; // 6,912 bytes + + // Active flags are stored in row-major order. + // Since the upgrade adds a column on each side, it's not possible to simply prepend and append default columns. + // For each row of data, we need to add default flags at the start and end of the row. + // Repeat for all rows. + + /// + /// Checks if an update is needed based on the current size and expected size. + /// + /// Current size of the data. + /// Desired size of the data. + /// if an update is needed; otherwise . + public static bool IsUpdateNeeded(long current, int expect) => expect switch + { + FlagSizeOld => current == FlagSizeNew, + FlagSizeNew => current == FlagSizeOld, + _ => false, + }; + + /// + /// Detects and performs an update on the field item flag data if needed. + /// + /// Data to update. + /// Desired size of the data. + /// if an update was performed; otherwise . + /// Pre-check to ensure a conversion is available. + public static bool DetectUpdate(ref byte[] data, int expect) + { + if (expect == FlagSizeNew && data.Length == FlagSizeOld) + data = Inflate(data); + else if (expect == FlagSizeOld && data.Length == FlagSizeNew) + data = Deflate(data); + else // No change needed/supported. + return false; + return true; + } + + /// + public static byte[] Inflate(ReadOnlySpan data) + { + if (data.Length != FlagSizeOld) + throw new ArgumentException($"Data length {data.Length} does not match expected old size {FlagSizeOld}."); + + var result = new byte[FlagSizeNew]; + Inflate(data, result); + return result; + } + + /// + /// Inflates old field item flag data to the new format. + /// + /// Old field item flag data. + /// Span to write new field item flag data to. + /// + public static void Inflate(ReadOnlySpan data, Span result) + { + if (data.Length != FlagSizeOld) + throw new ArgumentException($"Data length {data.Length} does not match expected old size {FlagSizeOld}."); + if (result.Length < FlagSizeNew) + throw new ArgumentException($"Result length {result.Length} is less than expected new size {FlagSizeNew}."); + + // For each tile row: + // - 4 bytes of zeros for new left column (result is already zeroed) + // - Copy 28 bytes from old data (existing flags) + // - 4 bytes of zeros for new right column (result is already zeroed) + + for (int row = 0; row < TileRowCount; row++) + { + var srcOffset = row * BytesPerRowOld; + var dstOffset = (row * BytesPerRowNew) + BytesPerAcreColumnPerRow; + data.Slice(srcOffset, BytesPerRowOld).CopyTo(result[dstOffset..]); + } + } + + /// + public static byte[] Deflate(ReadOnlySpan data) + { + if (data.Length != FlagSizeNew) + throw new ArgumentException($"Data length {data.Length} does not match expected new size {FlagSizeNew}."); + + var result = new byte[FlagSizeOld]; + Deflate(data, result); + return result; + } + + /// + /// Deflates new field item flag data to the old format. + /// + /// New field item flag data. + /// Span to write old field item flag data to. + /// + public static void Deflate(ReadOnlySpan data, Span result) + { + if (data.Length != FlagSizeNew) + throw new ArgumentException($"Data length {data.Length} does not match expected new size {FlagSizeNew}."); + if (result.Length < FlagSizeOld) + throw new ArgumentException($"Result length {result.Length} is less than expected old size {FlagSizeOld}."); + + // For each tile row: + // - Skip 4 bytes for new left column + // - Copy 28 bytes to result (existing flags) + // - Skip 4 bytes for new right column + + for (int row = 0; row < TileRowCount; row++) + { + var srcOffset = (row * BytesPerRowNew) + BytesPerAcreColumnPerRow; + var dstOffset = row * BytesPerRowOld; + data.Slice(srcOffset, BytesPerRowOld).CopyTo(result[dstOffset..]); + } + } +} \ No newline at end of file diff --git a/NHSE.Core/Editing/FieldItem/FieldItemUpgrade.cs b/NHSE.Core/Editing/FieldItem/FieldItemUpgrade.cs new file mode 100644 index 0000000..01f8ca4 --- /dev/null +++ b/NHSE.Core/Editing/FieldItem/FieldItemUpgrade.cs @@ -0,0 +1,128 @@ +using System; +using System.Runtime.InteropServices; + +namespace NHSE.Core; + +/// +/// Provides functionality to upgrade or downgrade field item data between different formats. +/// +public static class FieldItemUpgrade +{ + private const int ColumnCountOld = 7; + private const int ColumnCountNew = 9; // +1 column on each side + private const int RowCount = 6; + + private const byte SizeDim = LayerFieldItem.TilesPerAcreDim; + private const int TilesPerAcre = SizeDim * SizeDim; + private const int FieldItemSizeSingleColumn = RowCount * TilesPerAcre * Item.SIZE; + private const int FieldItemSizeOld = ColumnCountOld * FieldItemSizeSingleColumn; + private const int FieldItemSizeNew = ColumnCountNew * FieldItemSizeSingleColumn; + + // Items are stored in column-major order. + // Since the upgrade adds a column on each side, it's easy to prepend and append default columns. + + /// + /// Checks if an update is needed based on the current size and expected size. + /// + /// Current size of the data. + /// Desired size of the data. + /// if an update is needed; otherwise . + public static bool IsUpdateNeeded(long current, int expect) => expect switch + { + FieldItemSizeOld => current == FieldItemSizeNew, + FieldItemSizeNew => current == FieldItemSizeOld, + _ => false, + }; + + /// + /// Detects and performs an update on the field item data if needed. + /// + /// Data to update. + /// Desired size of the data. + /// if an update was performed; otherwise . + /// Pre-check to ensure a conversion is available. + public static bool DetectUpdate(ref byte[] data, int expect) + { + if (expect == FieldItemSizeNew && data.Length == FieldItemSizeOld) + data = Inflate(data); + else if (expect == FieldItemSizeOld && data.Length == FieldItemSizeNew) + data = Deflate(data); + else // No change needed/supported. + return false; + return true; + } + + /// + public static byte[] Inflate(ReadOnlySpan data) + { + if (data.Length != FieldItemSizeOld) + throw new ArgumentException($"Data length {data.Length} does not match expected old size {FieldItemSizeOld}."); + + var result = new byte[FieldItemSizeNew]; + Inflate(data, result); + return result; + } + + /// + /// Inflates old field item data to the new format. + /// + /// Old field item data. + /// Span to write new field item data to. + /// New field item data. + /// + public static void Inflate(ReadOnlySpan data, Span result) + { + if (data.Length != FieldItemSizeOld) + throw new ArgumentException($"Data length {data.Length} does not match expected old size {FieldItemSizeOld}."); + if (result.Length < FieldItemSizeNew) + throw new ArgumentException($"Result length {result.Length} is less than expected new size {FieldItemSizeNew}."); + + // The first acre column is default field items. + // Then, the existing data is present. + // Finally, an acre column is default field items. + + // Prepare a default column of no items. + Span defaultColumn = stackalloc byte[FieldItemSizeSingleColumn]; + FillColumnWithItem(defaultColumn, Item.NONE); + + // First default column + defaultColumn.CopyTo(result); + // Existing data + data.CopyTo(result[FieldItemSizeSingleColumn..]); + // Last default column + defaultColumn.CopyTo(result[^FieldItemSizeSingleColumn..]); + } + + private static void FillColumnWithItem(Span defaultColumn, ulong tileValue) + { + if (!BitConverter.IsLittleEndian) + tileValue = System.Buffers.Binary.BinaryPrimitives.ReverseEndianness(tileValue); + var cast = MemoryMarshal.Cast(defaultColumn); + foreach (ref var value in cast) + value = tileValue; + } + + /// + public static byte[] Deflate(ReadOnlySpan data) + { + if (data.Length != FieldItemSizeNew) + throw new ArgumentException($"Data length {data.Length} does not match expected new size {FieldItemSizeNew}."); + return data.Slice(FieldItemSizeSingleColumn, FieldItemSizeOld).ToArray(); + } + + /// + /// Deflates new field item data to the old format. + /// + /// New field item data. + /// Span to write old field item data to. + /// Old field item data. + /// + public static void Deflate(ReadOnlySpan data, Span result) + { + if (data.Length != FieldItemSizeNew) + throw new ArgumentException($"Data length {data.Length} does not match expected new size {FieldItemSizeNew}."); + if (result.Length < FieldItemSizeOld) + throw new ArgumentException($"Result length {result.Length} is less than expected old size {FieldItemSizeOld}."); + data.Slice(FieldItemSizeSingleColumn, FieldItemSizeOld).CopyTo(result); + } +} \ No newline at end of file diff --git a/NHSE.Core/Editing/ItemRequest/ItemParser.cs b/NHSE.Core/Editing/ItemRequest/ItemParser.cs index 96bd067..daba25c 100644 --- a/NHSE.Core/Editing/ItemRequest/ItemParser.cs +++ b/NHSE.Core/Editing/ItemRequest/ItemParser.cs @@ -211,7 +211,7 @@ private static Item CreateItem(byte[] convert, int requestIndex, IConfigItem con try { if (convert.Length != Item.SIZE) - throw new Exception(); + throw new Exception($"Invalid item byte length (expected {Item.SIZE}, got {convert.Length})."); item = convert.ToClass(); } catch (Exception ex) diff --git a/NHSE.Core/Save/Files/MainSave.cs b/NHSE.Core/Save/Files/MainSave.cs index f689761..fb7bbd6 100644 --- a/NHSE.Core/Save/Files/MainSave.cs +++ b/NHSE.Core/Save/Files/MainSave.cs @@ -165,7 +165,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; @@ -193,29 +194,50 @@ public void SetAcreBytes(ReadOnlySpan data) data.CopyTo(Data[Offsets.OutsideField..]); } - public TerrainTile[] GetTerrainTiles() => TerrainTile.GetArray(Data.Slice(Offsets.LandMakingMap, MapGrid.MapTileCount16x16 * TerrainTile.SIZE)); + +#pragma warning disable CA1822 // Mark members as static + public byte FieldItemAcreWidth => Offsets.FieldItemAcreWidth; // 3.0.0 updated from 7 => 9 + // ReSharper disable once MemberCanBeMadeStatic.Global + public byte FieldItemAcreHeight => 6; // always 6 + private int FieldItemAcreCount => FieldItemAcreWidth * FieldItemAcreHeight; +#pragma warning restore CA1822 // Mark members as static + + + private const int TotalTerrainTileCount = LayerTerrain.TilesPerAcreDim * LayerTerrain.TilesPerAcreDim * (7 * 6); + private int TotalFieldItemTileCount => LayerFieldItem.TilesPerAcreDim * LayerFieldItem.TilesPerAcreDim * FieldItemAcreCount; + + public TerrainTile[] GetTerrainTiles() => TerrainTile.GetArray(Data.Slice(Offsets.LandMakingMap, TotalTerrainTileCount * TerrainTile.SIZE)); public void SetTerrainTiles(IReadOnlyList array) => TerrainTile.SetArray(array).CopyTo(Data[Offsets.LandMakingMap..]); - public const int MapDesignNone = 0xF800; + public const ushort MapDesignNone = 0xF800; public Memory MapDesignTileData => Raw.Slice(Offsets.MyDesignMap, 112 * 96 * sizeof(ushort)); public ushort[] GetMapDesignTiles() => MemoryMarshal.Cast(MapDesignTileData.Span).ToArray(); public void SetMapDesignTiles(ReadOnlySpan value) => MemoryMarshal.Cast(value).CopyTo(MapDesignTileData.Span); - private const int FieldItemLayerSize = MapGrid.MapTileCount32x32 * Item.SIZE; - private const int FieldItemFlagSize = MapGrid.MapTileCount32x32 / 8; // bitflags + public void ClearDesignTiles() + { + var tiles = GetMapDesignTiles(); + tiles.AsSpan().Fill(MapDesignNone); + SetMapDesignTiles(tiles); + } - private int FieldItemLayer1 => Offsets.FieldItem; - private int FieldItemLayer2 => Offsets.FieldItem + FieldItemLayerSize; - public int FieldItemFlag1 => Offsets.FieldItem + (FieldItemLayerSize * 2); - public int FieldItemFlag2 => Offsets.FieldItem + (FieldItemLayerSize * 2) + FieldItemFlagSize; + private int FieldItemLayerSize => TotalFieldItemTileCount * Item.SIZE; + private int FieldItemFlagSize => TotalFieldItemTileCount / sizeof(byte); // bitflags + + private int FieldItemLayer0 => Offsets.FieldItem; + private int FieldItemLayer1 => Offsets.FieldItem + FieldItemLayerSize; + public int FieldItemFlag0 => Offsets.FieldItem + (FieldItemLayerSize * 2); + public int FieldItemFlag1 => Offsets.FieldItem + (FieldItemLayerSize * 2) + FieldItemFlagSize; + public Memory FieldItemFlag0Data => Data.Slice(FieldItemFlag0, FieldItemFlagSize).ToArray(); + public Memory FieldItemFlag1Data => Data.Slice(FieldItemFlag1, FieldItemFlagSize).ToArray(); + + public Item[] GetFieldItemLayer0() => Item.GetArray(Data.Slice(FieldItemLayer0, FieldItemLayerSize)); + public void SetFieldItemLayer0(IReadOnlyList array) => Item.SetArray(array).CopyTo(Data[FieldItemLayer0..]); public Item[] GetFieldItemLayer1() => Item.GetArray(Data.Slice(FieldItemLayer1, FieldItemLayerSize)); public void SetFieldItemLayer1(IReadOnlyList array) => Item.SetArray(array).CopyTo(Data[FieldItemLayer1..]); - public Item[] GetFieldItemLayer2() => Item.GetArray(Data.Slice(FieldItemLayer2, FieldItemLayerSize)); - public void SetFieldItemLayer2(IReadOnlyList array) => Item.SetArray(array).CopyTo(Data[FieldItemLayer2..]); - public ushort OutsideFieldTemplateUniqueId { get => ReadUInt16LittleEndian(Data[(Offsets.OutsideField + AcreSizeAll)..]); diff --git a/NHSE.Core/Save/Meta/EncryptedFilePair.cs b/NHSE.Core/Save/Meta/EncryptedFilePair.cs index 14f875a..8f3c96d 100644 --- a/NHSE.Core/Save/Meta/EncryptedFilePair.cs +++ b/NHSE.Core/Save/Meta/EncryptedFilePair.cs @@ -46,12 +46,12 @@ protected EncryptedFilePair(ISaveFileProvider provider, string name) RawHeader = hd; RawData = md; - Info = RawHeader[..FileHeaderInfo.SIZE].ToClass(); + Info = Header[..FileHeaderInfo.SIZE].ToArray().ToClass(); } public void Save(uint seed) { - var encrypt = Encryption.Encrypt(RawData, seed, RawHeader); + var encrypt = Encryption.Encrypt(Data, seed, Header); Provider.WriteFile(NameData, encrypt.Data.Span); Provider.WriteFile(NameHeader, encrypt.Header.Span); } diff --git a/NHSE.Core/Save/Meta/HorizonSave.cs b/NHSE.Core/Save/Meta/HorizonSave.cs index 1719c88..880ff67 100644 --- a/NHSE.Core/Save/Meta/HorizonSave.cs +++ b/NHSE.Core/Save/Meta/HorizonSave.cs @@ -11,11 +11,10 @@ namespace NHSE.Core; /// Creates a HorizonSave from a file provider. /// /// Provider for reading/writing save files. -public class HorizonSave(ISaveFileProvider provider) +public sealed class HorizonSave(ISaveFileProvider provider) { public readonly MainSave Main = new(provider); - public readonly Player[] Players = Player.ReadMany(provider); - private readonly ISaveFileProvider Provider = provider; + public readonly IReadOnlyList Players = Player.ReadMany(provider); public override string ToString() => $"{Players[0].Personal.TownName} - {Players[0]}"; @@ -57,7 +56,7 @@ public void Save(uint seed) pair.Save(seed); } } - Provider.Flush(); + provider.Flush(); } /// diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets.cs index 5a97a6b..3cc7194 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets.cs @@ -62,7 +62,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 data); public abstract IVillagerHouse ReadVillagerHouse(Memory data); diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets10.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets10.cs index 6648499..bc079e2 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets10.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets10.cs @@ -5,7 +5,7 @@ namespace NHSE.Core; /// /// /// -public class MainSaveOffsets10 : MainSaveOffsets +public sealed class MainSaveOffsets10 : MainSaveOffsets { #region GSaveLand public const int GSaveLandStart = 0x108; diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets11.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets11.cs index 8437bf7..e7f986f 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets11.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets11.cs @@ -5,7 +5,7 @@ namespace NHSE.Core; /// /// /// -public class MainSaveOffsets11 : MainSaveOffsets +public sealed class MainSaveOffsets11 : MainSaveOffsets { #region GSaveLand public const int GSaveLandStart = 0x110; diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets110.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets110.cs index d4f9957..9b48068 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets110.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets110.cs @@ -5,7 +5,7 @@ namespace NHSE.Core; /// /// /// -public class MainSaveOffsets110 : MainSaveOffsets +public sealed class MainSaveOffsets110 : MainSaveOffsets { public override int PatternCount => PatternCount2; diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets111.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets111.cs index 8be8cd9..4c8a7b4 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets111.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets111.cs @@ -6,7 +6,7 @@ namespace NHSE.Core; /// /// /// Same as -public class MainSaveOffsets111 : MainSaveOffsets +public sealed class MainSaveOffsets111 : MainSaveOffsets { public override int PatternCount => PatternCount2; diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets12.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets12.cs index 3a9cdd2..49f022e 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets12.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets12.cs @@ -5,7 +5,7 @@ namespace NHSE.Core; /// /// /// -public class MainSaveOffsets12 : MainSaveOffsets +public sealed class MainSaveOffsets12 : MainSaveOffsets { #region GSaveLand public const int GSaveLandStart = 0x110; diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets13.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets13.cs index ef29e22..02865b7 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets13.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets13.cs @@ -5,7 +5,7 @@ namespace NHSE.Core; /// /// /// -public class MainSaveOffsets13 : MainSaveOffsets +public sealed class MainSaveOffsets13 : MainSaveOffsets { #region GSaveLand public const int GSaveLandStart = 0x110; diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets14.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets14.cs index 7770ace..6809d37 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets14.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets14.cs @@ -5,7 +5,7 @@ namespace NHSE.Core; /// /// /// -public class MainSaveOffsets14 : MainSaveOffsets +public sealed class MainSaveOffsets14 : MainSaveOffsets { #region GSaveLand public const int GSaveLandStart = 0x110; diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets15.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets15.cs index 8a9c272..903eea1 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets15.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets15.cs @@ -5,7 +5,7 @@ namespace NHSE.Core; /// /// /// -public class MainSaveOffsets15 : MainSaveOffsets +public sealed class MainSaveOffsets15 : MainSaveOffsets { #region GSaveLand public const int GSaveLandStart = 0x110; diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets16.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets16.cs index febeb51..75300ed 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets16.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets16.cs @@ -5,7 +5,7 @@ namespace NHSE.Core; /// /// /// -public class MainSaveOffsets16 : MainSaveOffsets +public sealed class MainSaveOffsets16 : MainSaveOffsets { #region GSaveLand public const int GSaveLandStart = 0x110; diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets17.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets17.cs index 4fa3143..7704845 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets17.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets17.cs @@ -5,7 +5,7 @@ namespace NHSE.Core; /// /// /// -public class MainSaveOffsets17 : MainSaveOffsets +public sealed class MainSaveOffsets17 : MainSaveOffsets { #region GSaveLand public const int GSaveLandStart = 0x110; diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets18.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets18.cs index 8f0f69b..c260903 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets18.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets18.cs @@ -6,7 +6,7 @@ namespace NHSE.Core; /// /// /// Same as . -public class MainSaveOffsets18 : MainSaveOffsets +public sealed class MainSaveOffsets18 : MainSaveOffsets { #region GSaveLand public const int GSaveLandStart = 0x110; diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets19.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets19.cs index 28763a9..b201510 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets19.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets19.cs @@ -5,7 +5,7 @@ namespace NHSE.Core; /// /// /// -public class MainSaveOffsets19 : MainSaveOffsets +public sealed class MainSaveOffsets19 : MainSaveOffsets { public override int PatternCount => PatternCount2; diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets20.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets20.cs index db97961..b63cb10 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets20.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets20.cs @@ -6,7 +6,7 @@ namespace NHSE.Core; /// /// /// Same as -public class MainSaveOffsets20 : MainSaveOffsets +public sealed class MainSaveOffsets20 : MainSaveOffsets { public override int PatternCount => PatternCount2; diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets30.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets30.cs index 22d9751..9aac9f9 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets30.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets30.cs @@ -5,7 +5,7 @@ namespace NHSE.Core; /// /// /// -public class MainSaveOffsets30 : MainSaveOffsets +public sealed class MainSaveOffsets30 : MainSaveOffsets { public override int PatternCount => PatternCount2; @@ -35,7 +35,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 diff --git a/NHSE.Core/Structures/Building/Building.cs b/NHSE.Core/Structures/Building/Building.cs index 92b4e16..3975db1 100644 --- a/NHSE.Core/Structures/Building/Building.cs +++ b/NHSE.Core/Structures/Building/Building.cs @@ -8,7 +8,7 @@ namespace NHSE.Core; /// Interact-able structure that can be entered by the player. /// [StructLayout(LayoutKind.Explicit, Size = SIZE, Pack = 1)] -public class Building +public sealed class Building { public const int SIZE = 0x14; diff --git a/NHSE.Core/Structures/Building/BuildingType.cs b/NHSE.Core/Structures/Building/BuildingType.cs index 6618d65..0dab870 100644 --- a/NHSE.Core/Structures/Building/BuildingType.cs +++ b/NHSE.Core/Structures/Building/BuildingType.cs @@ -35,4 +35,13 @@ public enum BuildingType : ushort Incline = 27, ReddsTreasureTrawler = 28, Studio = 29, -} \ No newline at end of file + + Hotel = 42, +} +public static class BuildingUtil +{ + public static (int Width, int Height) GetDimensions(this BuildingType type) => type switch + { + _ => (2, 2), + }; +} diff --git a/NHSE.Core/Structures/Designs/DesignPattern.cs b/NHSE.Core/Structures/Designs/DesignPattern.cs index 89bd3ee..86de1df 100644 --- a/NHSE.Core/Structures/Designs/DesignPattern.cs +++ b/NHSE.Core/Structures/Designs/DesignPattern.cs @@ -6,7 +6,7 @@ namespace NHSE.Core; /// /// Simple design pattern /// -public class DesignPattern : IVillagerOrigin +public sealed class DesignPattern(Memory Raw) : IVillagerOrigin { public const int Width = 32; public const int Height = 32; @@ -20,11 +20,8 @@ public class DesignPattern : IVillagerOrigin private const int PixelCount = 0x400; // Width * Height //private const int PixelDataSize = PixelCount / 2; // 4bit|4bit pixel packing - public readonly Memory Raw; public Span Data => Raw.Span; - public DesignPattern(Memory data) => Raw = data; - public uint Hash { get => ReadUInt32LittleEndian(Data); @@ -119,7 +116,13 @@ public static int GetColorOffset(int index) /// public byte[] GetBitmap() { - byte[] data = new byte[4 * Width * Height]; + var result = new byte[4 * Width * Height]; + LoadBitmap(result); + return result; + } + + public void LoadBitmap(Span data) + { for (int i = 0; i < PixelCount; i++) { var choice = this[i]; @@ -132,7 +135,6 @@ public byte[] GetBitmap() data[ofs + 0] = Data[palette + 2]; data[ofs + 3] = 0xFF; // opaque } - return data; } /// @@ -141,6 +143,12 @@ public byte[] GetBitmap() public byte[] GetPaletteBitmap() { var result = new byte[3 * PaletteColorCount]; + LoadPaletteBitmap(result); + return result; + } + + public void LoadPaletteBitmap(Span result) + { for (int i = 0; i < PaletteColorCount; i++) { var ofs = PaletteDataStart + (i * 3); @@ -148,6 +156,5 @@ public byte[] GetPaletteBitmap() result[(i * 3) + 1] = Data[ofs + 1]; result[(i * 3) + 0] = Data[ofs + 2]; } - return result; } } \ No newline at end of file diff --git a/NHSE.Core/Structures/Designs/DesignPatternPRO.cs b/NHSE.Core/Structures/Designs/DesignPatternPRO.cs index 329d060..02779ff 100644 --- a/NHSE.Core/Structures/Designs/DesignPatternPRO.cs +++ b/NHSE.Core/Structures/Designs/DesignPatternPRO.cs @@ -6,7 +6,7 @@ namespace NHSE.Core; /// /// Advanced design pattern with 4 sheets arranged in a square. /// -public class DesignPatternPRO : IVillagerOrigin +public sealed class DesignPatternPRO(Memory Raw) : IVillagerOrigin { public const int Width = 32; public const int Height = 32; @@ -21,11 +21,8 @@ public class DesignPatternPRO : IVillagerOrigin private const int PixelCount = 0x400; // Width * Height private const int SheetDataSize = PixelCount / 2; // 4bit|4bit pixel packing - public readonly Memory Raw; public Span Data => Raw.Span; - public DesignPatternPRO(Memory data) => Raw = data; - public uint Hash { get => ReadUInt32LittleEndian(Data); @@ -114,7 +111,13 @@ public static int GetColorOffset(int index) /// public byte[] GetBitmap(int sheet) { - byte[] data = new byte[4 * Width * Height]; + var result = new byte[4 * Width * Height]; + LoadBitmap(sheet, result); + return result; + } + + private void LoadBitmap(int sheet, Span data) + { for (int i = 0; i < PixelCount; i++) { var choice = GetPixelAtIndex(sheet, i); @@ -127,7 +130,6 @@ public byte[] GetBitmap(int sheet) data[ofs + 0] = Data[palette + 2]; data[ofs + 3] = 0xFF; // opaque } - return data; } /// @@ -136,6 +138,12 @@ public byte[] GetBitmap(int sheet) public byte[] GetPaletteBitmap() { var result = new byte[3 * PaletteColorCount]; + LoadPaletteBitmap(result); + return result; + } + + private void LoadPaletteBitmap(Span result) + { for (int i = 0; i < PaletteColorCount; i++) { var ofs = PaletteDataStart + (i * 3); @@ -143,6 +151,5 @@ public byte[] GetPaletteBitmap() result[(i * 3) + 1] = Data[ofs + 1]; result[(i * 3) + 0] = Data[ofs + 2]; } - return result; } } \ No newline at end of file diff --git a/NHSE.Core/Structures/GameFileDumper.cs b/NHSE.Core/Structures/GameFileDumper.cs index a68ba0d..9481c57 100644 --- a/NHSE.Core/Structures/GameFileDumper.cs +++ b/NHSE.Core/Structures/GameFileDumper.cs @@ -75,7 +75,7 @@ public static void DumpPlayerHouses(this IReadOnlyList houses, IRe /// Path to dump to public static void DumpPlayerHouses(this HorizonSave sav, string path) { - var count = Math.Min(sav.Players.Length, MainSaveOffsets.PlayerCount); + var count = Math.Min(sav.Players.Count, MainSaveOffsets.PlayerCount); for (int i = 0; i < count; i++) { var p = sav.Players[i]; diff --git a/NHSE.Core/Structures/Item/Item.cs b/NHSE.Core/Structures/Item/Item.cs index 5df3f10..e7022aa 100644 --- a/NHSE.Core/Structures/Item/Item.cs +++ b/NHSE.Core/Structures/Item/Item.cs @@ -225,6 +225,7 @@ public void CopyFrom(Item item) public static Item[] GetArray(ReadOnlySpan data) => data.GetArray(SIZE); public static byte[] SetArray(IReadOnlyList data) => data.SetArray(SIZE); + public static byte[] SetArray(ReadOnlySpan data) => data.SetArray(SIZE); public ushort GetWrappedItemName() => WrappingType switch { diff --git a/NHSE.Core/Structures/Item/ItemArrayEditor.cs b/NHSE.Core/Structures/Item/ItemArrayEditor.cs index 0b08a85..0f5ef11 100644 --- a/NHSE.Core/Structures/Item/ItemArrayEditor.cs +++ b/NHSE.Core/Structures/Item/ItemArrayEditor.cs @@ -3,10 +3,9 @@ namespace NHSE.Core; -public class ItemArrayEditor where T : Item, ICopyableItem +public sealed record ItemArrayEditor(IReadOnlyList Items) + where T : Item, ICopyableItem { - public readonly IReadOnlyList Items; - public ItemArrayEditor(IReadOnlyList items) => Items = items; public int ItemSize => Items[0].Size; public int TotalSize => Items.Count * ItemSize; diff --git a/NHSE.Core/Structures/Item/Remake/ItemRemakeInfo.cs b/NHSE.Core/Structures/Item/Remake/ItemRemakeInfo.cs index 06da87a..caa5c5c 100644 --- a/NHSE.Core/Structures/Item/Remake/ItemRemakeInfo.cs +++ b/NHSE.Core/Structures/Item/Remake/ItemRemakeInfo.cs @@ -5,38 +5,19 @@ namespace NHSE.Core; /// /// Metadata for an item's customization permissions /// -public class ItemRemakeInfo +public sealed record ItemRemakeInfo( + short Index, + ushort ItemUniqueID, + sbyte ReBodyPatternNum, + byte[] ReBodyPatternColors0, + byte[] ReBodyPatternColors1, + byte[] ReFabricPatternColors0, + byte[] ReFabricPatternColors1, + bool ReFabricPattern0VisibleOff) { public const int BodyColorCountMax = 8; public const int NoColor = (int)ItemCustomColor.None; // 14 - public readonly short Index; - public readonly ushort ItemUniqueID; - public readonly sbyte ReBodyPatternNum; // count of body colors - - public readonly byte[] ReBodyPatternColors0; - public readonly byte[] ReBodyPatternColors1; - - public readonly byte[] ReFabricPatternColors0; - public readonly byte[] ReFabricPatternColors1; - - public readonly bool ReFabricPattern0VisibleOff; - - public ItemRemakeInfo(short index, ushort id, sbyte count, byte[] bc0, byte[] bc1, byte[] fc0, byte[] fc1, bool fp0) - { - Index = index; - ItemUniqueID = id; - ReBodyPatternNum = count; - - ReBodyPatternColors0 = bc0; - ReBodyPatternColors1 = bc1; - - ReFabricPatternColors0 = fc0; - ReFabricPatternColors1 = fc1; - - ReFabricPattern0VisibleOff = fp0; - } - private const string Invalid = nameof(Invalid); public bool HasBodyColor(int variant) => ReBodyPatternColors0[variant] != NoColor || ReBodyPatternColors1[variant] != NoColor; diff --git a/NHSE.Core/Structures/Map/AcreCoordinate.cs b/NHSE.Core/Structures/Map/AcreCoordinate.cs new file mode 100644 index 0000000..0bc21b3 --- /dev/null +++ b/NHSE.Core/Structures/Map/AcreCoordinate.cs @@ -0,0 +1,37 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace NHSE.Core; + +/// +/// Navigation metadata for acre coordinates. +/// +[DebuggerDisplay("{Name} ({X}, {Y})")] +public readonly record struct AcreCoordinate(char XChar, char YChar, byte X, byte Y) +{ + public const int CountAcreExteriorWidth = 9; + public const int CountAcreExteriorHeight = 8; + + /// + /// Entire grid including exterior acre coordinates (bordered by acres of deep sea->shoreline=>terrain). + /// + public static readonly AcreCoordinate[] Exterior = GetGridWithExterior(CountAcreExteriorWidth, CountAcreExteriorHeight); + + public string Name => $"{YChar}{XChar}"; + + private static AcreCoordinate[] GetGridWithExterior([ConstantExpected] int width, [ConstantExpected] int height) + { + var result = new AcreCoordinate[width * height]; + int i = 0; + for (byte y = 0; y < height; y++) + { + 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); + result[i++] = new AcreCoordinate(xn, yn, x, y); + } + } + return result; + } +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/AcreSelectionGrid.cs b/NHSE.Core/Structures/Map/AcreSelectionGrid.cs new file mode 100644 index 0000000..353e5c1 --- /dev/null +++ b/NHSE.Core/Structures/Map/AcreSelectionGrid.cs @@ -0,0 +1,14 @@ +namespace NHSE.Core; + +/// +/// Basic logic implementation for interacting with the manipulatable map grid. +/// +public abstract record AcreSelectionGrid(TileGridViewport TileInfo) +{ + /// + /// Checks if the specified relative x/y coordinates are within the bounds of this layer. + /// + /// The requested tile's X-coordinate, relative to the layer origin. + /// The requested tile's Y-coordinate, relative to the layer origin. + public bool Contains(int relX, int relY) => TileInfo.Contains(relX, relY); +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Layers/ILayerBuilding.cs b/NHSE.Core/Structures/Map/Layers/ILayerBuilding.cs new file mode 100644 index 0000000..547bcc0 --- /dev/null +++ b/NHSE.Core/Structures/Map/Layers/ILayerBuilding.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; + +namespace NHSE.Core; + +/// +/// Logic for interacting with a map's building layer. +/// +public interface ILayerBuilding +{ + /// + /// Instantiated list of all buildings on the map. + /// + IReadOnlyList Buildings { get; init; } + + /// + /// Converts relative building coordinates to absolute coordinates in the map grid. + /// + /// Relative building X coordinate + /// Relative building Y coordinate + /// Map absolute X/Y coordinates + (int X, int Y) GetCoordinatesAbsolute(ushort relX, ushort relY); + + /// + /// Converts absolute map grid coordinates to relative building coordinates. + /// + /// Map absolute X coordinate + /// Map absolute Y coordinate + /// Relative building X/Y coordinates + (int X, int Y) GetCoordinatesRelative(int absX, int absY); + + Building this[int i] { get; } + int Count { get; } +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Layers/ILayerFieldItemFlag.cs b/NHSE.Core/Structures/Map/Layers/ILayerFieldItemFlag.cs new file mode 100644 index 0000000..3607e73 --- /dev/null +++ b/NHSE.Core/Structures/Map/Layers/ILayerFieldItemFlag.cs @@ -0,0 +1,48 @@ +using System; + +namespace NHSE.Core; + +/// +/// Logic for managing field item flags within a layer. +/// +public interface ILayerFieldItemFlag +{ + /// + /// Gets the active state of the field item flag at the specified relative coordinates. + /// + /// Relative X coordinate within the array. + /// Relative Y coordinate within the array. + /// The active state of the field item flag. + bool GetIsActive(int relX, int relY); + + /// + /// Sets the active state of the field item flag at the specified relative coordinates. + /// + /// Relative X coordinate within the array. + /// Relative Y coordinate within the array. + /// The active state to set. + void SetIsActive(int relX, int relY, bool value = true); + + bool this[int relX, int relY] + { + get => GetIsActive(relX, relY); + set => SetIsActive(relX, relY, value); + } + + /// + /// Deactivates all field item flags. + /// + void DeactivateAll(); + + /// + /// Saves the (in)active flags into the provided destination span. + /// + /// Destination span to save the flags into. + void Save(Span dest); + + /// + /// Imports the (in)active flags from the provided source span. + /// + /// Source span containing the flags to import. + void Import(Span src); +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Layers/ILayerFieldItemSet.cs b/NHSE.Core/Structures/Map/Layers/ILayerFieldItemSet.cs new file mode 100644 index 0000000..63c57eb --- /dev/null +++ b/NHSE.Core/Structures/Map/Layers/ILayerFieldItemSet.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +namespace NHSE.Core; + +public interface ILayerFieldItemSet +{ + LayerFieldItem Layer0 { get; } + LayerFieldItem Layer1 { get; } + + bool IsOccupied(int relX, int relY); + + Item GetItem(int relX, int relY, bool baseLayer); + void SetItem(int relX, int relY, bool baseLayer, Item value); + + Item this[int relX, int relY, bool baseLayer] + { + get => GetItem(relX, relY, baseLayer); + set => SetItem(relX, relY, baseLayer, value); + } + + /// + /// Lists out all coordinates of tiles present in that don't have anything underneath in to support them. + /// + List GetUnsupportedTiles(int totalWidth, int totalHeight); + + List GetUnsupportedTiles() => GetUnsupportedTiles(Layer0.TileInfo.TotalWidth, Layer0.TileInfo.TotalHeight); +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Layers/ItemLayer.cs b/NHSE.Core/Structures/Map/Layers/ItemLayer.cs deleted file mode 100644 index bf79d85..0000000 --- a/NHSE.Core/Structures/Map/Layers/ItemLayer.cs +++ /dev/null @@ -1,207 +0,0 @@ -using System; -using System.Diagnostics; - -namespace NHSE.Core; - -public abstract class ItemLayer : MapGrid -{ - public readonly Item[] Tiles; - - protected ItemLayer(Item[] tiles, int w, int h) : this(tiles, w, h, w, h) - { - } - - protected ItemLayer(Item[] tiles, int w, int h, int gw, int gh) : base(gw, gh, w, h) - { - Tiles = tiles; - Debug.Assert(MaxWidth * MaxHeight == tiles.Length); - } - - public Item GetTile(in int x, in int y) => this[GetTileIndex(x, y)]; - - public Item this[int index] - { - get => Tiles[index]; - set => Tiles[index] = value; - } - - public byte[] DumpAll() - { - var result = new byte[Tiles.Length * Item.SIZE]; - for (int i = 0; i < Tiles.Length; i++) - Tiles[i].ToBytesClass().CopyTo(result, i * Item.SIZE); - return result; - } - - public void ImportAll(ReadOnlySpan data) - { - var tiles = Item.GetArray(data); - for (int i = 0; i < tiles.Length; i++) - Tiles[i].CopyFrom(tiles[i]); - } - - public int RemoveAll(in int xmin, in int ymin, in int width, in int height, Func criteria) - { - int count = 0; - for (int x = xmin; x < xmin + width; x++) - { - for (int y = ymin; y < ymin + height; y++) - { - var t = GetTile(x, y); - if (!criteria(t)) - continue; - t.Delete(); - count++; - } - } - - return count; - } - - public void DeleteExtensionTiles(Item tile, int x, int y) - { - GetTileWidthHeight(tile, x, y, out var w, out var h); - - for (int ix = 0; ix < w; ix++) - { - for (int iy = 0; iy < h; iy++) - { - if (iy == 0 && ix == 0) - continue; - var t = GetTile(x + ix, y + iy); - t.Delete(); - } - } - } - - public void SetExtensionTiles(Item tile, in int x, in int y) - { - GetTileWidthHeight(tile, x, y, out var w, out var h); - - for (byte ix = 0; ix < w; ix++) - { - for (byte iy = 0; iy < h; iy++) - { - if (iy == 0 && ix == 0) - continue; - var t = GetTile(x + ix, y + iy); - t.SetAsExtension(tile, ix, iy); - } - } - } - - private void GetTileWidthHeight(Item tile, int x, int y, out int w, out int h) - { - var type = ItemInfo.GetItemSize(tile); - w = type.Width; - h = type.Height; - - // Rotation - if ((tile.Rotation & 1) == 1) - (w, h) = (h, w); - - // Clamp to grid bounds - if (x + w - 1 >= MaxWidth) - w = MaxWidth - x; - if (y + h - 1 >= MaxHeight) - h = MaxHeight - y; - } - - /// - /// Checks if writing the at the specified and coordinates will overlap with any existing tiles. - /// - /// True if any tile will be overwritten, false if nothing is there. - public PlacedItemPermission IsOccupied(Item tile, in int x, in int y) - { - var type = ItemInfo.GetItemSize(tile); - var w = type.Width; - var h = type.Height; - - if ((tile.Rotation & 1) == 1) - (w, h) = (h, w); - - if (x + w - 1 >= MaxWidth) - return PlacedItemPermission.OutOfBounds; - if (y + h - 1 >= MaxHeight) - return PlacedItemPermission.OutOfBounds; - - for (byte ix = 0; ix < w; ix++) - { - for (byte iy = 0; iy < h; iy++) - { - var t = GetTile(x + ix, y + iy); - if (!t.IsNone) - return PlacedItemPermission.Collision; - } - } - - return PlacedItemPermission.NoCollision; - } - - public int ReplaceAll(Item oldItem, Item newItem, in int xmin, in int ymin, in int width, in int height) - { - var sizeOld = ItemInfo.GetItemSize(oldItem); - var sizeNew = ItemInfo.GetItemSize(newItem); - - if (sizeOld != sizeNew) - return -1; - - int count = 0; - for (int x = xmin; x < xmin + width; x++) - { - for (int y = ymin; y < ymin + height; y++) - { - var t = GetTile(x, y); - if (!t.IsRoot) - continue; - - if (!t.Equals(oldItem)) - continue; - - DeleteExtensionTiles(t, x, y); - t.CopyFrom(newItem); - SetExtensionTiles(t, x, y); - count++; - } - } - - return count; - } - - public int ClearDanglingExtensions(in int xmin, in int ymin, in int width, in int height) - { - int count = 0; - for (int x = xmin; x < xmin + width; x++) - { - for (int y = ymin; y < ymin + height; y++) - { - var t = GetTile(x, y); - if (IsValidExtension(t, x, y)) - continue; - t.Delete(); - count++; - } - } - return count; - } - - private bool IsValidExtension(Item t, int x, int y) - { - if (!t.IsExtension) - return true; - var parentX = x - t.ExtensionX; - var parentY = y - t.ExtensionY; - - try - { - var parent = GetTile(parentX, parentY); - if (parent.ItemId == t.ExtensionItemId) - return true; - } - catch - { - // corrupt? - } - return false; - } -} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Layers/LayerBuilding.cs b/NHSE.Core/Structures/Map/Layers/LayerBuilding.cs new file mode 100644 index 0000000..115eec9 --- /dev/null +++ b/NHSE.Core/Structures/Map/Layers/LayerBuilding.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; + +namespace NHSE.Core; + +/// +/// Logic for interacting with a map's building layer. +/// +public sealed record LayerBuilding : ILayerBuilding +{ + public required IReadOnlyList Buildings { get; init; } + + // Although there is terrain in the Top Row and Left Column, no buildings can be placed there. + // Buildings can only be placed below that line; per the map layout, there is 2 acres worth of buffer, then our origin starts. + + /// + /// Compared to Item Tiles, building tiles are a 16x16 resolution (2x2 item tiles). + /// When converting between building coordinates and absolute coordinates, we need to account for this. + /// + private const int BuildingResolution = 2; + private const int BuildingTilesPerAcre = 16; + private const int AcreBufferEdge = 2; + + public (int X, int Y) GetCoordinatesAbsolute(ushort relX, ushort relY) + { + int absX = (relX * BuildingResolution) + (AcreBufferEdge * BuildingTilesPerAcre); + int absY = (relY * BuildingResolution) + (AcreBufferEdge * BuildingTilesPerAcre); + return (absX, absY); + } + + public (int X, int Y) GetCoordinatesRelative(int absX, int absY) + { + int relX = (absX - (AcreBufferEdge * BuildingTilesPerAcre)) / BuildingResolution; + int relY = (absY - (AcreBufferEdge * BuildingTilesPerAcre)) / BuildingResolution; + return (relX, relY); + } + + public Building this[int i] => Buildings[i]; + public int Count => Buildings.Count; +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Layers/FieldItemLayer.cs b/NHSE.Core/Structures/Map/Layers/LayerFieldItem.cs similarity index 73% rename from NHSE.Core/Structures/Map/Layers/FieldItemLayer.cs rename to NHSE.Core/Structures/Map/Layers/LayerFieldItem.cs index 1f6856f..a03a6dd 100644 --- a/NHSE.Core/Structures/Map/Layers/FieldItemLayer.cs +++ b/NHSE.Core/Structures/Map/Layers/LayerFieldItem.cs @@ -3,47 +3,17 @@ namespace NHSE.Core; -public class FieldItemLayer : ItemLayer +public sealed record LayerFieldItem(Item[] Tiles, byte AcreWidth, byte AcreHeight) + : LayerItem(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 GetAcreTile(int acreIndex, int tileIndex) => this[GetAcreTileIndex(acreIndex, tileIndex)]; - - public byte[] DumpAcre(int acre) - { - int count = GridTileCount; - var result = new byte[Item.SIZE * count]; - for (int i = 0; i < count; i++) - { - var tile = GetAcreTile(acre, i); - var bytes = tile.ToBytesClass(); - bytes.CopyTo(result, i * Item.SIZE); - } - return result; - } - - public void ImportAcre(int acre, ReadOnlySpan data) - { - int count = GridTileCount; - var tiles = Item.GetArray(data); - for (int i = 0; i < count; i++) - { - var tile = GetAcreTile(acre, i); - tile.CopyFrom(tiles[i]); - } - } - - public int ClearFieldPlanted(Func criteria) => ClearFieldPlanted(0, 0, MaxWidth, MaxHeight, criteria); - public int RemoveAll(Func criteria) => RemoveAll(0, 0, MaxWidth, MaxHeight, criteria); - public int RemoveAll(HashSet 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 criteria) => ClearFieldPlanted(0, 0, TileInfo.TotalWidth, TileInfo.TotalHeight, criteria); + public int RemoveAll(Func criteria) => RemoveAll(0, 0, TileInfo.TotalWidth, TileInfo.TotalHeight, criteria); + public int RemoveAll(HashSet 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 criteria) { @@ -54,6 +24,9 @@ public int ClearFieldPlanted(int xmin, int ymin, int width, int height, Func { for (int y = ymin; y < ymin + height; y++) { + if (!Contains(x, y)) + continue; var t = GetTile(x, y); if (!criteria(t)) continue; @@ -119,4 +94,10 @@ bool IsFlowerWaterable(Item item) return ModifyAll(xmin, ymin, width, height, IsFlowerWaterable, z => z.Water(all)); } + + public Item this[int relX, int relY] + { + get => GetTile(relX, relY); + set => SetTile(relX, relY, value); + } } \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Layers/LayerFieldItemFlag.cs b/NHSE.Core/Structures/Map/Layers/LayerFieldItemFlag.cs new file mode 100644 index 0000000..de14fec --- /dev/null +++ b/NHSE.Core/Structures/Map/Layers/LayerFieldItemFlag.cs @@ -0,0 +1,62 @@ +using System; +using System.Diagnostics; + +namespace NHSE.Core; + +/// +/// Logic for managing field item flags within a layer. +/// +public sealed class LayerFieldItemFlag(Memory raw, int width, int height) : ILayerFieldItemFlag +{ + public Span Data => raw.Span; + + public bool GetIsActive(int relX, int relY) + => FlagUtil.GetFlag(Data, GetLayerFlagIndex(relX, relY)); + public void SetIsActive(int relX, int relY, bool value) + => FlagUtil.SetFlag(Data, GetLayerFlagIndex(relX, relY), value); + + public bool this[int relX, int relY] + { + get => GetIsActive(relX, relY); + set => SetIsActive(relX, relY, value); + } + + /// + /// Although the Field Item Tiles are arranged y-column (y-x) based, the 'IsActive' flags are arranged x-row (x-y) based. + /// + private int GetLayerFlagIndex(int x, int y) => (y * width) + x; + + public void DeactivateAll() => Data.Clear(); + + public void Save(Span dest) => Data.CopyTo(dest); + + public void Import(Span src) => src.CopyTo(Data); + + public bool IsInLayer(int tileX, int tileY) => !((uint)tileX >= width || (uint)tileY >= height); + + /// + /// Diagnostic check of the active flags against the tiles. + /// + /// Tiles to check against. + public void DebugCheckTileActiveFlags(LayerItem tiles) + { + // this doesn't set anything; just diagnostic + + // 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 < width; x++) + { + for (int y = 0; y < height; y++) + { + var tile = tiles.GetTile(x, y); + var isActive = GetIsActive(x, y); + if (!isActive) + continue; + + bool empty = tile.IsNone; + if (empty) + Debug.WriteLine($"Flag at ({x},{y}) is not a root object."); + } + } + } +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Layers/LayerFieldItemSet.cs b/NHSE.Core/Structures/Map/Layers/LayerFieldItemSet.cs new file mode 100644 index 0000000..2388b98 --- /dev/null +++ b/NHSE.Core/Structures/Map/Layers/LayerFieldItemSet.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; + +namespace NHSE.Core; + +/// +/// Manages the data for the player's outside overworld. +/// +public sealed class LayerFieldItemSet : ILayerFieldItemSet +{ + /// + /// Base layer of items + /// + public required LayerFieldItem Layer0 { get; init; } + + /// + /// Layer of items that are supported by + /// + public required LayerFieldItem Layer1 { get; init; } + + /// + /// Lists out all coordinates of tiles present in that don't have anything underneath in to support them. + /// + public List GetUnsupportedTiles(int totalWidth, int totalHeight) + { + var result = new List(); + for (int x = 0; x < totalWidth; x++) + { + for (int y = 0; y < totalHeight; y++) + { + // If there is an item on this layer... + var tile = Layer1.GetTile(x, y); + if (tile.IsNone) + continue; + + // Then there must be something underneath it. + var support = Layer0.GetTile(x, y); + if (!support.IsNone) + continue; // dunno how to check if the tile can actually have an item put on top of it... + + result.Add($"{x:000},{y:000}"); + } + } + return result; + } + + public bool IsOccupied(int relX, int relY) => !Layer0.GetTile(relX, relY).IsNone || !Layer1.GetTile(relX, relY).IsNone; + + public Item GetItem(int relX, int relY, bool baseLayer) + { + var layer = baseLayer ? Layer0 : Layer1; + return layer.GetTile(relX, relY); + } + + public void SetItem(int relX, int relY, bool baseLayer, Item value) + { + var layer = baseLayer ? Layer0 : Layer1; + layer[relX, relY] = value; + } +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Layers/LayerItem.cs b/NHSE.Core/Structures/Map/Layers/LayerItem.cs new file mode 100644 index 0000000..271d859 --- /dev/null +++ b/NHSE.Core/Structures/Map/Layers/LayerItem.cs @@ -0,0 +1,414 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace NHSE.Core; + +public abstract record LayerItem : AcreSelectionGrid +{ + /// + /// All items in this layer, stored in column-major order. + /// + public Item[] Tiles { get; } + +#pragma warning disable CA1857 + protected LayerItem(Item[] tiles, [ConstantExpected] byte w, [ConstantExpected] byte h) : this(tiles, new(w, h, w, h)) +#pragma warning restore CA1857 + { + } + + protected LayerItem(Item[] tiles, TileGridViewport tileTileInfo) : base(tileTileInfo) + { + Tiles = tiles; + Debug.Assert(TileInfo.TotalWidth * TileInfo.TotalHeight == tiles.Length); + } + + /// + /// Retrieves the tile at the specified relative coordinates, or a default value if the coordinates are outside the + /// valid layer bounds. + /// + /// The requested tile's X-coordinate, relative to the layer origin. + /// The requested tile's Y-coordinate, relative to the layer origin. + /// The tile at the specified coordinates if they are within the layer; otherwise, a value indicating no item. + public Item GetTileSafe(in int relX, in int relY) + { + if (!Contains(relX, relY)) + return Item.NO_ITEM; + return GetTile(relX, relY); + } + + /// + /// Sets the tile at the specified relative coordinates if they are within the valid layer bounds. + /// + /// The requested tile's X-coordinate, relative to the layer origin. + /// The requested tile's Y-coordinate, relative to the layer origin. + /// The tile to set at the specified coordinates. + /// Returns true if the tile was set, false if the coordinates were out of bounds. + public bool SetTileSafe(in int relX, in int relY, Item tile) + { + if (!Contains(relX, relY)) + return false; + SetTile(relX, relY, tile); + return true; + } + + /// + /// Dumps the contents of an acre at the specified relative coordinates into a byte array. + /// + /// The requested tile's X-coordinate, relative to the layer origin. + /// The requested tile's Y-coordinate, relative to the layer origin. + /// A byte array containing the serialized data for the specified acre. The array length is determined by the number + /// of tiles in the view and the size of each item. + public byte[] DumpAcre(int relX, int relY) + { + int count = TileInfo.ViewCount; + var result = new byte[Item.SIZE * count]; + DumpAcre(result, relX, relY); + return result; + } + + /// + /// Writes the serialized data for an acre of tiles into the specified buffer, starting at the given relative X and Y coordinates. + /// + /// + /// If a tile at the specified coordinates is out of range, a default item is written for that position. + /// The data is written in column-major order, with columns stored before rows. + /// + /// The buffer that receives the serialized bytes for the acre. Must be large enough to hold all tile data. + /// The top-left tile's X-coordinate, relative to the layer origin. + /// The top-left tile's Y-coordinate, relative to the layer origin. + public void DumpAcre(Span result, int relX, int relY) + { + // Store columns first. If the x,y is out of range, store default item. + int offset = 0; + for (int y = 0; y < TileInfo.ViewHeight; y++) + { + var tileY = relY + y; + for (int x = 0; x < TileInfo.ViewWidth; x++) + { + var tileX = relX + x; + var tile = GetTileSafe(tileX, tileY); + tile.ToBytesClass().CopyTo(result[offset..]); + offset += Item.SIZE; + } + } + } + + /// + /// Imports a block of tile data into the map at the specified relative coordinates. + /// + /// The method attempts to set each tile in the specified region. Only tiles that are valid and + /// within the map bounds are imported; others are ignored. The return value indicates how many tiles were actually + /// updated. + /// A read-only span of bytes containing the tile data to import. The data must be formatted as expected by the tile + /// array parser. + /// The top-left tile's X-coordinate, relative to the layer origin. + /// The top-left tile's Y-coordinate, relative to the layer origin. + /// The number of tiles that were successfully imported and set in the map. + public int ImportAcre(ReadOnlySpan data, int relX, int relY) + { + int count = 0; + int i = 0; + var tiles = Item.GetArray(data); + for (int y = 0; y < TileInfo.ViewHeight; y++) + { + var tileY = relY + y; + for (int x = 0; x < TileInfo.ViewWidth; x++) + { + var tileX = relX + x; + if (SetTileSafe(tileX, tileY, tiles[i++])) + count++; + } + } + return count; + } + + /// + /// Retrieves the tile at the specified relative coordinates. + /// + /// The X-coordinate of the tile, relative to the layer origin. + /// The Y-coordinate of the tile, relative to the layer origin. + /// The tile at the specified coordinates. + public Item GetTile(in int relX, in int relY) => this[TileInfo.GetTileIndex(relX, relY)]; + + /// + /// Sets the tile at the specified relative X and Y coordinates to the given tile value. + /// + /// The X-coordinate of the tile, relative to the layer origin. + /// The Y-coordinate of the tile, relative to the layer origin. + /// The tile value to assign at the specified coordinates. + public void SetTile(in int relX, in int relY, Item tile) => this[TileInfo.GetTileIndex(relX, relY)] = tile; + + public Item this[int index] + { + get => Tiles[index]; + set => Tiles[index] = value; + } + + /// + /// Serializes all tiles into a single byte array. + /// + /// + /// A byte array containing the serialized data of all tiles, concatenated in order. + /// The length of the array is equal to the number of tiles multiplied by the size of each item. + /// + public byte[] DumpAll() + { + var result = new byte[Tiles.Length * Item.SIZE]; + DumpAll(result); + return result; + } + + /// + /// Writes the byte representation of all tiles to the specified buffer. + /// + /// A buffer that receives the serialized bytes of all tiles. + public void DumpAll(Span result) + { + for (int i = 0; i < Tiles.Length; i++) + Tiles[i].ToBytesClass().CopyTo(result[(i * Item.SIZE)..]); + } + + /// + /// Imports all tiles from the provided byte data into the layer. + /// + /// A read-only span of bytes containing the tile data to import. + public void ImportAll(ReadOnlySpan data) + { + var tiles = Item.GetArray(data); + for (int i = 0; i < tiles.Length; i++) + Tiles[i].CopyFrom(tiles[i]); + } + + /// + /// Removes all items within the specified rectangular region that match the given criteria. + /// + /// + /// The method evaluates the criteria for each item in the specified region and removes only those items for which the criteria returns . + /// Items outside the specified region are not affected. + /// + /// The relative x-coordinate of the upper-left corner of the region to search. + /// The relative y-coordinate of the upper-left corner of the region to search. + /// The width, in tiles, of the region to search. Must be greater than or equal to 0. + /// The height, in tiles, of the region to search. Must be greater than or equal to 0. + /// + /// A function that defines the condition each item must satisfy to be removed. + /// The function is invoked for each item in the region. + /// + /// The number of items that were removed. + public int RemoveAll(in int xmin, in int ymin, in int width, in int height, Func criteria) + { + int count = 0; + for (int x = xmin; x < xmin + width; x++) + { + for (int y = ymin; y < ymin + height; y++) + { + var t = GetTile(x, y); + if (!criteria(t)) + continue; + t.Delete(); + count++; + } + } + + return count; + } + + /// + /// Deletes all extension tiles associated with the specified tile at the given coordinates, except for the main (root) tile itself. + /// + /// + /// This method removes only the extension tiles that are part of the same logical group as the specified main tile, + /// leaving the main tile itself intact. + /// Use this method to clean up extension tiles when the main tile is being modified or removed. + /// + /// The main tile whose extension tiles are to be deleted. + /// The relative x-coordinate of the main (root) tile. + /// The relative y-coordinate of the main (root) tile. + public void DeleteExtensionTiles(Item tile, int x, int y) + { + GetTileWidthHeight(tile, x, y, out var w, out var h); + + for (int ix = 0; ix < w; ix++) + { + for (int iy = 0; iy < h; iy++) + { + if (iy == 0 && ix == 0) + continue; + var t = GetTile(x + ix, y + iy); + t.Delete(); + } + } + } + + /// + /// Marks all tiles covered by the specified multi-tile item, except the origin tile, + /// as extension tiles associated with the given item at the specified coordinates. + /// + /// + /// This method is typically used when placing a multi-tile item to ensure that all tiles it occupies, + /// except for the origin, are correctly marked as extensions. + /// The origin tile at the specified coordinates is not modified by this method. + /// The multi-tile item whose extension tiles are to be set. + /// The relative x-coordinate of the main (root) tile. + /// The relative y-coordinate of the main (root) tile. + public void SetExtensionTiles(Item tile, in int x, in int y) + { + GetTileWidthHeight(tile, x, y, out var w, out var h); + + for (byte ix = 0; ix < w; ix++) + { + for (byte iy = 0; iy < h; iy++) + { + if (iy == 0 && ix == 0) + continue; + var t = GetTile(x + ix, y + iy); + t.SetAsExtension(tile, ix, iy); + } + } + } + + private void GetTileWidthHeight(Item tile, int x, int y, out int w, out int h) + { + var type = ItemInfo.GetItemSize(tile); + w = type.Width; + h = type.Height; + + // Rotation + if ((tile.Rotation & 1) == 1) + (w, h) = (h, w); + + // Clamp to grid bounds + if (x + w - 1 >= TileInfo.TotalWidth) + w = TileInfo.TotalWidth - x; + if (y + h - 1 >= TileInfo.TotalHeight) + h = TileInfo.TotalHeight - y; + } + + /// + /// Checks if writing the at the specified and coordinates will overlap with any existing tiles. + /// + /// True if any tile will be overwritten, false if nothing is there. + public PlacedItemPermission IsOccupied(Item tile, in int x, in int y) + { + var type = ItemInfo.GetItemSize(tile); + var w = type.Width; + var h = type.Height; + + if ((tile.Rotation & 1) == 1) + (w, h) = (h, w); + + if (x + w - 1 >= TileInfo.TotalWidth) + return PlacedItemPermission.OutOfBounds; + if (y + h - 1 >= TileInfo.TotalHeight) + return PlacedItemPermission.OutOfBounds; + + for (byte ix = 0; ix < w; ix++) + { + for (byte iy = 0; iy < h; iy++) + { + var t = GetTile(x + ix, y + iy); + if (!t.IsNone) + return PlacedItemPermission.Collision; + } + } + + return PlacedItemPermission.NoCollision; + } + + /// + /// Replaces all occurrences of a specified item with a new item within the defined rectangular region. + /// + /// + /// Only root tiles that exactly match are replaced. + /// No replacements are made if the item sizes differ. + /// + /// The item to search for and replace within the specified region. + /// The item to use as a replacement for each occurrence of . + /// The relative x-coordinate of the upper-left corner of the region in which to perform replacements. + /// The relative y-coordinate of the upper-left corner of the region in which to perform replacements. + /// The width, in tiles, of the region in which to perform replacements. Must be greater than zero. + /// The height, in tiles, of the region in which to perform replacements. Must be greater than zero. + /// + /// The number of items replaced within the specified region, + /// or -1 if and are incompatible. + /// + public int ReplaceAll(Item oldItem, Item newItem, in int xmin, in int ymin, in int width, in int height) + { + var sizeOld = ItemInfo.GetItemSize(oldItem); + var sizeNew = ItemInfo.GetItemSize(newItem); + + if (sizeOld != sizeNew) + return -1; + + int count = 0; + for (int x = xmin; x < xmin + width; x++) + { + for (int y = ymin; y < ymin + height; y++) + { + var t = GetTile(x, y); + if (!t.IsRoot) + continue; + + if (!t.Equals(oldItem)) + continue; + + DeleteExtensionTiles(t, x, y); + t.CopyFrom(newItem); + SetExtensionTiles(t, x, y); + count++; + } + } + + return count; + } + + /// + /// Removes all invalid or dangling extensions within the specified rectangular region. + /// + /// + /// Only extension tiles that are determined to be invalid or dangling are removed. + /// Valid extension tiles within the region are not changed. + /// + /// The relative x-coordinate of the upper-left corner of the region in which to perform replacements. + /// The relative y-coordinate of the upper-left corner of the region in which to perform replacements. + /// The width of the region, in tiles. Must be greater than zero. + /// The height of the region, in tiles. Must be greater than zero. + /// The number of extensions that were removed from the specified region. + public int ClearDanglingExtensions(in int xmin, in int ymin, in int width, in int height) + { + int count = 0; + for (int x = xmin; x < xmin + width; x++) + { + for (int y = ymin; y < ymin + height; y++) + { + var t = GetTile(x, y); + if (IsValidExtension(t, x, y)) + continue; + t.Delete(); + count++; + } + } + return count; + } + + private bool IsValidExtension(Item t, int x, int y) + { + if (!t.IsExtension) + return true; + var parentX = x - t.ExtensionX; + var parentY = y - t.ExtensionY; + + try + { + var parent = GetTile(parentX, parentY); + if (parent.ItemId == t.ExtensionItemId) + return true; + } + catch + { + // corrupt? + } + return false; + } +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Layers/LayerPositionConfig.cs b/NHSE.Core/Structures/Map/Layers/LayerPositionConfig.cs new file mode 100644 index 0000000..506aa4e --- /dev/null +++ b/NHSE.Core/Structures/Map/Layers/LayerPositionConfig.cs @@ -0,0 +1,129 @@ +using System.Diagnostics.CodeAnalysis; + +namespace NHSE.Core; + +/// +/// Configures how a layer rests within the Map's grid, relative to a "chunk" or "acre". +/// +/// Number of acres in the width direction. +/// Number of acres in the height direction. +/// Horizontal acre shift from the map's origin. +/// Vertical acre shift from the map's origin. +/// Number of tiles per acre in one dimension (16 or 32). +/// Bit shift value to convert between tiles and acres (4 for 16 tiles, 5 for 32 tiles). +/// Size of tile compared to the smallest tile possible (2 for 16 tiles, 1 for 32 tiles). +public readonly record struct LayerPositionConfig( + byte CountWidth, byte CountHeight, + byte ShiftWidth, byte ShiftHeight, + [ConstantExpected] byte TilesPerAcre, byte TileBitShift, + [ConstantExpected(Max = 2, Min = 1)] byte MetaTileSize) +{ + // Maps in Animal Crossing: New Horizons are made up of acres that are 9 tiles wide and 8 tiles high. + // 5 columns in the center are land, surrounded by 2 tiles of beach and 2 tiles of sea on each side. + // 4 rows in the center are land, surrounded by 2 rows of beach and 2 rows of sea on each side. + // +-----------+ + // | ~~~~~~~~~ | + // | ~*******~ | + // | ~*=====*~ | + // | ~*=====*~ | + // | ~*=====*~ | + // | ~*=====*~ | + // | ~*******~ | + // | ~~~~~~~~~ | + // +-----------+ + + // Main Island Map Config - True Dimensions + private const byte MapAcreWidth = 9; // 2 sea, 2 beach, 5 land + private const byte MapAcreHeight = 8; // 2 sea, 2 beach, 4 land + + // Optimize some calculations away by using bit-shift instead of mul/div, as we're always a multiple of 2. + private const byte Grid32 = 32; + private const byte Grid16 = 16; + private const byte Shift32 = 5; // div32 is same as sh 5 + private const byte Shift16 = 4; // div16 is same as sh 4 + + /// + /// Creates a new instance, centering the layer within the acre. + /// + /// Width of the layer in acres. + /// Height of the layer in acres. + /// Number of tiles per acre (16 or 32). + /// Size of tile compared to the smallest tile possible (2 for 16 tiles, 1 for 32 tiles). + /// Acre-wise horizontal shift from the map origin. + /// Acre-wise vertical shift from the map origin. + /// A new instance. + public static LayerPositionConfig Create(byte width, byte height, + [ConstantExpected(Min = Grid16, Max = Grid32)] byte tilesPerAcre, + [ConstantExpected(Min = 1, Max = 2)] byte metaTileSize, byte shiftW, byte shiftH) + { + var bitShift = tilesPerAcre == Grid16 ? Shift16 : Shift32; +#pragma warning disable CA1857 + return new LayerPositionConfig(width, height, shiftW, shiftH, tilesPerAcre, bitShift, metaTileSize); +#pragma warning restore CA1857 + } + + /// + /// Calculates the absolute map coordinates based on the specified relative X and Y coordinates within the layer. + /// + /// The relative X-coordinate within the layer. + /// The relative Y-coordinate within the layer. + public (int X, int Y) GetCoordinatesAbsolute(int relX, int relY) + { + var absX = relX + ((ShiftWidth * MetaTileSize) << TileBitShift); + var absY = relY + ((ShiftHeight * MetaTileSize) << TileBitShift); + return (absX, absY); + } + + /// + /// Gets the absolute coordinates of the layer's origin (0,0) in the map. + /// + public (int X, int Y) GetCoordinatesAbsolute() => GetCoordinatesAbsolute(0, 0); + + /// + /// Calculates the relative coordinates within the layer, based on the specified absolute X and Y coordinates. + /// + /// The absolute X coordinate to convert. + /// The absolute Y coordinate to convert. + /// A tuple containing the X and Y coordinates relative to the layer. + public (int X, int Y) GetCoordinatesRelative(int absX, int absY) + { + var relX = absX - ((ShiftWidth * MetaTileSize) << TileBitShift); + var relY = absY - ((ShiftHeight * MetaTileSize) << TileBitShift); + return (relX, relY); + } + + /// + /// Determines whether the specified absolute X and Y coordinates are within the valid bounds of the map. + /// + /// The absolute X coordinate to validate. Must be within the horizontal bounds of the map. + /// The absolute Y coordinate to validate. Must be within the vertical bounds of the map. + /// if the coordinates are valid; otherwise, . + public bool IsCoordinateValidRelative(int relX, int relY) + { + if ((uint)relX >= CountWidth << TileBitShift) + return false; + if ((uint)relY >= CountHeight << TileBitShift) + return false; + return true; + } + + /// + /// Layer total width in tiles. + /// + public int LayerTotalWidth => CountWidth * TilesPerAcre; + + /// + /// Layer total height in tiles. + /// + public int LayerTotalHeight => CountHeight * TilesPerAcre; + + /// + /// Gets the total width of the map, in tiles. + /// + public int MapTotalWidth => MapAcreWidth * TilesPerAcre; + + /// + /// Gets the total height of the map, in tiles. + /// + public int MapTotalHeight => MapAcreHeight * TilesPerAcre; +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Layers/RoomItemLayer.cs b/NHSE.Core/Structures/Map/Layers/LayerRoomItem.cs similarity index 51% rename from NHSE.Core/Structures/Map/Layers/RoomItemLayer.cs rename to NHSE.Core/Structures/Map/Layers/LayerRoomItem.cs index 5d4c973..05f440c 100644 --- a/NHSE.Core/Structures/Map/Layers/RoomItemLayer.cs +++ b/NHSE.Core/Structures/Map/Layers/LayerRoomItem.cs @@ -3,27 +3,27 @@ namespace NHSE.Core; -public class RoomItemLayer : ItemLayer +public sealed record LayerRoomItem : LayerItem { 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 data) : this(Item.GetArray(data)) { } - public RoomItemLayer(Item[] tiles) : base(tiles, Width, Height) { } + public LayerRoomItem(ReadOnlySpan data) : this(Item.GetArray(data)) { } + public LayerRoomItem(Item[] tiles) : base(tiles, Width, Height) { } - public static RoomItemLayer[] GetArray(ReadOnlySpan data) + public static LayerRoomItem[] GetArray(ReadOnlySpan data) { - var result = new RoomItemLayer[data.Length / SIZE]; + var result = new LayerRoomItem[data.Length / SIZE]; for (int i = 0; i < result.Length; i++) { var slice = data.Slice(i * SIZE, SIZE); - result[i] = new RoomItemLayer(slice); + result[i] = new LayerRoomItem(slice); } return result; } - public static byte[] SetArray(IReadOnlyList data) + public static byte[] SetArray(IReadOnlyList data) { var result = new byte[data.Count * SIZE]; for (int i = 0; i < data.Count; i++) diff --git a/NHSE.Core/Structures/Map/Layers/LayerTerrain.cs b/NHSE.Core/Structures/Map/Layers/LayerTerrain.cs new file mode 100644 index 0000000..15db2c2 --- /dev/null +++ b/NHSE.Core/Structures/Map/Layers/LayerTerrain.cs @@ -0,0 +1,306 @@ +using System; +using System.Diagnostics; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace NHSE.Core; + +/// +/// Grid of +/// +public sealed record LayerTerrain : AcreSelectionGrid +{ + /// + /// Terrain tiles in this layer, stored in column-major order. + /// + public TerrainTile[] Tiles { get; } + + /// + /// Gets the underlying memory buffer containing the base acre template data (such as sea, beach, interior). + /// + public Memory BaseAcres { get; } + + /// + /// 16x16 tiles per acre. + /// + public const byte TilesPerAcreDim = 16; + + /// + /// Interior acre-count width (without the deep-sea border). + /// + private const byte CountAcreWidth = 7; + + /// + /// Interior acre-count height (without the deep-sea border). + /// + private const byte CountAcreHeight = 6; + + private static TileGridViewport Viewport => new(TilesPerAcreDim, TilesPerAcreDim, CountAcreWidth, CountAcreHeight); + + public LayerTerrain(TerrainTile[] tiles, Memory acres) : base(Viewport) + { + BaseAcres = acres; + Tiles = tiles; + Debug.Assert(TileInfo.TotalCount == tiles.Length); + } + + /// + /// Gets the terrain tile at the specified coordinates. + /// + /// The requested tile's X-coordinate, relative to the layer origin. + /// The requested tile's Y-coordinate, relative to the layer origin. + /// + /// An exception is thrown if the requested coordinates are out of the layer's range. + /// + public TerrainTile GetTile(int relX, int relY) => this[TileInfo.GetTileIndex(relX, relY)]; + + private TerrainTile GetTileSafe(int relX, int relY) + { + if (Contains(relX, relY)) + return new TerrainTile(); + return GetTile(relX, relY); + } + + private bool SetTileSafe(int relX, int relY, TerrainTile tile) + { + if (Contains(relX, relY)) + return false; + GetTile(relX, relY).CopyFrom(tile); + return true; + } + + public TerrainTile this[int index] + { + get => Tiles[index]; + set => Tiles[index] = value; + } + + /// + /// Flattens the terrain tiles into a contiguous byte array. + /// + public byte[] DumpAll() => TerrainTile.SetArray(Tiles); + + /// + /// Imports terrain tiles from a contiguous byte array. + /// + /// Byte array containing terrain tile data. + public void ImportAll(ReadOnlySpan data) + { + var tiles = TerrainTile.GetArray(data); + for (int i = 0; i < tiles.Length; i++) + Tiles[i].CopyFrom(tiles[i]); + } + + /// + /// Retrieves the serialized data for the acre at the specified relative coordinates. + /// + /// The relative X-coordinate of the top-left tile of the acre to dump. + /// The relative Y-coordinate of the top-left tile of the acre to dump. + /// + /// A byte array containing the serialized terrain data of the specified acre. + /// The array length is determined by the terrain tile size and the view count. + /// + public byte[] DumpAcre(int relX, int relY) + { + int count = TileInfo.ViewCount; + var result = new byte[TerrainTile.SIZE * count]; + DumpAcre(result, relX, relY); + return result; + } + + /// + /// Writes the serialized data for an acre of tiles, starting at the specified relative coordinates, into the provided buffer. + /// + /// + /// If a tile at the specified coordinates is out of range, a default value is written for that tile. + /// The data is written in column-major order, with each tile's bytes written sequentially into the buffer. + /// + /// + /// The buffer that receives the serialized tile data. + /// Must be large enough to hold the data for the entire acre. + /// + /// The relative X-coordinate of the top-left tile of the acre to dump. + /// The relative Y-coordinate of the top-left tile of the acre to dump. + public void DumpAcre(Span result, int relX, int relY) + { + // Store columns first. If the x,y is out of range, store default. + int offset = 0; + for (int y = 0; y < TileInfo.ViewHeight; y++) + { + var tileY = relY + y; + for (int x = 0; x < TileInfo.ViewWidth; x++) + { + var tileX = relX + x; + var tile = GetTileSafe(tileX, tileY); + tile.ToBytesClass().CopyTo(result[offset..]); + offset += Item.SIZE; + } + } + } + + /// + /// Imports terrain tile data into the layer at the specified relative coordinates. + /// + /// + /// A read-only span of bytes containing the serialized terrain tile data to import. + /// The data must be in the format expected by TerrainTile.GetArray. + /// + /// The X-coordinate, relative to the layer origin, where the imported tiles will be placed. + /// The Y-coordinate, relative to the layer origin, where the imported tiles will be placed. + /// The number of tiles successfully imported. Returns 0 if no tiles were imported. + public int ImportAcre(ReadOnlySpan data, int relX, int relY) + { + int count = 0; + int i = 0; + var tiles = TerrainTile.GetArray(data); + for (int y = 0; y < TileInfo.ViewHeight; y++) + { + var tileY = relY + y; + for (int x = 0; x < TileInfo.ViewWidth; x++) + { + var tileX = relX + x; + SetTileSafe(tileX, tileY, tiles[i++]); + } + } + return count; + } + + /// + /// Sets all tiles to the specified tile. + /// + /// + /// If true, only sets the interior tiles, skipping the outermost ring of beach/rock acres. + public void SetAll(TerrainTile tile, in bool interiorOnly = true) + { + if (interiorOnly) + { + // skip outermost ring of tiles + 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++) + GetTile(x, y).CopyFrom(tile); + } + } + else + { + foreach (var t in Tiles) + t.CopyFrom(tile); + } + } + + /// + /// Sets all road tiles (not the terrain itself, but the road atop) to the specified tile. + /// + /// Road tile info to copy from. + /// If true, only sets the interior tiles, skipping the outermost ring of beach/rock acres. + public void SetAllRoad(TerrainTile tile, bool interiorOnly = true) + { + if (interiorOnly) + { + // skip outermost ring of tiles + 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++) + GetTile(x, y).CopyRoadFrom(tile); + } + } + else + { + foreach (var t in Tiles) + t.CopyRoadFrom(tile); + } + } + + /// + public int GetTileColor(int relX, int relY, int insideX, int insideY) + { + var acre = GetAcreTemplate(relX, relY); + return GetTileColor(acre, relX, relY, insideX, insideY); + } + + /// + /// Gets the base acre tile color at the specified terrain coordinates. + /// + /// + /// If the acre has a predefined appearance, that is used; otherwise, the terrain-based appearance is used. + /// + /// Base acre underneath the terrain tile. + /// Relative X coordinate in terrain tiles. + /// Relative Y coordinate in terrain tiles. + /// Inside X coordinate of the terrain tile (16px max). + /// Inside Y coordinate of the terrain tile (16px max). + /// ARGB color value. + public int GetTileColor(ushort acre, int relX, int relY, int insideX, int insideY) + { + if (acre != 0) // predefined appearance + { + var c = AcreTileColor.GetAcreTileColor(acre, relX % 16, relY % 16); + if (c != -0x1000000) // transparent + return c; + } + + // dynamic (terrain-based) appearance + var tile = GetTile(relX, relY); + return TerrainTileColor.GetTileColor(tile, insideX, insideY).ToArgb(); + } + + + /// + /// Gets the base acre tile color at the specified terrain coordinates. + /// + /// + /// If the acre has a predefined appearance, that is used; otherwise, the terrain-based appearance is used. + /// + /// Base acre underneath the terrain tile. + /// Terrain tile to render. + /// Relative X coordinate in terrain tiles. + /// Relative Y coordinate in terrain tiles. + /// Inside X coordinate of the terrain tile (16px max). + /// Inside Y coordinate of the terrain tile (16px max). + /// ARGB color value. + public int GetTileColor(ushort acre, TerrainTile tile, int relX, int relY, int insideX, int insideY) + { + // If acre is entirely transparent (interior acre), dynamic (terrain-based) appearance + if (acre == 0) + return TerrainTileColor.GetTileColor(tile, insideX, insideY).ToArgb(); + + // For beaches, a slim edge is customizable (indicative by a transparent value). + // Check if pre-defined appearance governs + var color = AcreTileColor.GetAcreTileColor(acre, relX % 16, relY % 16); + if (color == -0x1000000) // transparent (dynamic) + return TerrainTileColor.GetTileColor(tile, insideX, insideY).ToArgb(); + + return color; // pre-defined appearance + } + + /// + /// Gets the base acre template at the specified terrain coordinates. + /// + /// Relative X coordinate in terrain tiles. + /// Relative Y coordinate in terrain tiles. + /// Base acre underneath the terrain tile. + public ushort GetAcreTemplate(int relX, int relY) + { + // Acres are 16x16 tiles, and the acre data has a 1-acre deep-sea border around it. + var acreX = 1 + (relX / 16); + var acreY = 1 + (relY / 16); + + var acreIndex = ((CountAcreWidth + 2) * acreY) + acreX; + var span = GetBaseAcreSpan(acreIndex); + return ReadUInt16LittleEndian(span); + } + + /// + /// Gets the base acre template span at the specified index. + /// + /// Index of the acre. + /// Span of bytes representing the base acre template. + public Span GetBaseAcreSpan(int index) => BaseAcres.Span.Slice(index * 2, 2); +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Layers/TerrainLayer.cs b/NHSE.Core/Structures/Map/Layers/TerrainLayer.cs deleted file mode 100644 index d8127d9..0000000 --- a/NHSE.Core/Structures/Map/Layers/TerrainLayer.cs +++ /dev/null @@ -1,166 +0,0 @@ -using System; -using System.Diagnostics; -using static System.Buffers.Binary.BinaryPrimitives; - -namespace NHSE.Core; - -/// -/// Grid of -/// -public class TerrainLayer : MapGrid -{ - public TerrainTile[] Tiles { get; init; } - public byte[] BaseAcres { get; init; } - - public TerrainLayer(TerrainTile[] tiles, byte[] acres) : base(16, 16, AcreWidth * 16, AcreHeight * 16) - { - BaseAcres = acres; - Tiles = tiles; - Debug.Assert(MaxTileCount == 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 GetAcreTile(int acreIndex, int tileIndex) => this[GetAcreTileIndex(acreIndex, tileIndex)]; - - public TerrainTile this[int index] - { - get => Tiles[index]; - set => Tiles[index] = value; - } - - public byte[] DumpAll() - { - var result = new byte[Tiles.Length * TerrainTile.SIZE]; - for (int i = 0; i < Tiles.Length; i++) - Tiles[i].ToBytesClass().CopyTo(result, i * TerrainTile.SIZE); - return result; - } - - public byte[] DumpAcre(int acre) - { - int count = GridTileCount; - var result = new byte[TerrainTile.SIZE * count]; - for (int i = 0; i < count; i++) - { - var tile = GetAcreTile(acre, i); - var bytes = tile.ToBytesClass(); - bytes.CopyTo(result, i * TerrainTile.SIZE); - } - - return result; - } - - public void ImportAll(ReadOnlySpan data) - { - var tiles = TerrainTile.GetArray(data); - for (int i = 0; i < tiles.Length; i++) - Tiles[i].CopyFrom(tiles[i]); - } - - public void ImportAcre(int acre, ReadOnlySpan data) - { - int count = GridTileCount; - var tiles = TerrainTile.GetArray(data); - for (int i = 0; i < count; i++) - { - var tile = GetAcreTile(acre, i); - tile.CopyFrom(tiles[i]); - } - } - - 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; - for (int x = xmin; x < xmax; x++) - { - for (int y = ymin; y < ymax; y++) - GetTile(x, y).CopyFrom(tile); - } - } - else - { - foreach (var t in Tiles) - t.CopyFrom(tile); - } - } - - 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; - for (int x = xmin; x < xmax; x++) - { - for (int y = ymin; y < ymax; y++) - GetTile(x, y).CopyRoadFrom(tile); - } - } - else - { - foreach (var t in Tiles) - t.CopyRoadFrom(tile); - } - } - - public void GetBuildingCoordinate(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. - int buildingShift = GridWidth; - x = (int)(((bx / 2f) - buildingShift) * scale); - y = (int)(((by / 2f) - buildingShift) * scale); - } - - public void GetBuildingRelativeCoordinates(int topX, int topY, int acreScale, ushort bx, ushort by, out int relX, out int relY) - { - GetBuildingCoordinate(bx, by, acreScale, out var x, out var y); - relX = x - (topX * acreScale); - relY = y - (topY * acreScale); - } - - public bool IsWithinGrid(int acreScale, int relX, int relY) - { - if ((uint)relX >= GridWidth * acreScale) - return false; - - if ((uint)relY >= GridHeight * acreScale) - return false; - - return true; - } - - public int GetTileColor(int x, in int y, int relativeX, int relativeY) - { - var acre = GetTileAcre(x, y); - if (acre != 0) - { - var c = AcreTileColor.GetAcreTileColor(acre, x % 16, y % 16); - if (c != -0x1000000) // transparent - return c; - } - - var tile = GetTile(x, y); - return TerrainTileColor.GetTileColor(tile, relativeX, relativeY).ToArgb(); - } - - private ushort GetTileAcre(int x, int y) - { - var acreX = 1 + (x / 16); - var acreY = 1 + (y / 16); - - var acreIndex = ((AcreWidth + 2) * acreY) + acreX; - var ofs = acreIndex * 2; - return ReadUInt16LittleEndian(BaseAcres.AsSpan(ofs)); - } -} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Managers/FieldItemManager.cs b/NHSE.Core/Structures/Map/Managers/FieldItemManager.cs deleted file mode 100644 index 7deea64..0000000 --- a/NHSE.Core/Structures/Map/Managers/FieldItemManager.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics; - -namespace NHSE.Core; - -/// -/// Manages the data for the player's outside overworld. -/// -public class FieldItemManager -{ - /// - /// Base layer of items - /// - public readonly FieldItemLayer Layer1; - - /// - /// Layer of items that are supported by - /// - public readonly FieldItemLayer Layer2; - - /// - /// Reference to the save file that will be updated when is called. - /// - public readonly MainSave SAV; - - public FieldItemManager(MainSave sav) - { - Layer1 = new FieldItemLayer(sav.GetFieldItemLayer1()); - Layer2 = new FieldItemLayer(sav.GetFieldItemLayer2()); - SAV = sav; - } - - /// - /// Stores all values for the back to the . - /// - public void Save() - { - SAV.SetFieldItemLayer1(Layer1.Tiles); - SAV.SetFieldItemLayer2(Layer2.Tiles); - - SetTileActiveFlags(Layer1, SAV.FieldItemFlag1); - SetTileActiveFlags(Layer2, SAV.FieldItemFlag2); - } - - /// - /// Lists out all coordinates of tiles present in that don't have anything underneath in to support them. - /// - /// - public List GetUnsupportedTiles() - { - var result = new List(); - for (int x = 0; x < FieldItemLayer.FieldItemWidth; x++) - { - for (int y = 0; y < FieldItemLayer.FieldItemHeight; y++) - { - var tile = Layer2.GetTile(x, y); - if (tile.IsNone) - continue; - - var support = Layer1.GetTile(x, y); - if (!support.IsNone) - continue; // dunno how to check if the tile can actually have an item put on top of it... - - result.Add($"{x:000},{y:000}"); - } - } - return result; - } - - private void SetTileActiveFlags(ItemLayer tiles, int ofs) - { - // this doesn't set anything; just diagnostic - - // 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 y = 0; y < FieldItemLayer.FieldItemHeight; y++) - { - var tile = tiles.GetTile(x, y); - var isActive = GetIsActive(ofs, x, y); - if (!isActive) - continue; - - bool empty = tile.IsNone; - if (empty) - Debug.WriteLine($"Flag at ({x},{y}) is not a root object."); - } - } - } - - 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); - - public bool IsOccupied(int x, int y) => !Layer1.GetTile(x, y).IsNone || !Layer2.GetTile(x, y).IsNone; -} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Managers/MapEditor.cs b/NHSE.Core/Structures/Map/Managers/MapEditor.cs new file mode 100644 index 0000000..b832437 --- /dev/null +++ b/NHSE.Core/Structures/Map/Managers/MapEditor.cs @@ -0,0 +1,129 @@ +namespace NHSE.Core; + +public sealed class MapEditor +{ + /// + /// Master interactor for mutating the map. + /// + public required MapMutator Mutator { get; init; } + + /// + /// Amount of pixel upscaling compared to a 1px = 1 tile map. + /// + public int MapScale { get; set; } = 1; + + /// + /// Amount of pixel upscaling compared to a 1px = 1 tile map. + /// + public int ViewScale { get; set; } = 16; + + /// + /// Converts an upscaled coordinate to a tile coordinate. + /// + /// X coordinate (mouse on upscaled image). + /// Y coordinate (mouse on upscaled image). + public (int X, int Y) GetCursorCoordinates(in int mX, in int mY) + { + var x = mX / MapScale; + var y = mY / MapScale; + return (x, y); + } + + /// + /// Creates a new instance of the MapEditor class initialized from the specified save file. + /// + /// The save file containing the data used to initialize the MapEditor. Cannot be null. + /// A MapEditor instance populated with data from the provided save file. + public static MapEditor FromSaveFile(MainSave sav) + => new() { Mutator = MapMutator.FromSaveFile(sav) }; + + + public int X => Mutator.View.X; + public int Y => Mutator.View.Y; + public LayerTerrain Terrain => Mutator.Manager.LayerTerrain; + public ILayerBuilding Buildings => Mutator.Manager.LayerBuildings; + public ILayerFieldItemSet Items => Mutator.Manager.FieldItems; + + /// + /// Converts building map coordinates to view pixel coordinates. + /// + /// Building map X coordinate. + /// Building map Y coordinate. + /// View coordinates. + public (int X, int Y) GetViewCoordinatesBuilding(uint relX, uint relY) + => GetViewCoordinates((int)relX, (int)relY, Mutator.Manager.ConfigBuildings); + + public (int X, int Y) GetViewCoordinatesTerrain(int relX, int relY) + => GetViewCoordinates(relX, relY, Mutator.Manager.ConfigTerrain); + + public (int X, int Y) GetViewCoordinatesFieldItem(int relX, int relY) + => GetViewCoordinates(relX, relY, Mutator.Manager.ConfigItems); + + public (int X, int Y) GetViewCoordinates(int posX, int posY, LayerPositionConfig shifter) + { + // Get absolute coordinates from the layer. + var (x, y) = shifter.GetCoordinatesAbsolute(posX, posY); + + // Shift to view coordinates + x -= X; + y -= Y; + // Scale to pixel coordinates + x *= ViewScale; + y *= ViewScale; + return (x, y); + } + + /// + /// From a map pixel coordinate (), get the clamped map tile coordinate. + /// + /// Upscaled Map pixel X coordinate. + /// Upscaled Map pixel Y coordinate. + /// Option to adjust the coordinates to a desired type. + /// + public (int X, int Y) GetMapCoordinates(int x, int y, MapViewCoordinateRequest type) + { + x /= MapScale; + y /= MapScale; + + if (x < 0) x = 0; + if (y < 0) y = 0; + + // Adjust the view coordinate + if (type == MapViewCoordinateRequest.Centered) + { + // Reticle size is GridWidth, center = /2 + var shift = (LayerFieldItem.TilesPerAcreDim * MapScale) / 2; + x -= shift; + y -= shift; + } + else if (type == MapViewCoordinateRequest.SnapAcre) + { + // Snap to the nearest acre + x -= x % LayerFieldItem.TilesPerAcreDim; + y -= y % LayerFieldItem.TilesPerAcreDim; + } + + var view = Mutator.View; + // Clamp to viewport dimensions, and center to nearest acre if desired. + // Clamp to boundaries so that we always have a full grid to view. + return view.EnforceEdgeBuffer(x, y); + } +} + +public enum MapViewCoordinateRequest +{ + /// + /// No adjustment to the coordinates. + /// + None = 0, + + /// + /// Snap the coordinates to the nearest acre boundary. + /// + SnapAcre, + + /// + /// Center the view around the requested (x,y). + /// + Centered, +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Managers/MapManager.cs b/NHSE.Core/Structures/Map/Managers/MapManager.cs new file mode 100644 index 0000000..d026b15 --- /dev/null +++ b/NHSE.Core/Structures/Map/Managers/MapManager.cs @@ -0,0 +1,16 @@ +namespace NHSE.Core; + +public sealed class MapTileManager +{ + public required LayerPositionConfig ConfigTerrain { get; init; } + public required LayerPositionConfig ConfigItems { get; init; } + public required LayerPositionConfig ConfigBuildings { get; init; } + + public required ILayerFieldItemSet FieldItems { get; init; } + public required ILayerFieldItemFlag LayerItemFlag0 { get; init; } + public required ILayerFieldItemFlag LayerItemFlag1 { get; init; } + public required ILayerBuilding LayerBuildings { get; init; } + + public required MapStatePlaza Plaza { get; init; } + public required LayerTerrain LayerTerrain { get; set; } +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Managers/MapMutator.cs b/NHSE.Core/Structures/Map/Managers/MapMutator.cs new file mode 100644 index 0000000..b434d5d --- /dev/null +++ b/NHSE.Core/Structures/Map/Managers/MapMutator.cs @@ -0,0 +1,98 @@ +using System; + +namespace NHSE.Core; + +public sealed record MapMutator +{ + public MapViewState View { get; init; } = new(); + public required MapTileManager Manager { get; init; } + + // Mutability State Tracking + public uint ItemLayerIndex { get; set => field = value & 1; } + + public LayerFieldItem CurrentLayer => ItemLayerIndex == 0 ? Manager.FieldItems.Layer0 : Manager.FieldItems.Layer1; + + /// + public int ModifyFieldItems(Func action, in bool wholeMap) + => ModifyFieldItems(action, wholeMap, CurrentLayer); + + /// + public int ReplaceFieldItems(Item oldItem, Item newItem, in bool wholeMap) + => ReplaceFieldItems(oldItem, newItem, wholeMap, CurrentLayer); + + /// + /// Modifies field items in the specified using the provided function. + /// + /// Range selector (xmin, ymin, width, height) to use. + /// If true, the modification is applied across the entire map; otherwise, only within the current view. + /// The layer field item to perform the modification on. + /// The number of items modified. + public int ModifyFieldItems(Func action, in bool wholeMap, LayerFieldItem layerField) + { + int xMin, yMin, width, height; + if (wholeMap) + { + (xMin, yMin) = (0, 0); + var info = layerField.TileInfo; + (width, height) = info.DimTotal; + } + else + { + // Convert absolute to relative coordinates + (xMin, yMin) = Manager.ConfigItems.GetCoordinatesRelative(View.X, View.Y); + if (!Manager.ConfigItems.IsCoordinateValidRelative(xMin, yMin)) + return 0; + + var info = layerField.TileInfo; + (width, height) = info.DimAcre; + } + return action(xMin, yMin, width, height); + } + + /// + /// Replaces all instances of with in the specified . + /// + /// Item to be replaced. + /// Item to replace with. + /// If true, the replacement is done across the entire map; otherwise, only within the current view. + /// The layer field item to perform the replacement on. + /// The number of items replaced. + private int ReplaceFieldItems(Item oldItem, Item newItem, bool wholeMap, LayerFieldItem layerField) + { + int xMin, yMin, width, height; + if (wholeMap) + { + (xMin, yMin) = (0, 0); + var info = layerField.TileInfo; + (width, height) = info.DimTotal; + } + else + { + // Convert absolute to relative coordinates + (xMin, yMin) = Manager.ConfigItems.GetCoordinatesRelative(View.X, View.Y); + if (!Manager.ConfigItems.IsCoordinateValidRelative(xMin, yMin)) + return 0; + + var info = layerField.TileInfo; + (width, height) = info.DimAcre; + } + + return layerField.ReplaceAll(oldItem, newItem, xMin, yMin, width, height); + } + + /// + /// Creates a from the provided file. + /// + /// The save file containing the data used to initialize the MapMutator. Cannot be null. + /// A MapMutator instance populated with data from the provided save file. + public static MapMutator FromSaveFile(MainSave sav) + => new() { Manager = MapTileManagerUtil.FromSaveFile(sav) }; + + /// + /// Creates a separate view-mutator with shared map objects. + /// + public MapMutator CreateCopy() => this with + { + View = View with { }, + }; +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Managers/MapStatePlaza.cs b/NHSE.Core/Structures/Map/Managers/MapStatePlaza.cs new file mode 100644 index 0000000..22ce5e8 --- /dev/null +++ b/NHSE.Core/Structures/Map/Managers/MapStatePlaza.cs @@ -0,0 +1,18 @@ +namespace NHSE.Core; + +/// +/// Value storage for plaza position in the map. +/// +public sealed class MapStatePlaza +{ + /// + /// Plaza Position X coordinate. + /// + + public required uint X { get; set; } + + /// + /// Plaza Position Z coordinate. + /// + public required uint Z { get; set; } +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Managers/MapTileManagerUtil.cs b/NHSE.Core/Structures/Map/Managers/MapTileManagerUtil.cs new file mode 100644 index 0000000..9255479 --- /dev/null +++ b/NHSE.Core/Structures/Map/Managers/MapTileManagerUtil.cs @@ -0,0 +1,42 @@ +namespace NHSE.Core; + +public static class MapTileManagerUtil +{ + /// + /// Retrieves a from the provided . + /// + public static MapTileManager FromSaveFile(MainSave sav) => new() + { + ConfigTerrain = LayerPositionConfig.Create(7, 6, 16, 1, 1, 1), + ConfigBuildings = LayerPositionConfig.Create(9, 7, 16, 1, 0, 0), + ConfigItems = LayerPositionConfig.Create(sav.FieldItemAcreWidth, sav.FieldItemAcreHeight, 32, 1, + (byte)((9 - sav.FieldItemAcreWidth) / 2), 1), + + LayerTerrain = new LayerTerrain(sav.GetTerrainTiles(), sav.GetAcreBytes()), + LayerBuildings = new LayerBuilding { Buildings = sav.Buildings }, + FieldItems = new LayerFieldItemSet + { + Layer0 = new LayerFieldItem(sav.GetFieldItemLayer0(), sav.FieldItemAcreWidth, sav.FieldItemAcreHeight), + Layer1 = new LayerFieldItem(sav.GetFieldItemLayer1(), sav.FieldItemAcreWidth, sav.FieldItemAcreHeight), + }, + LayerItemFlag0 = new LayerFieldItemFlag(sav.FieldItemFlag0Data, sav.FieldItemAcreWidth, sav.FieldItemAcreHeight), + LayerItemFlag1 = new LayerFieldItemFlag(sav.FieldItemFlag1Data, sav.FieldItemAcreWidth, sav.FieldItemAcreHeight), + Plaza = new MapStatePlaza { X = sav.EventPlazaLeftUpX, Z = sav.EventPlazaLeftUpZ }, + }; + + /// + /// Sets the values from the provided into the provided . + /// + public static void SetManager(this MapTileManager mgr, MainSave sav) + { + sav.Buildings = mgr.LayerBuildings.Buildings; + sav.SetTerrainTiles(mgr.LayerTerrain.Tiles); + sav.SetFieldItemLayer0(mgr.FieldItems.Layer0.Tiles); + sav.SetFieldItemLayer1(mgr.FieldItems.Layer1.Tiles); + mgr.LayerItemFlag0.Save(sav.FieldItemFlag0Data.Span); + mgr.LayerItemFlag1.Save(sav.FieldItemFlag1Data.Span); + sav.SetAcreBytes(mgr.LayerTerrain.BaseAcres.Span); + sav.EventPlazaLeftUpX = mgr.Plaza.X; + sav.EventPlazaLeftUpZ = mgr.Plaza.Z; + } +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Managers/MapViewState.cs b/NHSE.Core/Structures/Map/Managers/MapViewState.cs new file mode 100644 index 0000000..66dc84d --- /dev/null +++ b/NHSE.Core/Structures/Map/Managers/MapViewState.cs @@ -0,0 +1,154 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace NHSE.Core; + +public sealed record MapViewState +{ + private const int MaxX = LayerFieldItem.TilesPerAcreDim * AcreCoordinate.CountAcreExteriorWidth; + private const int MaxY = LayerFieldItem.TilesPerAcreDim * AcreCoordinate.CountAcreExteriorHeight; + private const int EdgeBuffer = LayerFieldItem.TilesPerAcreDim; + + /// + /// Amount the X/Y coordinates change when using arrow movement. + /// + [Range(1, 8)] public uint ArrowViewInterval { get; set; } = 2; + + [Range(0, 1)] public int ItemLayerIndex + { + get; + set + { + if (value is not (0 or 1)) + throw new ArgumentOutOfRangeException(nameof(value), "Item layer index must be 0 or 1."); + field = value; + } + } + + /// + /// Top-left-origin X coordinate of the view. + /// + public int X + { + get; + private set + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)value, (uint)MaxX); + field = value; + } + } + + /// + /// Top-left-origin Y coordinate of the view. + /// + public int Y + { + get; + private set + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)value, (uint)MaxY); + field = value; + } + } + + public bool CanUp => Y != 0; + public bool CanDown => Y != MaxY; + public bool CanLeft => X != 0; + public bool CanRight => X != MaxX; + + /// + /// Moves the view up by tiles. + /// + /// if the view changed; otherwise, . + public bool ArrowUp() + { + if (!CanUp) + return false; + Y = Math.Max(0, Y - (int)ArrowViewInterval); + return true; + } + + /// + /// Moves the view left by tiles. + /// + /// if the view changed; otherwise, . + public bool ArrowLeft() + { + if (!CanLeft) + return false; + X = Math.Max(0, X - (int)ArrowViewInterval); + return true; + } + + /// + /// Moves the view right by tiles. + /// + /// if the view changed; otherwise, . + public bool ArrowRight() + { + if (!CanRight) + return false; + X = Math.Min(MaxX - EdgeBuffer, X + (int)ArrowViewInterval); + return true; + } + + /// + /// Moves the view down by tiles. + /// + /// if the view changed; otherwise, . + public bool ArrowDown() + { + if (!CanDown) + return false; + Y = Math.Min(MaxY - EdgeBuffer, Y + (int)ArrowViewInterval); + return true; + } + + /// + /// Applies the requested coordinates (sanity checked). + /// + /// if the view changed; otherwise, . + public bool SetViewTo(in int absX, in int absY) + { + var (x, y) = (X, Y); + X = Math.Clamp(absX, 0, MaxX - EdgeBuffer); + Y = Math.Clamp(absY, 0, MaxY - EdgeBuffer); + return x != X || y != Y; + } + + /// + /// Drags the view by the specified delta amounts. + /// + /// if the view changed; otherwise, . + public bool DragView(int dX, int dY) => SetViewTo(X + dX, Y + dY); + + /// + /// Sets the view to the top-left of the specified acre. + /// + /// + /// Acres are ordered Y-down-first. + /// + /// Acre index to set the view to. + public void SetViewToAcre(int acre) + { + var acreX = acre % (MaxX / EdgeBuffer); + var acreY = acre / (MaxX / EdgeBuffer); + SetViewTo(acreX * EdgeBuffer, acreY * EdgeBuffer); + } + + public (int X, int Y) EnforceEdgeBuffer(int x, int y) + { + x = Math.Clamp(x, 0, MaxX - EdgeBuffer); + y = Math.Clamp(y, 0, MaxY - EdgeBuffer); + return (x, y); + } + + public bool IsWithinView(int x, int y, int tileStride) + { + if (x < X || x >= X + tileStride) + return false; + if (y < Y || y >= Y + tileStride) + return false; + return true; + } +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Managers/RoomLayerSurface.cs b/NHSE.Core/Structures/Map/Managers/RoomLayerSurface.cs index d4686eb..bc320b2 100644 --- a/NHSE.Core/Structures/Map/Managers/RoomLayerSurface.cs +++ b/NHSE.Core/Structures/Map/Managers/RoomLayerSurface.cs @@ -1,6 +1,6 @@ namespace NHSE.Core; -public enum RoomLayerSurface +public enum RoomLayerSurface : byte { Floor = 0, FloorSupported = 1, diff --git a/NHSE.Core/Structures/Map/Managers/RoomItemManager.cs b/NHSE.Core/Structures/Map/Managers/RoomManager.cs similarity index 67% rename from NHSE.Core/Structures/Map/Managers/RoomItemManager.cs rename to NHSE.Core/Structures/Map/Managers/RoomManager.cs index c964d5d..c59361b 100644 --- a/NHSE.Core/Structures/Map/Managers/RoomItemManager.cs +++ b/NHSE.Core/Structures/Map/Managers/RoomManager.cs @@ -4,14 +4,18 @@ namespace NHSE.Core; -public class RoomItemManager +public sealed class RoomManager { - public readonly RoomItemLayer[] Layers; + public readonly LayerRoomItem[] Layers; public readonly IPlayerRoom Room; + + /// + /// + /// private const int LayerCount = 8; - public RoomItemManager(IPlayerRoom room) + public RoomManager(IPlayerRoom room) { Layers = room.GetItemLayers(); Room = room; @@ -20,14 +24,14 @@ public RoomItemManager(IPlayerRoom room) public void Save() => Room.SetItemLayers(Layers); - public bool IsOccupied(int layer, int x, int y) + public bool IsOccupied(RoomLayerSurface layer, int x, int y) { if ((uint)layer >= LayerCount) throw new ArgumentOutOfRangeException(nameof(layer)); - var l = Layers[layer]; + var l = Layers[(int)layer]; var tile = l.GetTile(x, y); - return !tile.IsNone || (layer == (int)RoomLayerSurface.FloorSupported && IsOccupied((int)RoomLayerSurface.Floor, x, y)); + return !tile.IsNone || (layer == RoomLayerSurface.FloorSupported && IsOccupied(RoomLayerSurface.Floor, x, y)); } public List GetUnsupportedTiles() @@ -35,9 +39,11 @@ public List GetUnsupportedTiles() var lBase = Layers[(int)RoomLayerSurface.Floor]; var lSupport = Layers[(int)RoomLayerSurface.FloorSupported]; var result = new List(); - for (int x = 0; x < lBase.MaxWidth; x++) + + var (width, height) = lBase.TileInfo.DimTotal; + for (int x = 0; x < width; x++) { - for (int y = 0; y < lBase.MaxHeight; y++) + for (int y = 0; y < height; y++) { var tile = lSupport.GetTile(x, y); if (tile.IsNone) diff --git a/NHSE.Core/Structures/Map/MapGrid.cs b/NHSE.Core/Structures/Map/MapGrid.cs deleted file mode 100644 index cfd3516..0000000 --- a/NHSE.Core/Structures/Map/MapGrid.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace NHSE.Core; - -/// -/// Basic logic implementation for interacting with the manipulatable map grid. -/// -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; - } -} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/OutsideAcre.cs b/NHSE.Core/Structures/Map/OutsideAcre.cs index 8958b6e..fb3cf50 100644 --- a/NHSE.Core/Structures/Map/OutsideAcre.cs +++ b/NHSE.Core/Structures/Map/OutsideAcre.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; -using System.Drawing; - -namespace NHSE.Core; +namespace NHSE.Core; public enum OutsideAcre : ushort { @@ -223,38 +220,4 @@ public enum OutsideAcre : ushort FldOutEGarden00 = 315, FldOutNGardenLFront00 = 316, FldOutNGardenRFront00 = 317, -} - -public static class CollisionUtil -{ - public static readonly Dictionary Dict = new() - { - {00, Color.FromArgb( 70, 120, 64)}, // Grass - {01, Color.FromArgb(128, 215, 195)}, // River - {03, Color.FromArgb(192, 192, 192)}, // Stone - {04, Color.FromArgb(240, 230, 170)}, // Sand - {05, Color.FromArgb(128, 215, 195)}, // Sea - {06, Color.FromArgb(255, 128, 128)}, // Wood - {07, Color.FromArgb(0 , 0, 0)}, // Null - {08, Color.FromArgb(32 , 32, 32)}, // Building - {09, Color.FromArgb(255, 0, 0)}, // ?? - {10, Color.FromArgb(48 , 48, 48)}, // Door - {12, Color.FromArgb(128, 215, 195)}, // Water at mouths of river - {15, Color.FromArgb(128, 215, 195)}, // Strip of water between river mouth and river - {22, Color.FromArgb(190, 98, 98)}, // Wood (thin) - {28, Color.FromArgb(255, 0, 0)}, // ?? this one isn't even in ColGroundAttributeParam... - {29, Color.FromArgb(232, 222, 162)}, // Edge of beach, next to sea - {41, Color.FromArgb(118, 122, 132)}, // Rocks at top of map - {42, Color.FromArgb(128, 133, 147)}, // Taller regions, rocks at top of map - {43, Color.Cyan}, // Tide pool - {44, Color.FromArgb( 62, 112, 56)}, // Edge connecting grass and beach - {45, Color.FromArgb(118, 122, 132)}, // Some kind of rock - {46, Color.FromArgb(120, 207, 187)}, // Edge of sea, next to beach - {47, Color.FromArgb(128, 128, 0)}, // Sandstone - {49, Color.FromArgb(190, 98, 98)}, // Pier - {51, Color.FromArgb(32 , 152, 32)}, // "Grass-growing building"?? - {70, Color.FromArgb(109, 113, 124)}, // Kapp'n's island rock - {149, Color.FromArgb(179, 207, 252)}, // Ice (traversable) - {150, Color.FromArgb(61 , 119, 212)}, // Ice (tall, with collision) - }; } \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Terrain/AcreCoordinate.cs b/NHSE.Core/Structures/Map/Terrain/AcreCoordinate.cs deleted file mode 100644 index 9e19ba5..0000000 --- a/NHSE.Core/Structures/Map/Terrain/AcreCoordinate.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace NHSE.Core; - -/// -/// Navigation metadata for acre coordinates. -/// -public class AcreCoordinate -{ - public readonly string Name; - public readonly int X, Y; - - public AcreCoordinate(int x, int y) : this((char)('0' + x), (char)('A' + y), x, y) { } - - public AcreCoordinate(char xName, char yName, int x, int y) - { - Name = $"{yName}{xName}"; - X = x; - Y = y; - } - - public static AcreCoordinate[] GetGrid(int width, int height) - { - var result = new AcreCoordinate[width * height]; - int i = 0; - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - result[i++] = new AcreCoordinate(x, y); - } - return result; - } - - public static AcreCoordinate[] GetGridWithExterior(int width, int height) - { - var result = new AcreCoordinate[width * height]; - int i = 0; - for (int y = 0; y < height; y++) - { - for (int 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); - result[i++] = new AcreCoordinate(xn, yn, x, y); - } - } - return result; - } -} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Terrain/PlacedItemPermission.cs b/NHSE.Core/Structures/Map/Terrain/PlacedItemPermission.cs index c5547b2..88bd90c 100644 --- a/NHSE.Core/Structures/Map/Terrain/PlacedItemPermission.cs +++ b/NHSE.Core/Structures/Map/Terrain/PlacedItemPermission.cs @@ -3,20 +3,20 @@ /// /// Flagging various issues when trying to place an item. /// -public enum PlacedItemPermission +public enum PlacedItemPermission : byte { /// /// Item does not have any of its tiles overlapping with any other items. /// - NoCollision, + NoCollision = 0, /// /// Item tiles are overlapping with another item. /// - Collision, + Collision = 1, /// /// Item tiles would overflow out-of-bounds. /// - OutOfBounds, + OutOfBounds = 2, } \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/Terrain/TerrainTile.cs b/NHSE.Core/Structures/Map/Terrain/TerrainTile.cs index 545f292..fe2d7e2 100644 --- a/NHSE.Core/Structures/Map/Terrain/TerrainTile.cs +++ b/NHSE.Core/Structures/Map/Terrain/TerrainTile.cs @@ -9,7 +9,7 @@ namespace NHSE.Core; /// Represents a Terraform-able terrain tile. /// [StructLayout(LayoutKind.Sequential)] -public class TerrainTile +public sealed class TerrainTile { // tile[2] (u16 model, u16 variation, u16 angle) // u16 elevation @@ -76,9 +76,9 @@ public void CopyRoadFrom(TerrainTile tile) LandMakingAngleRoad = tile.LandMakingAngleRoad; } - public bool Rotate() => UnitModelRoad != 0 ? RotateRoad() : RotateTerrain(); + public bool TryRotate() => UnitModelRoad != 0 ? TryRotateRoad() : TryRotateTerrain(); - private bool RotateTerrain() + private bool TryRotateTerrain() { if (UnitModel == TerrainUnitModel.Base) return false; @@ -88,7 +88,7 @@ private bool RotateTerrain() return true; } - private bool RotateRoad() + private bool TryRotateRoad() { var rot = LandMakingAngleRoad; rot = (ushort) ((rot + 1) & 3); diff --git a/NHSE.Core/Structures/Map/TileCollisionUtil.cs b/NHSE.Core/Structures/Map/TileCollisionUtil.cs new file mode 100644 index 0000000..4955ace --- /dev/null +++ b/NHSE.Core/Structures/Map/TileCollisionUtil.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Drawing; + +namespace NHSE.Core; + +public static class TileCollisionUtil +{ + public static readonly Dictionary Dict = new() + { + {00, Color.FromArgb( 70, 120, 64)}, // Grass + {01, Color.FromArgb(128, 215, 195)}, // River + {03, Color.FromArgb(192, 192, 192)}, // Stone + {04, Color.FromArgb(240, 230, 170)}, // Sand + {05, Color.FromArgb(128, 215, 195)}, // Sea + {06, Color.FromArgb(255, 128, 128)}, // Wood + {07, Color.FromArgb(0 , 0, 0)}, // Null + {08, Color.FromArgb(32 , 32, 32)}, // Building + {09, Color.FromArgb(255, 0, 0)}, // ?? + {10, Color.FromArgb(48 , 48, 48)}, // Door + {12, Color.FromArgb(128, 215, 195)}, // Water at mouths of river + {15, Color.FromArgb(128, 215, 195)}, // Strip of water between river mouth and river + {22, Color.FromArgb(190, 98, 98)}, // Wood (thin) + {28, Color.FromArgb(255, 0, 0)}, // ?? this one isn't even in ColGroundAttributeParam... + {29, Color.FromArgb(232, 222, 162)}, // Edge of beach, next to sea + {41, Color.FromArgb(118, 122, 132)}, // Rocks at top of map + {42, Color.FromArgb(128, 133, 147)}, // Taller regions, rocks at top of map + {43, Color.Cyan}, // Tide pool + {44, Color.FromArgb( 62, 112, 56)}, // Edge connecting grass and beach + {45, Color.FromArgb(118, 122, 132)}, // Some kind of rock + {46, Color.FromArgb(120, 207, 187)}, // Edge of sea, next to beach + {47, Color.FromArgb(128, 128, 0)}, // Sandstone + {49, Color.FromArgb(190, 98, 98)}, // Pier + {51, Color.FromArgb(32 , 152, 32)}, // "Grass-growing building"?? + {70, Color.FromArgb(109, 113, 124)}, // Kapp'n's island rock + {149, Color.FromArgb(179, 207, 252)}, // Ice (traversable) + {150, Color.FromArgb(61 , 119, 212)}, // Ice (tall, with collision) + }; +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/TileGrid.cs b/NHSE.Core/Structures/Map/TileGrid.cs deleted file mode 100644 index 4a176e4..0000000 --- a/NHSE.Core/Structures/Map/TileGrid.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; - -namespace NHSE.Core; - -/// -/// Basic logic implementation for interacting with the manipulatable tile grid. -/// -/// -/// Certain use this as a viewport on a subsection of the entire tile-set. -/// -public abstract class TileGrid -{ - /// Amount of viewable tiles wide - public readonly int GridWidth; - - /// Amount of viewable tiles high - public readonly int GridHeight; - - /// Max amount of tiles wide the entire grid is - public readonly int MaxWidth; - - /// Max amount of tiles high the entire grid is - 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; - } - - /// - /// Amount of tiles present in the grid. - /// - public int GridTileCount => GridWidth * GridHeight; - - /// - /// Amount of ALL tiles present in the entire grid (including the grid). - /// - 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); - } -} \ No newline at end of file diff --git a/NHSE.Core/Structures/Map/TileGridViewport.cs b/NHSE.Core/Structures/Map/TileGridViewport.cs new file mode 100644 index 0000000..5ef7617 --- /dev/null +++ b/NHSE.Core/Structures/Map/TileGridViewport.cs @@ -0,0 +1,78 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace NHSE.Core; + +/// +/// Basic configuration of a narrow view for interacting with the larger manipulatable tile grid. +/// +/// Viewable amount of viewable tiles wide +/// Viewable amount of viewable tiles high +/// Columns of view available +/// Rows of view available +public readonly record struct TileGridViewport([ConstantExpected] byte ViewWidth, [ConstantExpected] byte ViewHeight, byte Columns, byte Rows) +{ + /// + /// Total width of the entire grid (including the view). + /// + public int TotalWidth => Columns * ViewWidth; + + /// + /// Total height of the entire grid (including the view). + /// + public int TotalHeight => Rows * ViewHeight; + + /// + /// Amount of tiles present in the grid. + /// + public int ViewCount => ViewWidth * ViewHeight; + + /// + /// Amount of ALL tiles present in the entire grid (including the grid). + /// + public int TotalCount => TotalWidth * TotalHeight; + + /// + /// Gets the dimensions of the viewable area (an acre-worth). + /// + public (int X, int Y) DimAcre => (ViewWidth, ViewHeight); + + /// + /// Gets the total dimensions of the entire grid (including the view). + /// + public (int X, int Y) DimTotal => (TotalWidth, TotalHeight); + + /// + /// Gets the absolute index of the absolute tile in the grid based on the x/y coordinates. + /// + /// Relative X-coordinate of the tile in the grid + /// Relative Y-coordinate of the tile in the grid + /// Absolute index of the tile in the grid + public int GetTileIndex(in int relX, in int relY) => (TotalHeight * relX) + relY; + + /// + /// Clamps the specified relative X and Y coordinates so that they remain within the valid bounds of the area. + /// + /// + /// Use this method to prevent coordinates from exceeding the valid area, + /// which may help avoid out-of-bounds errors when working with grid-based data or images. + /// + /// The relative X coordinate to clamp. + /// The relative Y coordinate to clamp. + public void ClampInside(ref int relX, ref int relY) + => ClampCoordinatesTo(ref relX, ref relY, TotalWidth - 1, TotalHeight - 1); + + private static void ClampCoordinatesTo(ref int relX, ref int relY, int maxX, int maxY) + { + relX = Math.Clamp(relX, 0, maxX); + relY = Math.Clamp(relY, 0, maxY); + } + + /// + /// Determines whether the specified relative coordinates are within the bounds of the area. + /// + /// The horizontal coordinate, relative to the left edge of the area. + /// The vertical coordinate, relative to the top edge of the area. + /// if the specified coordinates are within the bounds; otherwise, . + public bool Contains(int relX, int relY) => !((uint)relX >= TotalWidth || (uint)relY >= TotalHeight); +} \ No newline at end of file diff --git a/NHSE.Core/Structures/Misc/GSaveFg.cs b/NHSE.Core/Structures/Misc/GSaveFg.cs index 93321fc..8d71724 100644 --- a/NHSE.Core/Structures/Misc/GSaveFg.cs +++ b/NHSE.Core/Structures/Misc/GSaveFg.cs @@ -4,7 +4,7 @@ namespace NHSE.Core; [StructLayout(LayoutKind.Sequential, Pack = 4)] -public class GSaveFg +public sealed class GSaveFg { public const int SIZE = 0x928; private const int _7b9816fbCount = 0x900; diff --git a/NHSE.Core/Structures/Misc/GSavePlayerManpu.cs b/NHSE.Core/Structures/Misc/GSavePlayerManpu.cs index b1d7a54..7aad662 100644 --- a/NHSE.Core/Structures/Misc/GSavePlayerManpu.cs +++ b/NHSE.Core/Structures/Misc/GSavePlayerManpu.cs @@ -13,49 +13,14 @@ public struct GSavePlayerManpu : IReactionStore private const int MaxCount = 64; private const int WheelCount = 8; - /// - /// List of known Reaction IDs - /// [field: MarshalAs(UnmanagedType.ByValArray, SizeConst = MaxCount)] public Reaction[] ManpuBit { get; set; } - /// - /// Emotions that are currently bound to the Reaction Wheel. - /// [field: MarshalAs(UnmanagedType.ByValArray, SizeConst = WheelCount)] public Reaction[] UIList { get; set; } - /// - /// Flags indicating if a Reaction (at the same index?) is newly learned or not. - /// [field: MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = MaxCount)] public bool[] NewFlag { get; set; } - - public void AddMissingReactions() - { - var all = Enum.GetValues(); - foreach (var react in all) - AddReaction(react); - } - - // returns true if failed - public bool AddReaction(Reaction react) - { - if (react.ToString().StartsWith("UNUSED")) - return true; - - var index = Array.IndexOf(ManpuBit, react); - if (index >= 0) - return false; - - var empty = EmptyIndex; - if (empty < 0) - return true; - ManpuBit[empty] = react; - return false; - } - - private readonly int EmptyIndex => Array.FindIndex(ManpuBit, z => z == 0); } /// @@ -68,56 +33,64 @@ public struct GSavePlayerManpu15 : IReactionStore private const int MaxCount = 256; // up from 64 private const int WheelCount = 8; - /// - /// List of known Reaction IDs - /// [field: MarshalAs(UnmanagedType.ByValArray, SizeConst = MaxCount)] public Reaction[] ManpuBit { get; set; } - /// - /// Emotions that are currently bound to the Reaction Wheel. - /// [field: MarshalAs(UnmanagedType.ByValArray, SizeConst = WheelCount)] public Reaction[] UIList { get; set; } - /// - /// Flags indicating if a Reaction (at the same index?) is newly learned or not. - /// [field: MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = MaxCount)] public bool[] NewFlag { get; set; } - - public void AddMissingReactions() - { - var all = Enum.GetValues(); - foreach (var react in all) - AddReaction(react); - } - - // returns true if failed - public bool AddReaction(Reaction react) - { - if (react.ToString().StartsWith("UNUSED")) - return true; - - var index = Array.IndexOf(ManpuBit, react); - if (index >= 0) - return false; - - var empty = EmptyIndex; - if (empty < 0) - return true; - ManpuBit[empty] = react; - return false; - } - - private readonly int EmptyIndex => Array.FindIndex(ManpuBit, z => z == 0); } public interface IReactionStore { + /// + /// List of Reaction IDs the player currently knows. + /// Reaction[] ManpuBit { get; set; } + + /// + /// Emotions that are currently bound to the Reaction Wheel. + /// Reaction[] UIList { get; set; } + + /// + /// Flags indicating if a Reaction (at the same index?) is newly learned or not. + /// bool[] NewFlag { get; set; } - bool AddReaction(Reaction react); - void AddMissingReactions(); + + /// + /// Adds all possible reaction values from 's defined list. + /// + void AddMissingReactions() + { + var all = Enum.GetValues(); + foreach (var react in all) + TryAddReaction(react); + } + + /// + /// Attempts to add the to the list of reactions. + /// + /// Reaction to add to list + bool TryAddReaction(Reaction react) + { + if (react.ToString().StartsWith("UNUSED")) + return false; // shouldn't add + + if (ManpuBit.Contains(react)) + return true; // already have + + var empty = EmptyIndex; + if (empty < 0) + return true; // full? already have + ManpuBit[empty] = react; + return true; + } + + /// + /// First empty index within the array of reactions. + /// + int EmptyIndex => ManpuBit.IndexOf(Reaction.None); } \ No newline at end of file diff --git a/NHSE.Core/Structures/Misc/GSaveVisitorNpc.cs b/NHSE.Core/Structures/Misc/GSaveVisitorNpc.cs index 4ab8d5f..03b56c0 100644 --- a/NHSE.Core/Structures/Misc/GSaveVisitorNpc.cs +++ b/NHSE.Core/Structures/Misc/GSaveVisitorNpc.cs @@ -5,7 +5,7 @@ namespace NHSE.Core; [StructLayout(LayoutKind.Sequential, Pack = 1)] -public class GSaveVisitorNpc +public sealed class GSaveVisitorNpc { public const int SIZE = 0x78; private const int Days = 7; @@ -46,7 +46,7 @@ public struct V3f public float Y { get; set; } public float Z { get; set; } - public override string ToString() => $"({X},{Y},{Z})"; + public readonly override string ToString() => $"({X},{Y},{Z})"; } public enum VisitorNPC diff --git a/NHSE.Core/Structures/Misc/MapManager.cs b/NHSE.Core/Structures/Misc/MapManager.cs deleted file mode 100644 index 7ef42f1..0000000 --- a/NHSE.Core/Structures/Misc/MapManager.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; - -namespace NHSE.Core; - -public class MapManager : MapTerrainStructure -{ - public readonly FieldItemManager Items; - - 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) - { - var tiles = sav.GetMapDesignTiles(); - for (int i = 0; i < tiles.Length; i++) - tiles[i] = MainSave.MapDesignNone; - sav.SetMapDesignTiles(tiles); - } - - public static byte[] ExportDesignTiles(MainSave sav) - { - var tiles = sav.GetMapDesignTiles(); - var result = new byte[tiles.Length * 2]; - Buffer.BlockCopy(tiles, 0, result, 0, result.Length); - return result; - } - - public static void ImportDesignTiles(MainSave sav, ReadOnlySpan result) - { - var tiles = sav.GetMapDesignTiles(); - MemoryMarshal.Cast(result).CopyTo(tiles); - sav.SetMapDesignTiles(tiles); - } -} - -public class MapTerrainStructure -{ - public readonly TerrainLayer Terrain; - public readonly IReadOnlyList 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; - } -} \ No newline at end of file diff --git a/NHSE.Core/Structures/Misc/MapView.cs b/NHSE.Core/Structures/Misc/MapView.cs deleted file mode 100644 index e49600d..0000000 --- a/NHSE.Core/Structures/Misc/MapView.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; - -namespace NHSE.Core; - -public class MapView -{ - private const int ViewInterval = 2; - public readonly MapManager Map; - - public int MapScale { get; } = 1; - public int AcreScale { get; } - public int TerrainScale => AcreScale * 2; - - // Top Left Anchor Coordinates - public int X { get; set; } - public int Y { get; set; } - - protected MapView(MapManager m, int scale = 16) - { - AcreScale = scale; - Map = m; - } - - public bool CanUp => Y != 0; - public bool CanDown => Y < Map.CurrentLayer.MaxHeight - Map.CurrentLayer.GridHeight; - public bool CanLeft => X != 0; - public bool CanRight => X < Map.CurrentLayer.MaxWidth - Map.CurrentLayer.GridWidth; - - public bool ArrowUp() - { - if (Y <= 0) - return false; - Y -= ViewInterval; - return true; - } - - public bool ArrowLeft() - { - if (X <= 0) - return false; - X -= ViewInterval; - return true; - } - - public bool ArrowRight() - { - if (X >= Map.CurrentLayer.MaxWidth - 2) - return false; - X += ViewInterval; - return true; - } - - public bool ArrowDown() - { - if (Y >= Map.CurrentLayer.MaxHeight - ViewInterval) - return false; - Y += ViewInterval; - return true; - } - - 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)); - bool diff = X != newX || Y != newY; - X = newX; - Y = newY; - return diff; - } - - public void SetViewToAcre(in int acre) - { - var layer = Map.Items.Layer1; - layer.GetViewAnchorCoordinates(acre, out var x, out var y); - SetViewTo(x, y); - } - - public int ModifyFieldItems(Func action, in bool wholeMap) - { - var layer = Map.CurrentLayer; - return wholeMap - ? action(0, 0, layer.MaxWidth, layer.MaxHeight) - : action(X, Y, layer.GridWidth, layer.GridHeight); - } - - 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); - } - - public void GetCursorCoordinates(in int mX, in int mY, out int x, out int y) - { - x = mX / MapScale; - y = mY / MapScale; - } - - public void GetViewAnchorCoordinates(int mX, int mY, out int x, out int y, bool centerReticle) - { - GetCursorCoordinates(mX, mY, out x, out y); - var layer = Map.Items.Layer1; - layer.GetViewAnchorCoordinates(ref x, ref y, centerReticle); - } -} \ No newline at end of file diff --git a/NHSE.Core/Structures/Misc/Museum.cs b/NHSE.Core/Structures/Misc/Museum.cs index 3285372..ead6874 100644 --- a/NHSE.Core/Structures/Misc/Museum.cs +++ b/NHSE.Core/Structures/Misc/Museum.cs @@ -4,15 +4,12 @@ namespace NHSE.Core; -public class Museum +public sealed class Museum(Memory raw) { public const int SIZE = 0x3404; public const int EntryCount = 1024; - public readonly Memory Raw; - public Span Data => Raw.Span; - - public Museum(Memory data) => Raw = data; + public Span Data => raw.Span; public int MuseumLevel { diff --git a/NHSE.Core/Structures/Misc/MuseumEditor.cs b/NHSE.Core/Structures/Misc/MuseumEditor.cs index 15a21f3..08758d0 100644 --- a/NHSE.Core/Structures/Misc/MuseumEditor.cs +++ b/NHSE.Core/Structures/Misc/MuseumEditor.cs @@ -5,27 +5,17 @@ namespace NHSE.Core; -public class MuseumEditor +public sealed record MuseumEditor(Museum Museum) { - public readonly Museum Museum; - public readonly GSaveDate[] Dates; - public readonly Item[] Items; - public readonly byte[] Players; - - public MuseumEditor(Museum museum) - { - Museum = museum; - Dates = museum.GetDates(); - Items = museum.GetItems(); - Players = museum.GetPlayers(); - } + public readonly GSaveDate[] Dates = Museum.GetDates(); + public readonly Item[] Items = Museum.GetItems(); + public readonly byte[] Players = Museum.GetPlayers(); public void Save() { - var museum = Museum; - museum.SetDates(Dates); - museum.SetItems(Items); - museum.SetPlayers(Players); + Museum.SetDates(Dates); + Museum.SetItems(Items); + Museum.SetPlayers(Players); } public IEnumerable GetDonationSummary(GameStrings str) diff --git a/NHSE.Core/Structures/Misc/RecipeBook.cs b/NHSE.Core/Structures/Misc/RecipeBook.cs index e88b131..2c42f34 100644 --- a/NHSE.Core/Structures/Misc/RecipeBook.cs +++ b/NHSE.Core/Structures/Misc/RecipeBook.cs @@ -3,16 +3,14 @@ namespace NHSE.Core; -public class RecipeBook +public sealed class RecipeBook(Memory raw) { private const int BitFlagArraySize = 0x100; private const int BitFlagArrayCount = 4; public const int SIZE = BitFlagArraySize * BitFlagArrayCount; public const ushort RecipeCount = BitFlagArraySize * 8; - private readonly Memory Raw; - private Span Data => Raw.Span; - public RecipeBook(Memory raw) => Raw = raw; + private Span Data => raw.Span; public void Save(Span data) => Data.CopyTo(data); diff --git a/NHSE.Core/Structures/Misc/ValueTypeTypeConverter.cs b/NHSE.Core/Structures/Misc/ValueTypeTypeConverter.cs index dfca493..ba0f7c9 100644 --- a/NHSE.Core/Structures/Misc/ValueTypeTypeConverter.cs +++ b/NHSE.Core/Structures/Misc/ValueTypeTypeConverter.cs @@ -7,7 +7,7 @@ namespace NHSE.Core; /// /// Used for allowing a struct to be mutated in a PropertyGrid. /// -public class ValueTypeTypeConverter : ExpandableObjectConverter +public sealed class ValueTypeTypeConverter : ExpandableObjectConverter { public override bool GetCreateInstanceSupported(ITypeDescriptorContext? context) => true; diff --git a/NHSE.Core/Structures/Records/LifeSupportAchievement.cs b/NHSE.Core/Structures/Records/LifeSupportAchievement.cs index f946234..565c41c 100644 --- a/NHSE.Core/Structures/Records/LifeSupportAchievement.cs +++ b/NHSE.Core/Structures/Records/LifeSupportAchievement.cs @@ -6,51 +6,24 @@ namespace NHSE.Core; /// /// Multi-milestone definition for tracking game-play achievements. /// -public class LifeSupportAchievement : INamedValue +public sealed record LifeSupportAchievement( + ushort Index, + byte AchievementCount, + uint Threshold1, + uint Threshold2, + uint Threshold3, + uint Threshold4, + uint Threshold5, + short FlagLand, + short FlagPlayer, + string Name) + : INamedValue { /// /// Amount of milestones an achievement can have. /// public const int MilestoneMax = 6; - public readonly short FlagLand; - public readonly short FlagPlayer; - - public ushort Index { get; } - public string Name { get; } - - /// Total number of milestones for this achievement type. - public readonly int AchievementCount; - - /// First Milestone's Satisfaction Threshold - public readonly uint Threshold1; - - /// Second Milestone's Satisfaction Threshold - public readonly uint Threshold2; - - /// Third Milestone's Satisfaction Threshold - public readonly uint Threshold3; - - /// Fourth Milestone's Satisfaction Threshold - public readonly uint Threshold4; - - /// Fifth Milestone's Satisfaction Threshold - public readonly uint Threshold5; - - public LifeSupportAchievement(ushort index, byte max, uint t1, uint t2, uint t3, uint t4, uint t5, short land, short player, string name) - { - Index = index; - AchievementCount = max; - Threshold1 = t1; - Threshold2 = t2; - Threshold3 = t3; - Threshold4 = t4; - Threshold5 = t5; - FlagLand = land; - FlagPlayer = player; - Name = name; - } - public uint MaxThreshold => Math.Max(Threshold1, Math.Max(Threshold2, Math.Max(Threshold3, Math.Max(Threshold4, Threshold5)))); /// diff --git a/NHSE.Core/Structures/TurnipStonk.cs b/NHSE.Core/Structures/TurnipStonk.cs index a54435a..5f8fa08 100644 --- a/NHSE.Core/Structures/TurnipStonk.cs +++ b/NHSE.Core/Structures/TurnipStonk.cs @@ -4,7 +4,7 @@ namespace NHSE.Core; [StructLayout(LayoutKind.Sequential, Size = SIZE)] -public class TurnipStonk // GSaveShopKabu +public sealed class TurnipStonk // GSaveShopKabu { public const int SIZE = 0x44; diff --git a/NHSE.Core/Structures/Villager/GSaveMemory.cs b/NHSE.Core/Structures/Villager/GSaveMemory.cs index 7129c39..c8c0456 100644 --- a/NHSE.Core/Structures/Villager/GSaveMemory.cs +++ b/NHSE.Core/Structures/Villager/GSaveMemory.cs @@ -3,14 +3,11 @@ namespace NHSE.Core; -public class GSaveMemory : IVillagerOrigin +public sealed class GSaveMemory(Memory raw) : IVillagerOrigin { public const int SIZE = 0x5F0; - public readonly Memory Raw; - public Span Data => Raw.Span; - - public GSaveMemory(Memory data) => Raw = data; + public Span Data => raw.Span; public GSavePlayerId PlayerId { diff --git a/NHSE.Core/Structures/Villager/IPlayerRoom.cs b/NHSE.Core/Structures/Villager/IPlayerRoom.cs index 610dba0..1accaca 100644 --- a/NHSE.Core/Structures/Villager/IPlayerRoom.cs +++ b/NHSE.Core/Structures/Villager/IPlayerRoom.cs @@ -8,6 +8,6 @@ public interface IPlayerRoom byte[] Write(); string Extension { get; } - RoomItemLayer[] GetItemLayers(); - void SetItemLayers(IReadOnlyList value); + LayerRoomItem[] GetItemLayers(); + void SetItemLayers(IReadOnlyList value); } \ No newline at end of file diff --git a/NHSE.Core/Structures/Villager/PlayerHouse1.cs b/NHSE.Core/Structures/Villager/PlayerHouse1.cs index d8f304e..06c34b5 100644 --- a/NHSE.Core/Structures/Villager/PlayerHouse1.cs +++ b/NHSE.Core/Structures/Villager/PlayerHouse1.cs @@ -4,15 +4,12 @@ namespace NHSE.Core; -public class PlayerHouse1 : IPlayerHouse +public class PlayerHouse1(Memory raw) : IPlayerHouse { public const int SIZE = 0x26400; public virtual string Extension => "nhph"; - public readonly Memory Raw; - public Span Data => Raw.Span; - - public PlayerHouse1(Memory data) => Raw = data; + public Span Data => raw.Span; public byte[] Write() => Data.ToArray(); diff --git a/NHSE.Core/Structures/Villager/PlayerHouse2.cs b/NHSE.Core/Structures/Villager/PlayerHouse2.cs index 4144380..13dccd8 100644 --- a/NHSE.Core/Structures/Villager/PlayerHouse2.cs +++ b/NHSE.Core/Structures/Villager/PlayerHouse2.cs @@ -2,13 +2,11 @@ namespace NHSE.Core; -public class PlayerHouse2 : PlayerHouse1 +public sealed class PlayerHouse2(Memory raw) : PlayerHouse1(raw) { public new const int SIZE = 0x28A28; public override string Extension => "nhph2"; - public PlayerHouse2(Memory data) : base(data) { } - public override IPlayerRoom GetRoom(int roomIndex) { if ((uint)roomIndex >= MaxRoom) diff --git a/NHSE.Core/Structures/Villager/PlayerRoom1.cs b/NHSE.Core/Structures/Villager/PlayerRoom1.cs index 92a7e3a..a033b61 100644 --- a/NHSE.Core/Structures/Villager/PlayerRoom1.cs +++ b/NHSE.Core/Structures/Villager/PlayerRoom1.cs @@ -3,15 +3,12 @@ namespace NHSE.Core; -public class PlayerRoom1 : IPlayerRoom +public class PlayerRoom1(Memory raw) : IPlayerRoom { public const int SIZE = 0x65C8; public virtual string Extension => "nhpr"; - public readonly Memory Raw; - public Span Data => Raw.Span; - - public PlayerRoom1(Memory data) => Raw = data; + public Span Data => raw.Span; public byte[] Write() => Data.ToArray(); @@ -23,8 +20,8 @@ public class PlayerRoom1 : IPlayerRoom */ public const int LayerCount = 8; - public RoomItemLayer[] GetItemLayers() => RoomItemLayer.GetArray(Data[..(LayerCount * RoomItemLayer.SIZE)].ToArray()); - public void SetItemLayers(IReadOnlyList value) => RoomItemLayer.SetArray(value).AsSpan().CopyTo(Data); + public LayerRoomItem[] GetItemLayers() => LayerRoomItem.GetArray(Data[..(LayerCount * LayerRoomItem.SIZE)].ToArray()); + public void SetItemLayers(IReadOnlyList value) => LayerRoomItem.SetArray(value).CopyTo(Data); public bool GetIsActive(int layer, int x, int y) => FlagUtil.GetFlag(Data, 0x6400 + (layer * 0x34), (y * 20) + x); public void SetIsActive(int layer, int x, int y, bool value = true) => FlagUtil.SetFlag(Data, 0x6400 + (layer * 0x34), (y * 20) + x, value); diff --git a/NHSE.Core/Structures/Villager/PlayerRoom2.cs b/NHSE.Core/Structures/Villager/PlayerRoom2.cs index 8098a87..277ead6 100644 --- a/NHSE.Core/Structures/Villager/PlayerRoom2.cs +++ b/NHSE.Core/Structures/Villager/PlayerRoom2.cs @@ -4,12 +4,10 @@ namespace NHSE.Core; -public class PlayerRoom2 : PlayerRoom1 +public sealed class PlayerRoom2(Memory raw) : PlayerRoom1(raw) { public new const int SIZE = 0x6C24; - public new virtual string Extension => "nhpr2"; - - public PlayerRoom2(Memory data) : base(data) { } + public override string Extension => "nhpr2"; /* s_665e9093 ExtraEffectLayerList[2]; // @0x65c8 size 0x320, align 2 @@ -49,7 +47,7 @@ public GSaveMusicBoxInfo MusicBoxInfo // 3 bytes padding - public s_e13a81f4 _cfb139b9 + public s_e13a81f4 Unk_cfb139b9 { get => Data.Slice(0x6C10, s_e13a81f4.SIZE).ToStructure(); set => value.ToBytes().CopyTo(Data[0x6C10..]); diff --git a/NHSE.Core/Structures/Villager/Villager1.cs b/NHSE.Core/Structures/Villager/Villager1.cs index b298c32..6f24c14 100644 --- a/NHSE.Core/Structures/Villager/Villager1.cs +++ b/NHSE.Core/Structures/Villager/Villager1.cs @@ -8,14 +8,12 @@ namespace NHSE.Core; /// /// Villager object format from 1.0 to update 1.4 /// -public sealed class Villager1 : IVillager +public sealed class Villager1(Memory raw) : IVillager { public const int SIZE = 0x12AB0; public string Extension => "nhv"; - public readonly Memory Raw; - public Span Data => Raw.Span; - public Villager1(Memory data) => Raw = data; + public Span Data => raw.Span; public byte[] Write() => Data.ToArray(); public byte Species { get => Data[0]; set => Data[0] = value; } @@ -34,7 +32,7 @@ public GSaveMemory GetMemory(int index) if ((uint) index >= PlayerMemoryCount) throw new ArgumentOutOfRangeException(nameof(index)); - var bytes = Raw.Slice(0x4 + (index * GSaveMemory.SIZE), GSaveMemory.SIZE); + var bytes = raw.Slice(0x4 + (index * GSaveMemory.SIZE), GSaveMemory.SIZE); return new GSaveMemory(bytes); } @@ -115,7 +113,7 @@ public GSaveRoomFloorWall Room public DesignPatternPRO Design { - get => new(Raw.Slice(0x12128, DesignPatternPRO.SIZE)); + get => new(raw.Slice(0x12128, DesignPatternPRO.SIZE)); set => value.Data.CopyTo(Data[0x12128..]); } diff --git a/NHSE.Core/Structures/Villager/VillagerHouse1.cs b/NHSE.Core/Structures/Villager/VillagerHouse1.cs index 9318c37..56bb9eb 100644 --- a/NHSE.Core/Structures/Villager/VillagerHouse1.cs +++ b/NHSE.Core/Structures/Villager/VillagerHouse1.cs @@ -3,15 +3,13 @@ namespace NHSE.Core; -public class VillagerHouse1 : IVillagerHouse +public class VillagerHouse1(Memory raw) : IVillagerHouse { public const int SIZE = 0x1D4; public const int ItemCount = 36; public virtual string Extension => "nhvh"; - public readonly Memory Raw; - public VillagerHouse1(Memory raw) => Raw = raw; - public Span Data => Raw.Span; + public Span Data => raw.Span; public byte[] Write() => Data.ToArray(); diff --git a/NHSE.Core/Structures/Villager/VillagerHouse2.cs b/NHSE.Core/Structures/Villager/VillagerHouse2.cs index 29cc5bb..cac3f34 100644 --- a/NHSE.Core/Structures/Villager/VillagerHouse2.cs +++ b/NHSE.Core/Structures/Villager/VillagerHouse2.cs @@ -5,13 +5,11 @@ namespace NHSE.Core; -public class VillagerHouse2 : VillagerHouse1 +public sealed class VillagerHouse2(Memory raw) : VillagerHouse1(raw) { public new const int SIZE = 0x12E8; public override string Extension => "nhvh2"; - public VillagerHouse2(Memory data) : base(data) { } - // 0x1D4-0x12DB -- 0x1108 sized structure // 0x12DC -- 8 byte item // 0x12E4 -- 1 byte diff --git a/NHSE.Core/Util/ComboItem.cs b/NHSE.Core/Util/ComboItem.cs index bdc154b..0920d4b 100644 --- a/NHSE.Core/Util/ComboItem.cs +++ b/NHSE.Core/Util/ComboItem.cs @@ -6,7 +6,7 @@ namespace NHSE.Core; /// /// Key Value pair for a displayed and underlying value. /// -public record ComboItem(string Text, int Value); +public sealed record ComboItem(string Text, int Value); public static class ComboItemUtil { @@ -25,10 +25,10 @@ public static List GetArray(ReadOnlySpan items) return result; } - public static List GetArray(Type t) where T : struct, IFormattable + public static List GetArray() where T : struct, Enum, IFormattable { - var names = Enum.GetNames(t); - var values = (T[])Enum.GetValues(t); + var names = Enum.GetNames(); + var values = Enum.GetValues(); var acres = new List(names.Length); for (int i = 0; i < names.Length; i++) diff --git a/NHSE.Core/Util/FlagUtil.cs b/NHSE.Core/Util/FlagUtil.cs index d93df2a..ffe6ac3 100644 --- a/NHSE.Core/Util/FlagUtil.cs +++ b/NHSE.Core/Util/FlagUtil.cs @@ -21,4 +21,19 @@ public static void SetFlag(Span arr, int offset, int bitIndex, bool value) arr[offset] &= (byte)~(1 << bitIndex); arr[offset] |= (byte)((value ? 1 : 0) << bitIndex); } + + public static bool GetFlag(ReadOnlySpan arr, int bitIndex) + { + var b = arr[bitIndex >> 3]; + var mask = 1 << (bitIndex & 7); + return (b & mask) != 0; + } + + public static void SetFlag(Span arr, int bitIndex, bool value) + { + var offset = bitIndex >> 3; + bitIndex &= 7; // ensure bit access is 0-7 + arr[offset] &= (byte)~(1 << bitIndex); + arr[offset] |= (byte)((value ? 1 : 0) << bitIndex); + } } \ No newline at end of file diff --git a/NHSE.Core/Util/StructConverter.cs b/NHSE.Core/Util/StructConverter.cs index 196dc78..e4ecfdf 100644 --- a/NHSE.Core/Util/StructConverter.cs +++ b/NHSE.Core/Util/StructConverter.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace NHSE.Core; @@ -82,6 +83,14 @@ public static class StructConverter return result; } + public static byte[] SetArray(this ReadOnlySpan data, int size) where T : class + { + var result = new byte[data.Length * size]; + for (int i = 0; i < data.Length; i++) + data[i].ToBytesClass().CopyTo(result, i * size); + return result; + } + public static byte[] SetArrayStructure(this ReadOnlySpan data, int size) where T : struct { var result = new byte[data.Length * size]; diff --git a/NHSE.Injection/Injector/AutoInjector.cs b/NHSE.Injection/Injector/AutoInjector.cs index d17713c..55bf2df 100644 --- a/NHSE.Injection/Injector/AutoInjector.cs +++ b/NHSE.Injection/Injector/AutoInjector.cs @@ -3,12 +3,8 @@ namespace NHSE.Injection; -public class AutoInjector +public sealed record AutoInjector(IDataInjector Injector, Action DoRead, Action DoWrite) { - public readonly IDataInjector Injector; - private readonly Action AfterRead; - private readonly Action AfterWrite; - public bool AutoInjectEnabled { private get; set; } public bool ValidateEnabled @@ -17,13 +13,6 @@ public bool ValidateEnabled set => Injector.ValidateEnabled = value; } - public AutoInjector(IDataInjector inj, Action read, Action write) - { - Injector = inj; - AfterRead = read; - AfterWrite = write; - } - public void Validate() => Injector.Validate(); public InjectionResult Read(bool force = false) @@ -34,7 +23,7 @@ public InjectionResult Read(bool force = false) try { var result = Injector.Read(); - AfterRead(result); + DoRead(result); return result; } catch (IndexOutOfRangeException ex) @@ -51,7 +40,7 @@ public InjectionResult Write(bool force = false) try { var result = Injector.Write(); - AfterWrite(result); + DoWrite(result); return result; } catch (IndexOutOfRangeException ex) diff --git a/NHSE.Injection/PocketInjector.cs b/NHSE.Injection/PocketInjector.cs index b8d1ad7..7a0543f 100644 --- a/NHSE.Injection/PocketInjector.cs +++ b/NHSE.Injection/PocketInjector.cs @@ -1,35 +1,27 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using NHSE.Core; namespace NHSE.Injection; -public class PocketInjector : IDataInjector +public sealed class PocketInjector(IReadOnlyList items, IRAMReadWriter bot) : IDataInjector { - private readonly IReadOnlyList Items; - private readonly IRAMReadWriter Bot; - public bool Connected => Bot.Connected; + public bool Connected => bot.Connected; + private byte[]? LastData { get; set; } public uint WriteOffset { private get; set; } public bool ValidateEnabled { get; set; } = true; public bool SpoofInventoryWrite { get; set; } - private static readonly Item DroppableOnlyItem = new(0x9C9); // Gold nugget - public PocketInjector(IReadOnlyList items, IRAMReadWriter bot) - { - Items = items; - Bot = bot; - } + private static readonly Item DroppableOnlyItem = new(0x9C9); // Gold nugget public bool ReadValidate(out byte[] data) { PlayerItemSet.GetOffsetLength(WriteOffset, out var offset, out var size); - data = Bot.ReadBytes(offset, size); + data = bot.ReadBytes(offset, size); return Validate(data); } - private byte[]? LastData; - public InjectionResult Read() { if (!ReadValidate(out var data)) @@ -38,7 +30,7 @@ public InjectionResult Read() if (LastData?.SequenceEqual(data) == true) return InjectionResult.Same; - PlayerItemSet.ReadPlayerInventory(data, Items); + PlayerItemSet.ReadPlayerInventory(data, items); LastData = data; @@ -52,9 +44,9 @@ public InjectionResult Write() var orig = (byte[])data.Clone(); - var items = !SpoofInventoryWrite ? Items : Enumerable.Repeat(DroppableOnlyItem, Items.Count).ToArray(); + var items1 = !SpoofInventoryWrite ? items : Enumerable.Repeat(DroppableOnlyItem, items.Count).ToArray(); - PlayerItemSet.WritePlayerInventory(data, items); + PlayerItemSet.WritePlayerInventory(data, items1); if (data.SequenceEqual(orig)) return InjectionResult.Same; @@ -63,7 +55,7 @@ public InjectionResult Write() if (size != data.Length) return InjectionResult.FailBadSize; - Bot.WriteBytes(data, offset); + bot.WriteBytes(data, offset); LastData = data; diff --git a/NHSE.Injection/SysBot/Decoder.cs b/NHSE.Injection/SysBot/Decoder.cs index 0de97c1..59f45ba 100644 --- a/NHSE.Injection/SysBot/Decoder.cs +++ b/NHSE.Injection/SysBot/Decoder.cs @@ -7,44 +7,35 @@ public static class Decoder private static bool IsNum(char c) => (uint)(c - '0') <= 9; private static bool IsHexUpper(char c) => (uint)(c - 'A') <= 5; - public static byte[] ConvertHexByteStringToBytes(byte[] bytes) + public static byte[] ConvertHexByteStringToBytes(ReadOnlySpan bytes) { var dest = new byte[bytes.Length / 2]; - for (int i = 0; i < dest.Length; i++) - { - int ofs = i * 2; - var _0 = (char)bytes[ofs + 0]; - var _1 = (char)bytes[ofs + 1]; - dest[i] = DecodeTuple(_0, _1); - } + LoadHexBytesTo(bytes, dest, 2); return dest; } + public static void LoadHexBytesTo(ReadOnlySpan str, Span dest, int tupleSize) + { + // The input string is 2-char hex values optionally separated. + // The destination array should always be larger or equal than the bytes written. Let the runtime bounds check us. + // Iterate through the string without allocating. + for (int i = 0, j = 0; i < str.Length; i += tupleSize) + dest[j++] = DecodeTuple((char)str[i + 0], (char)str[i + 1]); + } + private static byte DecodeTuple(char _0, char _1) { - byte result; - if (IsNum(_0)) - result = (byte)((_0 - '0') << 4); - else if (IsHexUpper(_0)) - result = (byte)((_0 - 'A' + 10) << 4); - else + return (byte)(DecodeChar(_0) << 4 | DecodeChar(_1)); + + static int DecodeChar(char x) + { + if (char.IsAsciiDigit(x)) + return (byte)(x - '0'); + if (char.IsAsciiHexDigitUpper(x)) + return (byte)(x - 'A' + 10); + if (char.IsAsciiHexDigitLower(x)) + return (byte)(x - 'a' + 10); throw new ArgumentOutOfRangeException(nameof(_0)); - - if (IsNum(_1)) - result |= (byte)(_1 - '0'); - else if (IsHexUpper(_1)) - result |= (byte)(_1 - 'A' + 10); - else - throw new ArgumentOutOfRangeException(nameof(_1)); - return result; - } - - public static byte[] StringToByteArray(string hex) - { - int NumberChars = hex.Length; - byte[] bytes = new byte[NumberChars / 2]; - for (int i = 0; i < NumberChars; i += 2) - bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); - return bytes; + } } } \ No newline at end of file diff --git a/NHSE.Injection/SysBot/SysBot.cs b/NHSE.Injection/SysBot/SysBot.cs index bd9a5c7..d7ed295 100644 --- a/NHSE.Injection/SysBot/SysBot.cs +++ b/NHSE.Injection/SysBot/SysBot.cs @@ -3,7 +3,7 @@ namespace NHSE.Injection; -public class SysBot : IRAMReadWriter +public sealed class SysBot : IRAMReadWriter { public string IP = "192.168.1.65"; public int Port = 6000; diff --git a/NHSE.Injection/SysBot/USBBot.cs b/NHSE.Injection/SysBot/USBBot.cs index fee0a6d..fb891db 100644 --- a/NHSE.Injection/SysBot/USBBot.cs +++ b/NHSE.Injection/SysBot/USBBot.cs @@ -6,7 +6,7 @@ namespace NHSE.Injection; -public class USBBot : IRAMReadWriter +public sealed class USBBot : IRAMReadWriter { private UsbDevice? SwDevice; private UsbEndpointReader? reader; diff --git a/NHSE.Parsing/BCSV/BCSV.cs b/NHSE.Parsing/BCSV/BCSV.cs index 575efe1..2e3b59b 100644 --- a/NHSE.Parsing/BCSV/BCSV.cs +++ b/NHSE.Parsing/BCSV/BCSV.cs @@ -7,7 +7,7 @@ namespace NHSE.Parsing; -public class BCSV +public sealed class BCSV { public static readonly BCSVEnumDictionary EnumLookup = new(Resources.specs_130.Split('\n')); public static bool DecodeColumnNames { private get; set; } = true; diff --git a/NHSE.Parsing/BCSV/BCSVEnumDictionary.cs b/NHSE.Parsing/BCSV/BCSVEnumDictionary.cs index abceb75..09457f4 100644 --- a/NHSE.Parsing/BCSV/BCSVEnumDictionary.cs +++ b/NHSE.Parsing/BCSV/BCSVEnumDictionary.cs @@ -5,9 +5,9 @@ namespace NHSE.Parsing; -public class BCSVEnumDictionary +public sealed class BCSVEnumDictionary { - private readonly Dictionary Lookup = []; + private readonly Dictionary _lookup = []; public BCSVEnumDictionary(IEnumerable lines) { @@ -35,7 +35,7 @@ private void AddColumnName(string trim) var slice = value.AsSpan(2, value.Length - 3); var hex = StringUtil.GetHexValue(slice); - if (Lookup.TryGetValue(hex, out var exist)) + if (_lookup.TryGetValue(hex, out var exist)) { if (exist == name) return; @@ -43,7 +43,7 @@ private void AddColumnName(string trim) return; } - Lookup.Add(hex, name); + _lookup.Add(hex, name); } private void AddEnumName(string trim) @@ -56,7 +56,7 @@ private void AddEnumName(string trim) return; var hash = CRC32.Compute(text); - if (Lookup.TryGetValue(hash, out var exist)) + if (_lookup.TryGetValue(hash, out var exist)) { if (exist == text) return; @@ -64,10 +64,10 @@ private void AddEnumName(string trim) return; } - Lookup.Add(hash, text); + _lookup.Add(hash, text); } - public IEnumerable Dump() => Lookup.Select(z => $"{z.Key:X8}\t{z.Value}"); + public IEnumerable Dump() => _lookup.Select(z => $"{z.Key:X8}\t{z.Value}"); - public string this[uint key] => Lookup.TryGetValue(key, out var val) ? val : $"0x{key:X8}"; + public string this[uint key] => _lookup.TryGetValue(key, out var val) ? val : $"0x{key:X8}"; } \ No newline at end of file diff --git a/NHSE.Parsing/BCSV/BCSVFieldParam.cs b/NHSE.Parsing/BCSV/BCSVFieldParam.cs index d030116..d362f58 100644 --- a/NHSE.Parsing/BCSV/BCSVFieldParam.cs +++ b/NHSE.Parsing/BCSV/BCSVFieldParam.cs @@ -1,16 +1,6 @@ namespace NHSE.Parsing; -public class BCSVFieldParam +public sealed record BCSVFieldParam(uint ColumnKey, int Offset, int Index) { public const int SIZE = 8; - public readonly uint ColumnKey; - public readonly int Offset; - public readonly int Index; - - public BCSVFieldParam(uint key, int offset, int index) - { - ColumnKey = key; - Offset = offset; - Index = index; - } } \ No newline at end of file diff --git a/NHSE.Parsing/GameMSBTDumper.cs b/NHSE.Parsing/GameMSBTDumper.cs index 9ec121d..a2c322d 100644 --- a/NHSE.Parsing/GameMSBTDumper.cs +++ b/NHSE.Parsing/GameMSBTDumper.cs @@ -14,7 +14,7 @@ public static class GameMSBTDumper /// Destination folder where the dumps will be saved. /// Convert all files to CSV for easy viewing. /// Delimiter when exporting the files - public static void UpdateDumps(string root, string dest, bool csv = true, char delim = '\t') + public static void UpdateDumps(string root, string dest, bool csv = false, char delim = '\t') { if (csv) UpdateCSV(root, Path.Combine(dest, "csv"), delim); @@ -74,7 +74,7 @@ public static string[] GetArtList(string msgPath) result.Add($"{itemID:00000}, // {Text}{fake}"); } result.Sort(); - return result.ToArray(); + return [..result]; } public static string[] GetVillagerPhraseResource(string msgPath) @@ -82,7 +82,8 @@ public static string[] GetVillagerPhraseResource(string msgPath) var file = Path.Combine(msgPath, "Npc", "STR_NNpcPhrase.msbt"); var list = GetLabelList(file); var normal = list.Select(z => $"{z.Label}\t{z.Text}").Order(); - return normal.ToArray(); + + return [..normal]; } public static string[] GetVillagerListResource(string msgPath) @@ -95,7 +96,7 @@ public static string[] GetVillagerListResource(string msgPath) list = GetLabelList(file); var special = list.Select(z => $"{z.Label}\t{z.Text}").Order(); - return normal.Concat(special).ToArray(); + return [..normal, ..special]; } public static Dictionary GetItemList(string msgPath, string language) diff --git a/NHSE.Parsing/MSBT/LBL1.cs b/NHSE.Parsing/MSBT/LBL1.cs index 84ffb24..a19710c 100644 --- a/NHSE.Parsing/MSBT/LBL1.cs +++ b/NHSE.Parsing/MSBT/LBL1.cs @@ -2,7 +2,7 @@ namespace NHSE.Parsing; -public class LBL1() : MSBTSection(string.Empty, []) +public sealed class LBL1() : MSBTSection(string.Empty, []) { public uint NumberOfGroups; diff --git a/NHSE.Parsing/MSBT/MSBT.cs b/NHSE.Parsing/MSBT/MSBT.cs index 5053bb3..d9dfe23 100644 --- a/NHSE.Parsing/MSBT/MSBT.cs +++ b/NHSE.Parsing/MSBT/MSBT.cs @@ -5,7 +5,7 @@ namespace NHSE.Parsing; -public class MSBT +public sealed class MSBT { public readonly MSBTHeader Header; public readonly LBL1 LBL1 = new(); @@ -78,7 +78,7 @@ private void ReadLBL1(BinaryReaderX br) var length = Convert.ToUInt32(br.ReadByte()); var name = br.ReadString((int)length); var index = br.ReadUInt32(); - var lbl = new MSBTLabel(name) {Index = index, Length = length}; + var lbl = new MSBTLabel {Name = name, Index = index, Length = length}; LBL1.Labels.Add(lbl); } } diff --git a/NHSE.Parsing/MSBT/MSBTGroup.cs b/NHSE.Parsing/MSBT/MSBTGroup.cs index 34353fb..0c601d3 100644 --- a/NHSE.Parsing/MSBT/MSBTGroup.cs +++ b/NHSE.Parsing/MSBT/MSBTGroup.cs @@ -1,7 +1,7 @@ namespace NHSE.Parsing; -public class MSBTGroup +public sealed record MSBTGroup { - public uint NumberOfLabels; - public uint Offset; + public required uint NumberOfLabels { get; init; } + public required uint Offset { get; init; } } \ No newline at end of file diff --git a/NHSE.Parsing/MSBT/MSBTHeader.cs b/NHSE.Parsing/MSBT/MSBTHeader.cs index 1eced1c..357007d 100644 --- a/NHSE.Parsing/MSBT/MSBTHeader.cs +++ b/NHSE.Parsing/MSBT/MSBTHeader.cs @@ -2,7 +2,7 @@ namespace NHSE.Parsing; -public class MSBTHeader +public sealed class MSBTHeader { public readonly string Identifier; // MsgStdBn public readonly byte[] ByteOrderMark; diff --git a/NHSE.Parsing/MSBT/MSBTLabel.cs b/NHSE.Parsing/MSBT/MSBTLabel.cs index 5160358..e2c7c06 100644 --- a/NHSE.Parsing/MSBT/MSBTLabel.cs +++ b/NHSE.Parsing/MSBT/MSBTLabel.cs @@ -2,19 +2,14 @@ namespace NHSE.Parsing; -public class MSBTLabel +public sealed record MSBTLabel { - public uint Length; - public readonly string Name; - public MSBTTextString String; + public required uint Length { get; init; } + public required uint Index { get; init; } + public required string Name { get; init; } - public MSBTLabel(string name) - { - Name = name; - String = MSBTTextString.Empty; - } + public MSBTTextString String { get; set; } = MSBTTextString.Empty; - public uint Index { get; set; } public override string ToString() => Length > 0 ? Name : (Index + 1).ToString(); public string ToString(Encoding encoding) => encoding.GetString(String.Value.Span); } \ No newline at end of file diff --git a/NHSE.Parsing/MSBT/MSBTSection.cs b/NHSE.Parsing/MSBT/MSBTSection.cs index 8a5fc7a..778917f 100644 --- a/NHSE.Parsing/MSBT/MSBTSection.cs +++ b/NHSE.Parsing/MSBT/MSBTSection.cs @@ -1,14 +1,8 @@ namespace NHSE.Parsing; -public class MSBTSection +public abstract class MSBTSection(string identifier, byte[] padding) { - public string Identifier; - public uint SectionSize; // Begins after Unknown1 - public byte[] Padding1; // Always 0x0000 0000 - - public MSBTSection(string identifier, byte[] padding) - { - Identifier = identifier; - Padding1 = padding; - } + public string Identifier { get; set; } = identifier; + public uint SectionSize { get; set; } // Begins after Unknown1 + public byte[] Padding1 { get; set; } = padding; // Always 0x0000 0000 } \ No newline at end of file diff --git a/NHSE.Parsing/MSBT/MSBTTextString.cs b/NHSE.Parsing/MSBT/MSBTTextString.cs index 73ce141..d5f49c3 100644 --- a/NHSE.Parsing/MSBT/MSBTTextString.cs +++ b/NHSE.Parsing/MSBT/MSBTTextString.cs @@ -4,19 +4,10 @@ namespace NHSE.Parsing; -public class MSBTTextString +public sealed record MSBTTextString(Memory Value, uint Index) { - public readonly Memory Value; - public readonly uint Index; - public static readonly MSBTTextString Empty = new(default, 0); - public MSBTTextString(Memory v, uint i) - { - Value = v; - Index = i; - } - public override string ToString() => (Index + 1).ToString(); public string ToString(Encoding encoding) => encoding.GetString(Value.Span); diff --git a/NHSE.Parsing/MSBT/TXT2.cs b/NHSE.Parsing/MSBT/TXT2.cs index b5c066d..3da42b0 100644 --- a/NHSE.Parsing/MSBT/TXT2.cs +++ b/NHSE.Parsing/MSBT/TXT2.cs @@ -2,7 +2,7 @@ namespace NHSE.Parsing; -public class TXT2() : MSBTSection(string.Empty, []) +public sealed class TXT2() : MSBTSection(string.Empty, []) { public uint NumberOfStrings; diff --git a/NHSE.Parsing/PBC/PBC.cs b/NHSE.Parsing/PBC/PBC.cs index 187d643..4c3cfd3 100644 --- a/NHSE.Parsing/PBC/PBC.cs +++ b/NHSE.Parsing/PBC/PBC.cs @@ -6,7 +6,7 @@ namespace NHSE.Parsing; -public class PBC +public sealed class PBC { private const uint MAGIC = 0x00636270; // pbc\0 @@ -47,7 +47,7 @@ private byte[] GetTiles() } public byte GetTile(int x, int y) => Tiles[(y * Width) + x]; - public Color GetTileColor(int x, int y) => CollisionUtil.Dict[GetTile(x, y)]; + public Color GetTileColor(int x, int y) => TileCollisionUtil.Dict[GetTile(x, y)]; public uint Magic => ReadUInt32LittleEndian(Data); public uint Width => ReadUInt32LittleEndian(Data[0x04..]); diff --git a/NHSE.Parsing/ParseConverter.cs b/NHSE.Parsing/ParseConverter.cs index 3b49c26..24c48d1 100644 --- a/NHSE.Parsing/ParseConverter.cs +++ b/NHSE.Parsing/ParseConverter.cs @@ -1,4 +1,5 @@ -using System.IO; +using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Linq; namespace NHSE.Parsing; @@ -22,7 +23,7 @@ public static void ConvertItemStrings(string input, string output) private static string[] ConvertItemList(string path) { var lines = File.ReadAllLines(path); - var items = lines.Select(z => new ParseItem(z)).ToArray(); + var items = lines.Select(ParseItem.FromString).ToArray(); var max = items.Max(z => z.Index); var result = new string[max + 1]; @@ -32,14 +33,18 @@ private static string[] ConvertItemList(string path) } } -public class ParseItem +public readonly record struct ParseItem { - public readonly int Index; - public readonly string Name; + public required int Index {get; init; } + public required string Name { get; init; } + + [SetsRequiredMembers] public ParseItem(string line) { var split = line.Split(", "); Index = int.Parse(split[0], System.Globalization.NumberStyles.HexNumber); Name = split[1]; } + + public static ParseItem FromString(string s) => new(s); } \ No newline at end of file diff --git a/NHSE.Sprites/Field/ItemLayerSprite.cs b/NHSE.Sprites/Field/ItemLayerSprite.cs index 27e90df..0d874b5 100644 --- a/NHSE.Sprites/Field/ItemLayerSprite.cs +++ b/NHSE.Sprites/Field/ItemLayerSprite.cs @@ -4,183 +4,313 @@ namespace NHSE.Sprites; +/// +/// Logic for rendering an . +/// public static class ItemLayerSprite { - public static Bitmap GetBitmapItemLayer(ItemLayer layer) + private static readonly Pen Reticle = new(Color.Red); + + /// + /// Populates a bitmap data buffer with color values derived from a collection of items, arranging the colors in column-major order based on the specified width and height. + /// + /// + /// Each item's color is determined using and stored in ARGB format. + /// Colors are written to bmpData in column-major order, where each column is filled from top to bottom. + /// + /// List of items from which color values are extracted. The span must contain at least width × height elements. + /// Pixel data for the bitmap. The span must have a length of at least width × height. + /// Configuration for layer positioning. + private static void LoadBitmapLayer(ReadOnlySpan items, Span bmpData, in LayerPositionConfig cfg) { - var items = layer.Tiles; - var height = layer.MaxHeight; - var width = items.Length / height; + var (shiftX, shiftY) = cfg.GetCoordinatesAbsolute(); - var bmpData = new int[width * height]; - LoadBitmapLayer(items, bmpData, width, height); - - return ImageUtil.GetBitmap(bmpData, width, height); - } - - private static void LoadBitmapLayer(ReadOnlySpan items, Span bmpData, int width, int height) - { - for (int x = 0; x < width; x++) + // Iterate through the relative positions within the layer. + // Then, map to absolute positions in the bitmap with the configured shift. + var width = cfg.LayerTotalWidth; + var height = cfg.LayerTotalHeight; + var mapWidth = cfg.MapTotalWidth; // 1px scale + for (int relX = 0; relX < width; relX++) { - var ix = x * height; - for (int y = 0; y < height; y++) + var absX = relX + shiftX; + for (int relY = 0; relY < height; relY++) { - var index = ix + y; - var tile = items[index]; - bmpData[(y * width) + x] = FieldItemColor.GetItemColor(tile).ToArgb(); + // Get the tile at this position. + var tile = items[relY + relX * height]; + + // Get the actual shifted position in the bitmap. + var absY = relY + shiftY; + var offset = (absY * mapWidth) + absX; + + // Write the color to the bitmap data. + bmpData[offset] = FieldItemColor.GetItemColor(tile).ToArgb(); } } } - private static void LoadPixelsFromLayer(ItemLayer layer, int x0, int y0, int width, Span bmpData) + /// + /// Loads an item layer into a bitmap, scaling it up to the desired size, drawing special symbols, and overlaying a grid. + /// + /// Inflated acre bitmap to write to. + /// Item layer to draw from. + /// Configuration for layer positioning. + /// Top-left X coordinate to start drawing from, relative to the origin of the map. + /// Top-left Y coordinate to start drawing from. + /// Pixel data for 1px per tile image. + /// >Pixel data for final inflated image. + /// Scaling factor from 1px => final image dimensions. + /// Optional transparency override color. + /// Color to use for gridlines. + public static void LoadViewport(Bitmap imgScaled, LayerItem layer, in LayerPositionConfig cfg, int absX, int absY, + Span imgSingle, Span imgUpscaled, int imgScale, int transparency = -1, int gridlineColor = 0) { - var stride = layer.GridWidth; + // Update the 1px view-grid image pixel data. + LoadViewport(layer, cfg, imgSingle, absX, absY); - for (int y = 0; y < stride; y++) + // Get the final inflated size of the image. + var imgWidth = imgScaled.Width; + var imgHeight = imgScaled.Height; + // Inflate to the final size storage. + ImageUtil.ScalePixelImage(imgSingle, imgUpscaled, imgWidth, imgHeight, imgScale); + + // Draw symbols over special items now? + DrawDirectionals(layer, cfg, imgUpscaled, absX, absY, imgWidth, imgScale); + + // Optional transparency clamping to make image fainter. + if (transparency >>> 24 != 0xFF) + ImageUtil.ClampAllTransparencyTo(imgUpscaled, transparency); + + // Apply gridlines to visually separate each cell. + DrawGrid(imgUpscaled, imgWidth, imgHeight, gridlineColor, imgScale); + + // Update the bitmap, final data. + imgScaled.SetBitmapData(imgUpscaled); + } + + /// + /// Loads pixel data from the specified layer into the provided span, using the given starting coordinates and the layer's view dimensions. + /// + /// The layer from which to load pixel data. + /// Configuration for layer positioning. + /// Pixel data of the final image. + /// The x-coordinate of the upper-left corner in the map from which to start loading pixels. + /// The y-coordinate of the upper-left corner in the map from which to start loading pixels. + private static void LoadViewport(LayerItem layer, in LayerPositionConfig cfg, Span data, int absX, int absY) + { + var width = layer.TileInfo.ViewWidth; + var height = layer.TileInfo.ViewHeight; + + var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY); + + for (int y = 0; y < height; y++) { var baseIndex = (y * width); - for (int x = 0; x < stride; x++) + var tileY = relY + y; + for (int x = 0; x < width; x++) { - var tile = layer.GetTile(x + x0, y + y0); + var tileX = relX + x; + if (!cfg.IsCoordinateValidRelative(tileX, tileY)) + continue; + var tile = layer.GetTile(tileX, tileY); var color = FieldItemColor.GetItemColor(tile).ToArgb(); + var index = baseIndex + x; - bmpData[index] = color; + data[index] = color; } } } - // non-allocation image generator - public static Bitmap GetBitmapItemLayerViewGrid(ItemLayer layer, int x0, int y0, int scale, Span acre1, int[] acreScale, Bitmap dest, int transparency = -1, int gridlineColor = 0) + /// + /// Draws extension tile info for the tiles in the specified layer onto the provided pixel data. + /// + /// The layer containing the tiles to process. + /// Configuration for layer positioning. + /// Pixel data of the entire image. + /// Top-left X coordinate to start drawing from, relative to the origin of the map. + /// Top-left Y coordinate to start drawing from. + /// Width of the entire image. + /// Scaling factor from 1px => final image dimensions. + private static void DrawDirectionals(LayerItem layer, in LayerPositionConfig cfg, + Span data, + int absX, int absY, int imgWidth, int imgScale) { - var w = layer.GridWidth; - var h = layer.GridHeight; - LoadPixelsFromLayer(layer, x0, y0, w, acre1); - w *= scale; - h *= scale; - ImageUtil.ScalePixelImage(acre1, acreScale, w, h, scale); + var width = cfg.TilesPerAcre; + var height = cfg.TilesPerAcre; - if (transparency >>> 24 != 0xFF) - ImageUtil.ClampAllTransparencyTo(acreScale, transparency); + var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY); - // draw symbols over special items now? - DrawDirectionals(acreScale, layer, w, x0, y0, scale); - - // Slap on a grid - DrawGrid(acreScale, w, h, scale, gridlineColor); - - // Return final data - ImageUtil.SetBitmapData(dest, acreScale); - return dest; - } - - private static void DrawDirectionals(Span data, ItemLayer layer, int w, int x0, int y0, int scale) - { - for (int x = x0; x < x0 + layer.GridWidth; x++) + for (int viewX = 0; viewX < width; viewX++) { - for (int y = y0; y < y0 + layer.GridHeight; y++) + for (int viewY = 0; viewY < height; viewY++) { - var tile = layer.GetTile(x, y); + var pX = relX + viewX; + var pY = relY + viewY; + if (!cfg.IsCoordinateValidRelative(pX, pY)) + continue; + var tile = layer.GetTile(pX, pY); if (tile.IsNone) continue; - if (tile.IsBuried) - DrawX(data, (x - x0) * scale, (y - y0) * scale, scale, w); - else if (tile.IsDropped) - DrawPlus(data, (x - x0) * scale, (y - y0) * scale, scale, w); - else if (tile.IsExtension) - DrawDirectional(data, tile, (x - x0) * scale, (y - y0) * scale, scale, w); + // Apply cosmetic details based on the tile's details. + if (tile.IsBuried) + DrawX(data, viewX * imgScale, viewY * imgScale, imgScale, imgWidth); + else if (tile.IsDropped) + DrawPlus(data, viewX * imgScale, viewY * imgScale, imgScale, imgWidth); + else if (tile.IsExtension) + DrawDirectional(data, tile, viewX * imgScale, viewY * imgScale, imgScale, imgWidth); + + // Based on the display item, apply details. var id = tile.DisplayItemId; var kind = ItemInfo.GetItemKind(id); if (kind.IsFlowerGene(id)) { - int geneIndex; - if (tile.IsRoot) - { - geneIndex = 0; - } - else - { - geneIndex = (tile.ExtensionY << 1) | tile.ExtensionX; - tile = layer.GetTile(x - tile.ExtensionX, y - tile.ExtensionY); - } - + var geneIndex = GetGeneIndex(ref tile, layer, pX, pY); var genes = ((uint)tile.Genes ^ 0b00_11_00_00); // invert W bits var geneValue = (genes >> (geneIndex * 2)) & 3; if (geneValue == 0) continue; - DrawGene(data, (x - x0) * scale, (y - y0) * scale, scale, w, geneValue, geneIndex); + DrawGene(data, viewX * imgScale, viewY * imgScale, imgScale, imgWidth, geneValue, geneIndex); } } } } - private static void DrawGene(Span data, int x0, int y0, int scale, int w, uint geneValue, int geneIndex) + /// + /// Gets the index of the gene based on the specified extension value. Repoints the tile to root node if it is an extension tile. + /// + private static int GetGeneIndex(ref Item tile, LayerItem layer, int relX, int relY) { - var c = ShiftToGeneCoordinate(ref x0, ref y0, scale, geneIndex); - FillSquare(data, x0, y0, scale / 2, w, c, geneValue == 3 ? 1 : 2); + if (tile.IsRoot) + return 0; + + // Sanity check: can only extend by 1 in either direction, and must extend in same direction as relative position. + // Ignore bad extension values; we know the gene index from position alone. + var eX = relX & 1; + var eY = relY & 1; + var geneIndex = (eY << 1) | eX; + tile = layer.GetTile(relX - eX, relY - eY); + return geneIndex; } - private static void FillSquare(Span data, int x0, int y0, int scale, int w, int color, int increment) + /// + /// Draws a flower gene on the item cell. + /// + /// Pixel data of the entire image. + /// Top-left X coordinate to start drawing from. + /// Top-left Y coordinate to start drawing from. + /// Scale of the entire cell. + /// Width of the entire image. + /// Value of the gene (0-3). + /// 0-3 value indicating which gene (quadrant) to draw. + private static void DrawGene(Span data, int viewX, int viewY, int imgScale, int imgWidth, uint geneValue, int geneIndex) { - var baseIndex = (y0 * w) + x0; - for (int i = 0; i < scale * scale; i += increment) + var c = ShiftToGeneCoordinate(ref viewX, ref viewY, imgScale, geneIndex); + FillSquare(data, viewX, viewY, imgScale / 2, imgWidth, c, geneValue == 3 ? 1 : 2); + } + + /// + /// Fills a square region within a one-dimensional span with the specified color value, using a given increment to control the fill step. + /// + /// This method assumes that the specified region fits within the bounds of the provided span. + /// No bounds checking is performed. + /// The increment parameter can be used to fill a subset with the square's elements, which may be useful for performance or pattern effects. + /// + /// The span representing the target buffer to fill. Each element corresponds to a pixel or cell in a row-major order grid. + /// Top-left X coordinate to start drawing from. + /// Top-left Y coordinate to start drawing from. + /// Scale of the entire cell. + /// Width of the entire image. + /// The color value to assign to each filled element in the square region. + /// The step size to use when filling elements. Must be positive. A larger increment skips more elements within the square. + private static void FillSquare(Span data, int viewX, int viewY, int imgScale, int imgWidth, int color, int increment) + { + var baseIndex = (viewY * imgWidth) + viewX; + for (int i = 0; i < imgScale * imgScale; i += increment) { - var x = i % scale; - var y = i / scale; - var index = (y * w) + x; + var x = i % imgScale; + var y = i / imgScale; + var index = (y * imgWidth) + x; data[baseIndex + index] = color; } } - private static int ShiftToGeneCoordinate(ref int x0, ref int y0, int scale, int geneIndex) + /// + /// Adjusts the specified coordinates to the center of a gene region based on the given gene index and scale, and returns the corresponding ARGB color value for that region. + /// + /// + /// The method modifies the input coordinates in place according to the specified gene region. + /// The returned color value corresponds to the region: Red for bottom right, Yellow for bottom left, AntiqueWhite for top right, and Black for top left or any other index. + /// + /// Top-left X coordinate to start drawing from. + /// Top-left Y coordinate to start drawing from. + /// Scale of the entire cell. + /// The index of the gene region to shift to. Valid values are 0 (bottom right), 1 (bottom left), 2 (top right), and any other value for top left. + /// An integer representing the ARGB color value associated with the specified gene region. + private static int ShiftToGeneCoordinate(ref int absX, ref int absY, int imgScale, int geneIndex) { switch (geneIndex) { case 0: // bottom right - x0 += scale / 2; - y0 += scale / 2; + absX += imgScale / 2; + absY += imgScale / 2; return Color.Red.ToArgb(); case 1: // bottom left - y0 += scale / 2; + absY += imgScale / 2; return Color.Yellow.ToArgb(); case 2: // top right - x0 += scale / 2; + absX += imgScale / 2; return Color.AntiqueWhite.ToArgb(); default: // top left return Color.Black.ToArgb(); } } - private static void DrawPlus(Span data, int x0, int y0, int scale, int w) + /// + /// Updates the pixel data to draw a `+`, indicating the item as "dropped". + /// + /// Pixel data of the entire image. + /// Top-left X coordinate to start drawing from. + /// Top-left Y coordinate to start drawing from. + /// Scaling factor from 1px => final image dimensions. + /// Width of the entire image. + private static void DrawPlus(Span data, int viewX, int viewY, int imgScale, int imgWidth) { - var x0y0 = (w * y0) + x0; - var s2 = scale / 2; - var ws2 = w * s2; + var x0y0 = (imgWidth * viewY) + viewX; + var s2 = imgScale / 2; + var ws2 = imgWidth * s2; var v0 = x0y0 + s2; var h0 = x0y0 + ws2; - for (int x = scale / 4; x <= 3 * (scale / 4); x++) + for (int x = imgScale / 4; x <= 3 * (imgScale / 4); x++) { - var vert = v0 + (w * x); + var vert = v0 + (imgWidth * x); data[vert] ^= 0x808080; var hori = h0 + x; data[hori] ^= 0x808080; } } - private static void DrawX(Span data, int x0, int y0, int scale, int w) + /// + /// Updates the pixel data to draw an `X`, indicating the item as "buried". + /// + /// Pixel data of the entire image. + /// Top-left X coordinate to start drawing from. + /// Top-left Y coordinate to start drawing from. + /// Scaling factor from 1px => final image dimensions. + /// Width of the entire image. + private static void DrawX(Span data, int viewX, int viewY, int imgScale, int imgWidth) { - var opposite = scale - 1; - var wo = w * opposite; + var opposite = imgScale - 1; + var wo = imgWidth * opposite; // Starting offsets for each of the slashes - var bBackward = (w * y0) + x0; // Backwards \ + var bBackward = (imgWidth * viewY) + viewX; // Backwards \ var bForward = bBackward + wo; // Forwards / - for (int x = 0; x < scale; x++) + for (int x = 0; x < imgScale; x++) { - var wx = w * x; + var wx = imgWidth * x; var backward = bBackward + x + wx; data[backward] ^= 0x808080; var forward = bForward + x - wx; @@ -188,32 +318,51 @@ private static void DrawX(Span data, int x0, int y0, int scale, int w) } } - private static void DrawDirectional(Span data, Item tile, int x0, int y0, int scale, int w) + /// + /// Updates the pixel data to draw a directional, indicating the item as an extension of a root item node. + /// + /// Pixel data of the entire image. + /// Extension tile data. + /// Top-left X coordinate to start drawing from. + /// Top-left Y coordinate to start drawing from. + /// Scaling factor from 1px => final image dimensions. + /// Width of the entire image. + private static void DrawDirectional(Span data, Item tile, int viewX, int viewY, int imgScale, int imgWidth) { var eX = tile.ExtensionX; var eY = tile.ExtensionY; if (eX == 0 && eY == 0) return; var sum = eX + eY; - var start = scale / (sum + 1); + var start = imgScale / (sum + 1); var startX = eX >= eY ? 0 : start; var startY = eX <= eY ? 0 : start; - var baseIndex = (w * y0) + x0; - for (int x = startX, y = startY; x < scale && y < scale; x += eX, y += eY) + var baseIndex = (imgWidth * viewY) + viewX; + for (int x = startX, y = startY; x < imgScale && y < imgScale; x += eX, y += eY) { - var index = baseIndex + (w * y) + x; - data[index] ^= 0x808080; + var index = baseIndex + (imgWidth * y) + x; + if (index >= data.Length) // Since we can't guarantee valid extension values, just skip bad ones. + continue; + data[index] ^= 0x00_808080; } } - public static void DrawGrid(Span data, int w, int h, int scale, int gridlineColor) + /// + /// Draws gridlines on the provided pixel data at the specified scale. + /// + /// Pixel data of the entire image. + /// Width of the entire image. + /// Height of the entire image. + /// Color to use for gridlines. + /// Pixel interval to draw gridlines. + public static void DrawGrid(Span data, int imgWidth, int imgHeight, int gridlineColor, int gridlineInterval) { // Horizontal Lines - for (int y = scale; y < h; y += scale) + for (int y = gridlineInterval; y < imgHeight; y += gridlineInterval) { - var baseIndex = y * w; - for (int x = 0; x < w; x++) + var baseIndex = y * imgWidth; + for (int x = 0; x < imgWidth; x++) { var index = baseIndex + x; data[index] = gridlineColor; @@ -221,10 +370,10 @@ public static void DrawGrid(Span data, int w, int h, int scale, int gridlin } // Vertical Lines - for (int y = 0; y < h; y++) + for (int y = 0; y < imgHeight; y++) { - var baseIndex = y * w; - for (int x = scale; x < w; x += scale) + var baseIndex = y * imgWidth; + for (int x = gridlineInterval; x < imgWidth; x += gridlineInterval) { var index = baseIndex + x; data[index] = gridlineColor; @@ -232,23 +381,33 @@ public static void DrawGrid(Span 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) + /// + /// Loads an item layer into a viewport bitmap. + /// + /// Configuration for layer positioning. + /// Item layer to draw from. + /// Pixel data of the final image. + /// Optional transparency override color. + public static void LoadItemLayer1(LayerPositionConfig cfg, LayerItem layer, Span data, int transparency = -1) { - LoadBitmapLayer(layer.Tiles, data, layer.MaxWidth, layer.MaxHeight); + LoadBitmapLayer(layer.Tiles, data, cfg); if (transparency >>> 24 != 0xFF) ImageUtil.ClampAllTransparencyTo(data, transparency); - ImageUtil.SetBitmapData(dest, data); - return DrawViewReticle(dest, layer, x, y); } - private static Bitmap DrawViewReticle(Bitmap map, TileGrid g, int x, int y, int scale = 1) + /// + /// Draws a square reticle on the specified map image to indicate the current viewport area. + /// + /// The bitmap image on which to draw the reticle. + /// The viewport describing the area of the map present within the viewport. + /// The absolute X-coordinate, in tile units, of the top-left corner of the viewport. + /// The absolute Y-coordinate, in tile units, of the top-left corner of the viewport. + /// The image upscale scale factor to apply to the reticle's size and position. Must be a positive integer. The default is 1. + public static void DrawViewReticle(Bitmap map, TileGridViewport g, int absX, int absY, 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; - gfx.DrawRectangle(pen, x * scale, y * scale, w, h); - return map; + var reticleWidth = g.ViewWidth * scale; + var reticleHeight = g.ViewHeight * scale; + gfx.DrawRectangle(Reticle, absX * scale, absY * scale, reticleWidth, reticleHeight); } } \ No newline at end of file diff --git a/NHSE.Sprites/Field/MapRenderer.cs b/NHSE.Sprites/Field/MapRenderer.cs new file mode 100644 index 0000000..7bf8ace --- /dev/null +++ b/NHSE.Sprites/Field/MapRenderer.cs @@ -0,0 +1,146 @@ +using NHSE.Core; +using System; +using System.Drawing; + +namespace NHSE.Sprites; + +/// +/// Produces bitmaps for viewing map acres and full maps. +/// +public sealed class MapRenderer : IDisposable +{ + /// + /// Data source for map rendering. + /// + private readonly MapEditor Map; + + /// Scale factor for full map rendering. + private int MapScale => Map.MapScale; + + /// Scale factor for acre viewport rendering. + private int ViewScale => Map.ViewScale; + + // Cached acre view objects to remove allocation/GC + + /// 1px scale viewport item layer pixel data. + private readonly int[] ViewportItems1; + /// Upscaled viewport item layer pixel data. + private readonly int[] ViewportItemsX; + /// Upscaled viewport item layer image. + private readonly Bitmap ViewportItemsImage; + + /// Upscaled map item layer pixel data with reticle. + private readonly int[] MapItemsReticleX; + /// Upscaled map item layer image with reticle. + private readonly Bitmap MapItemsReticleImage; + + /// 1px scale viewport terrain layer pixel data. + private readonly int[] ViewportTerrain1; + /// Upscaled viewport terrain layer pixel data. + private readonly int[] ViewportTerrainX; + /// Upscaled viewport terrain layer image. + private readonly Bitmap ViewportTerrainImage; + + /// 1px scale map terrain layer pixel data. + private readonly int[] MapTerrain1; + /// Upscaled map terrain layer pixel data. + private readonly int[] MapTerrainX; + /// Upscaled map terrain layer image. + private readonly Bitmap MapTerrainImage; + + public MapRenderer(MapEditor m) + { + Map = m; + + // Initialize cached objects based on map size + // Get tile info from item layer, it's the tiniest cell we can render + var cfg = m.Mutator.Manager.ConfigItems; + var mapW = cfg.MapTotalWidth * MapScale; + var mapH = cfg.MapTotalHeight * MapScale; + MapItemsReticleImage = new Bitmap(mapW, mapH); + MapItemsReticleX = new int[mapW * mapH]; + + MapTerrain1 = new int[MapItemsReticleX.Length / (2 * 2)]; // 32px => 16px basis + MapTerrainX = new int[MapItemsReticleX.Length]; // 2x upscale + MapTerrainImage = new Bitmap(MapItemsReticleImage.Width, MapItemsReticleImage.Height); + MapTerrain1.AsSpan().Fill(TerrainSprite.ColorOcean); // blue color for ocean + + // Render a single acre viewport + var tpa = cfg.TilesPerAcre; + ViewportItems1 = new int[tpa * tpa]; + ViewportItemsX = new int[ViewportItems1.Length * ViewScale * ViewScale]; + ViewportItemsImage = new Bitmap(tpa * ViewScale, tpa * ViewScale); + + const byte pixelsPerTerrainTile = 16; + var dimTerrain = cfg.TilesPerAcre * pixelsPerTerrainTile; + ViewportTerrain1 = new int[dimTerrain * dimTerrain]; // each terrain tile is drawn as 16px, then we upscale + ViewportTerrainX = new int[ViewportItemsX.Length]; // 2x upscale (16px -> 32px) + ViewportTerrainImage = new Bitmap(ViewportItemsImage.Width, ViewportItemsImage.Height); + ViewportTerrain1.AsSpan().Fill(TerrainSprite.ColorOcean); // blue color for ocean + } + + public void Dispose() + { + MapItemsReticleImage.Dispose(); + MapTerrainImage.Dispose(); + + ViewportItemsImage.Dispose(); + ViewportTerrainImage.Dispose(); + } + + /// + /// Updates the map items reticle bitmap for the current layer and view position. + /// + /// Transparency of items (not including reticle). + /// Option to draw the reticle. + /// Updated bitmap with reticle. + public Bitmap UpdateMapItemsReticle(int transparency, bool drawReticle = true) + => UpdateMapItemsReticle(Map.Mutator.CurrentLayer, Map.Mutator.View.X, Map.Mutator.View.Y, transparency, drawReticle); + + /// + /// Updates the map terrain bitmap. + /// + /// Index of building to highlight, or -1 for none. + /// Updated map terrain bitmap. + public Bitmap UpdateMapTerrain(int selectedBuildingIndex = -1) + => TerrainSprite.GetMapWithBuildings(MapTerrainImage, Map, MapTerrain1, MapTerrainX, selectedBuildingIndex); + + /// + /// Updates the viewport items bitmap for the current layer and view position. + /// + /// Transparency of items. + public Bitmap UpdateViewportItems(int transparency) + => UpdateViewportItems(Map.Mutator.View.X, Map.Mutator.View.Y, transparency); + + /// + /// Updates the viewport terrain bitmap. + /// + /// Font to use for building labels. + /// Transparency for building shapes drawn on terrain. + /// Transparency for terrain layer. + /// Index of building to highlight, or -1 for none. + /// + public Bitmap UpdateViewportTerrain(Font f, byte transparencyBuilding, byte transparencyTerrain, int selectedBuildingIndex = -1) + { + TerrainSprite.LoadViewport(ViewportTerrainImage, Map, f, ViewportTerrain1, ViewportTerrainX, selectedBuildingIndex, transparencyBuilding, transparencyTerrain); + return ViewportTerrainImage; + } + + private Bitmap UpdateMapItemsReticle(LayerFieldItem layer, int absX, int absY, int transparency, bool drawReticle = true) + { + var cfg = Map.Mutator.Manager.ConfigItems; + ItemLayerSprite.LoadItemLayer1(cfg, layer, MapItemsReticleX, transparency); + MapItemsReticleImage.SetBitmapData(MapItemsReticleX); + if (drawReticle) + ItemLayerSprite.DrawViewReticle(MapItemsReticleImage, layer.TileInfo, absX, absY); + return MapItemsReticleImage; + } + + private Bitmap UpdateViewportItems(int absX, int absY, int transparency) + { + var cfg = Map.Mutator.Manager.ConfigItems; + var layer = Map.Mutator.CurrentLayer; + ItemLayerSprite.LoadViewport(ViewportItemsImage, layer, cfg, absX, absY, ViewportItems1, ViewportItemsX, ViewScale, transparency); + return ViewportItemsImage; + } +} \ No newline at end of file diff --git a/NHSE.Sprites/Field/MapViewer.cs b/NHSE.Sprites/Field/MapViewer.cs deleted file mode 100644 index 6d38699..0000000 --- a/NHSE.Sprites/Field/MapViewer.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Drawing; -using NHSE.Core; - -namespace NHSE.Sprites; - -public sealed class MapViewer : MapView, IDisposable -{ - // Cached acre view objects to remove allocation/GC - private readonly int[] PixelsItemAcre1; - private readonly int[] PixelsItemAcreX; - private readonly Bitmap ScaleAcre; - private readonly int[] PixelsItemMap; - private readonly Bitmap MapReticle; - - private readonly int[] PixelsBackgroundAcre1; - private readonly int[] PixelsBackgroundAcreX; - private readonly Bitmap BackgroundAcre; - private readonly int[] PixelsBackgroundMap1; - private readonly int[] PixelsBackgroundMapX; - private readonly Bitmap BackgroundMap; - - public MapViewer(MapManager m, int scale) : base(m, scale) - { - var l1 = m.Items.Layer1; - PixelsItemAcre1 = new int[l1.GridWidth * l1.GridHeight]; - PixelsItemAcreX = new int[PixelsItemAcre1.Length * AcreScale * AcreScale]; - ScaleAcre = new Bitmap(l1.GridWidth * AcreScale, l1.GridHeight * AcreScale); - - PixelsItemMap = new int[l1.MaxWidth * l1.MaxHeight * MapScale * MapScale]; - MapReticle = new Bitmap(l1.MaxWidth * MapScale, l1.MaxHeight * MapScale); - - PixelsBackgroundAcre1 = new int[(int)Math.Pow(16, 4)]; - PixelsBackgroundAcreX = new int[PixelsItemAcreX.Length]; - BackgroundAcre = new Bitmap(ScaleAcre.Width, ScaleAcre.Height); - - PixelsBackgroundMap1 = new int[PixelsItemMap.Length / 4]; - PixelsBackgroundMapX = new int[PixelsItemMap.Length]; - BackgroundMap = new Bitmap(MapReticle.Width, MapReticle.Height); - } - - public void Dispose() - { - ScaleAcre.Dispose(); - MapReticle.Dispose(); - BackgroundAcre.Dispose(); - BackgroundMap.Dispose(); - } - - public Bitmap GetLayerAcre(int t) => GetLayerAcre(X, Y, t); - public Bitmap GetMapWithReticle(int t) => GetMapWithReticle(X, Y, t, Map.CurrentLayer); - - public Bitmap GetBackgroundTerrain(int index = -1) - { - return TerrainSprite.GetMapWithBuildings(Map, null, PixelsBackgroundMap1, PixelsBackgroundMapX, BackgroundMap, 2, index); - } - - private Bitmap GetLayerAcre(int topX, int topY, int t) - { - var layer = Map.CurrentLayer; - return ItemLayerSprite.GetBitmapItemLayerViewGrid(layer, topX, topY, AcreScale, PixelsItemAcre1, PixelsItemAcreX, ScaleAcre, t); - } - - public Bitmap GetBackgroundAcre(Font f, byte tbuild, byte tterrain, int index = -1) - { - return TerrainSprite.GetAcre(this, f, PixelsBackgroundAcre1, PixelsBackgroundAcreX, BackgroundAcre, index, tbuild, tterrain); - } - - private Bitmap GetMapWithReticle(int topX, int topY, int t, FieldItemLayer layer) - { - return ItemLayerSprite.GetBitmapItemLayer(layer, topX, topY, PixelsItemMap, MapReticle, t); - } -} \ No newline at end of file diff --git a/NHSE.Sprites/Field/TerrainSprite.cs b/NHSE.Sprites/Field/TerrainSprite.cs index 077cfd8..81cd247 100644 --- a/NHSE.Sprites/Field/TerrainSprite.cs +++ b/NHSE.Sprites/Field/TerrainSprite.cs @@ -5,6 +5,9 @@ namespace NHSE.Sprites; +/// +/// Logic to build a viewport render of a subsection of a map (or the entire "map", assuming it is small enough). +/// public static class TerrainSprite { private static readonly Brush Selected = Brushes.Red; @@ -12,207 +15,319 @@ public static class TerrainSprite private static readonly Brush Text = Brushes.White; private static readonly Brush Tile = Brushes.Black; private static readonly Brush Plaza = Brushes.RosyBrown; + private static readonly Color PlazaColor = Color.RosyBrown; private static readonly StringFormat BuildingTextFormat = new() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }; - private const int PlazaWidth = 6 * 2; - private const int PlazaHeight = 5 * 2; + public const int ColorOcean = unchecked((int)0xFF80D7C3); + private const int ColorGrid1 = unchecked((int)0xFF888888u); // lighter + private const int ColorGrid2 = unchecked((int)0xFF666666u); // darker - public static void CreateMap(TerrainLayer mgr, Span pixels) + // 6x5 in a 16x16 acre scale. Since we are in 32x32 scale for items, our upscale is "double". + // Tiles are always rendered as 16x16 squares, to match the precomputed tile appearance bitmaps. + private const int TileScale = 16; + private const int TilesPerViewport = 16; + + // To display in a 32x32 viewport from 16x16 + private const int Scale = 2; + private const int PlazaWidth = 6 * Scale; + private const int PlazaHeight = 5 * Scale; + + /// + /// Generates a terrain map by loading, scaling, and applying terrain data to the specified bitmap. + /// + /// The bitmap to which the generated terrain map will be applied. + /// The map information manager that provides access to terrain configuration and management. + /// A span of integers used as a buffer for the initial terrain pixel data. + /// A span of integers used as a buffer for the upscaled terrain pixel data. + /// The scaling factor to apply when upscaling the terrain image. Must be a positive integer. + private static void GenerateMapTerrainAndUpscale(Bitmap map, MapMutator mut, Span scale1, Span scaleX, int imgScale) { - int i = 0; - for (int y = 0; y < mgr.MaxHeight; y++) + // Load the terrain pixels, then upscale. + var mgr = mut.Manager.LayerTerrain; + LoadTerrainPixels(mgr, mut.Manager.ConfigTerrain, scale1); + ImageUtil.ScalePixelImage(scale1, scaleX, map.Width, map.Height, imgScale); + map.SetBitmapData(scaleX); + } + + /// + /// Draws the map with all buildings and the plaza overlay onto the specified bitmap, using the provided map editor + /// and scaling information. + /// + /// + /// The method modifies the provided bitmap in place. + /// The scaling spans must be properly initialized to match the expected map dimensions. + /// If a specific building index is provided, only that building may be highlighted or rendered differently; + /// otherwise, all buildings are drawn normally. + /// + /// The bitmap on which the map, buildings, and plaza will be rendered. + /// The map editor instance containing map data, building information, and scaling parameters. + /// A span representing the primary scaling factors for rendering the map. + /// A span representing the secondary scaling factors for rendering the map. + /// + /// The index of a specific building to highlight or focus on. + /// Set to -1 to render all buildings without highlighting any particular one. + /// + /// The bitmap with the map, plaza, and buildings drawn onto it. The same instance as the input bitmap is returned. + public static Bitmap GetMapWithBuildings(Bitmap map, MapEditor m, Span scale1, Span scaleX, int buildingIndex = -1) + { + var imgScale = m.MapScale * 2; // because terrain is 16px per tile, items are 32px per tile + GenerateMapTerrainAndUpscale(map, m.Mutator, scale1, scaleX, imgScale); + using var gfx = Graphics.FromImage(map); + + var plaza = m.Mutator.Manager.Plaza; + gfx.DrawMapPlaza(m, (ushort)plaza.X, (ushort)plaza.Z, imgScale); + gfx.DrawMapBuildings(m, m.Buildings.Buildings, imgScale, buildingIndex); + return map; + } + + /// + /// Renders the current map viewport onto the specified bitmap, including terrain, buildings, grid overlays, and labels. + /// + /// + /// This method draws both graphical and textual elements of the map viewport, including overlays and labels. + /// It should be called whenever the viewport needs to be refreshed, such as after map edits or navigation. + /// The method modifies the provided bitmap in place. + /// + /// The bitmap onto which the viewport will be drawn. + /// The map editor instance providing map data, building information, and viewport configuration. + /// The font used to render building and terrain tile names within the viewport. + /// A span representing the primary scaling factors for rendering terrain pixels. + /// A span used for horizontal scaling and pixel data manipulation during rendering. + /// + /// The index of the currently selected building. + /// Used to highlight or annotate the selected building in the viewport. + /// + /// + /// The transparency level to apply when rendering buildings. + /// A value of 0xFF is fully opaque; lower values increase transparency. + /// + /// + /// The transparency level to apply when rendering terrain tile names. + /// A value of 0xFF is fully opaque; lower values increase transparency. + /// + public static void LoadViewport(Bitmap img, MapEditor m, Font f, + Span scale1, Span scaleX, + int selectedBuildingIndex, byte transparencyBuilding, byte transTerrain) + { + // Convert from absolute to relative. + var cfg = m.Mutator.Manager.ConfigTerrain; + int absX = m.X / 2; // 32px => 16px basis + int absY = m.Y / 2; // 32px => 16px basis + var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY); + + SetViewTerrainPixels(m.Terrain, cfg, relX, relY, scale1, scaleX, Scale); + + // Drawing building tiles currently uses the graphics API rather than writing pixels. + img.SetBitmapData(scaleX); + using var gfx = Graphics.FromImage(img); + gfx.DrawViewPlaza(m, transparencyBuilding); + gfx.DrawViewBuildings(m, selectedBuildingIndex, transparencyBuilding); + + // Return to pixel writing mode + img.GetBitmapData(scaleX); + + // Apply Grid + ItemLayerSprite.DrawGrid(scaleX, img.Width, img.Height, ColorGrid1, m.ViewScale); // minor + ItemLayerSprite.DrawGrid(scaleX, img.Width, img.Height, ColorGrid2, m.ViewScale * 2); // major + + // Switch back to graphics mode + img.SetBitmapData(scaleX); + // Draw Text of Building Names + foreach (var b in m.Buildings.Buildings) { - for (int x = 0; x < mgr.MaxWidth; x++, i++) + var (x, y) = m.GetViewCoordinatesBuilding(b.X, b.Y); + const int cellsAbove = 2; // Show label above the building, 2 cells up. + y -= (m.ViewScale * cellsAbove); + + // Don't bother drawing if not in view. + if (!m.Mutator.View.IsWithinView(m.ViewScale, x, y)) + continue; + + var type = b.BuildingType; + var name = type.ToString(); + var labelPosition = new PointF(x, y - (m.ViewScale * 2)); + gfx.DrawString(name, f, Text, labelPosition, BuildingTextFormat); + } + + // Draw Text of Terrain Tile Names + if (transTerrain != 0) + gfx.DrawViewTerrainTileNames(m.Terrain, cfg, f, relX, relY, m.ViewScale * 2, transTerrain); + + // Done. + } + + private static void DrawViewBuildings(this Graphics gfx, MapEditor m, int selectedBuildingIndex, byte transBuild) + { + var buildings = m.Buildings.Buildings; + for (var i = 0; i < buildings.Count; i++) + { + var b = buildings[i]; + if (b.BuildingType == BuildingType.None) + continue; + var pen = selectedBuildingIndex == i ? Selected : Others; + if (transBuild != byte.MaxValue) { - pixels[i] = mgr.GetTileColor(x, y, x, y); + var orig = ((SolidBrush)pen).Color; + pen = new SolidBrush(Color.FromArgb(transBuild, orig)); + } + gfx.DrawViewBuilding(m, b, pen, m.ViewScale, Text); + } + } + + private static void LoadTerrainPixels(LayerTerrain mgr, LayerPositionConfig cfg, Span pixels) + { + var (shiftX, shiftY) = cfg.GetCoordinatesAbsolute(); + + // Iterate through the relative positions within the layer. + // Then, map to absolute positions in the bitmap with the configured shift. + var width = cfg.LayerTotalWidth; + var height = cfg.LayerTotalHeight; + var mapWidth = cfg.MapTotalWidth; // 1px scale + + // Populate the image, with each pixel being a single tile. + // Only need to render the layer's width/height, as the rest is ocean/unable to be changed. + for (int y = 0; y < height; y++) + { + var absY = y + shiftY; + for (int x = 0; x < width; x++) + { + var absX = x + shiftX; + var color = mgr.GetTileColor(x, y, 0, 0); + var offset = (absY * mapWidth) + absX; + pixels[offset] = color; } } } - public static Bitmap CreateMap(TerrainLayer mgr, Span scale1, int[] scaleX, Bitmap map, int scale, int acreIndex = -1) + private static void DrawMapPlaza(this Graphics gfx, MapEditor map, ushort px, ushort py, int imgScale) { - CreateMap(mgr, scale1); - ImageUtil.ScalePixelImage(scale1, scaleX, map.Width, map.Height, scale); - ImageUtil.SetBitmapData(map, scaleX); + var (x, y) = (px, py); - if (acreIndex < 0) - return map; - - var acre = MapGrid.Acres[acreIndex]; - var x = acre.X * mgr.GridWidth; - var y = acre.Y * mgr.GridHeight; - - return DrawReticle(map, mgr, x, y, scale); - } - - private static Bitmap DrawReticle(Bitmap map, TileGrid 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; - gfx.DrawRectangle(pen, x * scale, y * scale, w, h); - return map; - } - - public static Bitmap GetMapWithBuildings(MapTerrainStructure m, Font? f, Span scale1, int[] scaleX, Bitmap map, int scale = 4, int index = -1) - { - CreateMap(m.Terrain, scale1, scaleX, map, scale); - using var gfx = Graphics.FromImage(map); - - gfx.DrawPlaza(m.Terrain, (ushort)m.PlazaX, (ushort)m.PlazaY, scale); - gfx.DrawBuildings(m.Terrain, m.Buildings, f, scale, index); - return map; - } - - private static void DrawPlaza(this Graphics gfx, TerrainLayer g, ushort px, ushort py, int scale) - { - g.GetBuildingCoordinate(px, py, scale, out var x, out var y); - - var width = scale * PlazaWidth; - var height = scale * PlazaHeight; + int width = imgScale * PlazaWidth; + int height = imgScale * PlazaHeight; gfx.FillRectangle(Plaza, x, y, width, height); } - private static void DrawBuildings(this Graphics gfx, TerrainLayer g, IReadOnlyList buildings, Font? f, int scale, int index = -1) + private static void DrawMapBuildings(this Graphics gfx, MapEditor m, IReadOnlyList buildings, int imgScale, int selectedBuildingIndex = -1) { for (int i = 0; i < buildings.Count; i++) { var b = buildings[i]; if (b.BuildingType == 0) continue; - g.GetBuildingCoordinate(b.X, b.Y, scale, out var x, out var y); - var pen = index == i ? Selected : Others; - DrawBuilding(gfx, f, scale, pen, x, y, b, Text); + var (width, height) = b.BuildingType.GetDimensions(); + var pen = selectedBuildingIndex == i ? Selected : Others; + gfx.FillRectangle(pen, b.X, b.Y, imgScale * width, imgScale * height); } } - private static void DrawBuilding(Graphics gfx, Font? f, int scale, Brush pen, int x, int y, Building b, Brush text) + private static void DrawViewBuilding(this Graphics gfx, MapEditor m, Building b, Brush bBrush, + int imgScale, Brush textBrush, Font? textFont = null) { - gfx.FillRectangle(pen, x - scale, y - scale, scale * 2, scale * 2); + var (x, y) = m.GetViewCoordinatesBuilding(b.X, b.Y); + var type = b.BuildingType; + var (width, height) = type.GetDimensions(); + x -= (width / 2) * m.ViewScale * 2; + y -= (height / 2) * m.ViewScale * 2; - if (f == null) + // Draw the building. + gfx.FillRectangle(bBrush, x, y, width * imgScale * 2, height * imgScale * 2); + + if (textFont == null) return; - var name = b.BuildingType.ToString(); - gfx.DrawString(name, f, text, new PointF(x, y - (scale * 2)), BuildingTextFormat); + // Draw the text label above it. + const int cellsAbove = 2; + var name = type.ToString(); + gfx.DrawString(name, textFont, textBrush, new PointF(x, y - (imgScale * cellsAbove)), BuildingTextFormat); } - private static void SetAcreTerrainPixels(int x, int y, TerrainLayer t, Span data, Span scaleX, int scale) + private static void SetViewTerrainPixels(LayerTerrain t, LayerPositionConfig cfg, int relX, int relY, Span data, Span scaleX, int imgScale) { - GetAcre1(x, y, t, data); - ImageUtil.ScalePixelImage(data, scaleX, 16 * scale, 16 * scale, scale / 16); + GetViewTerrain1(t, cfg, relX, relY, data); + ImageUtil.ScalePixelImage(data, scaleX, TilesPerViewport * TileScale * imgScale, TilesPerViewport * TileScale * imgScale, imgScale); } - private static void GetAcre1(int tileTopX, int tileTopY, TerrainLayer t, Span data) + private static void GetViewTerrain1(LayerTerrain t, LayerPositionConfig cfg, int relX, int relY, Span data) { - int index = 0; - - for (int tileY = 0; tileY < 16; tileY++) + for (int tileY = 0; tileY < TilesPerViewport; tileY++) { - var tileYIx = tileY + tileTopY; - for (int pixelY = 0; pixelY < 16; pixelY++) + var actY = tileY + relY; + for (int x = 0; x < TilesPerViewport; x++) { - for (int tileX = 0; tileX < 16; tileX++) + var actX = relX + x; + if (!cfg.IsCoordinateValidRelative(actX, actY)) { - var tileXIx = tileX + tileTopX; - for (int pixelX = 0; pixelX < 16; pixelX++) + // Fill tile's square with a solid color. + for (int pixelY = 0; pixelY < TileScale; pixelY++) { - data[index] = t.GetTileColor(tileXIx, tileYIx, pixelX, pixelY); - index++; + var index = (tileY * TileScale + pixelY) * (TilesPerViewport * TileScale) + x * TileScale; + for (int pixelX = 0; pixelX < TileScale; pixelX++) + { + data[index] = ColorOcean; + index++; + } + } + } + else + { + // Fill tile's square from terrain data. + var acreTemplate = t.GetAcreTemplate(actX, actY); + var tile = t.GetTile(actX, actY); + for (int pixelY = 0; pixelY < TileScale; pixelY++) + { + var index = (tileY * TileScale + pixelY) * (TilesPerViewport * TileScale) + x * TileScale; + for (int pixelX = 0; pixelX < TileScale; pixelX++) + { + data[index] = t.GetTileColor(acreTemplate, tile, actX, actY, pixelX, pixelY); + index++; + } } } } } } - public static Bitmap GetAcre(MapView m, Font f, Span scale1, int[] scaleX, Bitmap acre, int index, byte tbuild, byte tterrain) + private static void DrawViewTerrainTileNames(this Graphics gfx, LayerTerrain t, LayerPositionConfig cfg, Font f, + int relX, int relY, int scale, byte transparency) { - int mx = m.X / 2; - int my = m.Y / 2; - SetAcreTerrainPixels(mx, my, m.Map.Terrain, scale1, scaleX, m.TerrainScale); + var pen = Tile; + if (transparency != byte.MaxValue) + pen = new SolidBrush(Color.FromArgb(transparency, Color.Black)); - const int grid1 = unchecked((int)0xFF888888u); - const int grid2 = unchecked((int)0xFF666666u); - ImageUtil.SetBitmapData(acre, scaleX); - - using var gfx = Graphics.FromImage(acre); - - gfx.DrawAcrePlaza(m.Map.Terrain, mx, my, (ushort)m.Map.PlazaX, (ushort)m.Map.PlazaY, m.TerrainScale, tbuild); - - var buildings = m.Map.Buildings; - var t = m.Map.Terrain; - for (var i = 0; i < buildings.Count; i++) + // iterate over every tile in the view + for (int y = 0; y < TilesPerViewport; y++) { - var b = buildings[i]; - t.GetBuildingRelativeCoordinates(mx, my, m.TerrainScale, b.X, b.Y, out var x, out var y); - - var pen = index == i ? Selected : Others; - if (tbuild != byte.MaxValue) + var actY = relY + y; + var centerY = (y * scale) + (scale / 2); + for (int x = 0; x < TilesPerViewport; x++) { - var orig = ((SolidBrush)pen).Color; - pen = new SolidBrush(Color.FromArgb(tbuild, orig)); - } + var actX = relX + x; + if (!cfg.IsCoordinateValidRelative(actX, actY)) + continue; - DrawBuilding(gfx, null, m.TerrainScale, pen, x, y, b, Text); - } - - ImageUtil.GetBitmapData(acre, scaleX); - ItemLayerSprite.DrawGrid(scaleX, acre.Width, acre.Height, m.AcreScale, grid1); - ItemLayerSprite.DrawGrid(scaleX, acre.Width, acre.Height, m.TerrainScale, grid2); - ImageUtil.SetBitmapData(acre, scaleX); - - foreach (var b in buildings) - { - t.GetBuildingRelativeCoordinates(mx, my, m.TerrainScale, b.X, b.Y, out var x, out var y); - if (!t.IsWithinGrid(m.TerrainScale, x, y)) - continue; - var name = b.BuildingType.ToString(); - gfx.DrawString(name, f, Text, new PointF(x, y - (m.TerrainScale * 2)), BuildingTextFormat); - } - - if (tterrain != 0) - DrawTerrainTileNames(mx, my, gfx, t, f, m.TerrainScale, tterrain); - - return acre; - } - - private static void DrawTerrainTileNames(int topX, int topY, Graphics gfx, TerrainLayer t, Font f, int scale, byte transparency) - { - var pen = transparency != byte.MaxValue ? new SolidBrush(Color.FromArgb(transparency, Color.Black)) : Tile; - - for (int y = 0; y < 16; y++) - { - var yi = y + topY; - int cy = (y * scale) + (scale / 2); - for (int x = 0; x < 16; x++) - { - var xi = x + topX; - var tile = t.GetTile(xi, yi); - - int cx = (x * scale) + (scale / 2); + var tile = t.GetTile(actX, actY); + var centerX = (x * scale) + (scale / 2); var name = TerrainTileColor.GetTileName(tile); - gfx.DrawString(name, f, pen, new PointF(cx, cy), BuildingTextFormat); + gfx.DrawString(name, f, pen, centerX, centerY, BuildingTextFormat); } } } - private static void DrawAcrePlaza(this Graphics gfx, TerrainLayer g, int topX, int topY, ushort px, ushort py, int scale, byte transparency) + private static void DrawViewPlaza(this Graphics gfx, MapEditor m, byte transparency) { - g.GetBuildingRelativeCoordinates(topX, topY, scale, px, py, out var x, out var y); + var plaza = m.Mutator.Manager.Plaza; + var (x, y) = m.GetViewCoordinatesBuilding(plaza.X, plaza.Z); + var scale = m.ViewScale * 2; var width = scale * PlazaWidth; var height = scale * PlazaHeight; var pen = Plaza; if (transparency != byte.MaxValue) - { - var orig = ((SolidBrush)pen).Color; - pen = new SolidBrush(Color.FromArgb(transparency, orig)); - } + pen = new SolidBrush(Color.FromArgb(transparency, PlazaColor)); gfx.FillRectangle(pen, x, y, width, height); } } \ No newline at end of file diff --git a/NHSE.Sprites/Util/ImageUtil.cs b/NHSE.Sprites/Util/ImageUtil.cs index a4e263f..879ac87 100644 --- a/NHSE.Sprites/Util/ImageUtil.cs +++ b/NHSE.Sprites/Util/ImageUtil.cs @@ -24,39 +24,69 @@ public static class ImageUtil public Bitmap GetPalette() => GetBitmap(bg.GetPaletteBitmap(), DesignPatternPRO.PaletteColorCount, 1, PixelFormat.Format24bppRgb); } + extension(Bitmap bmp) + { + public Span GetBitmapData(out BitmapData bmpData, PixelFormat format = PixelFormat.Format32bppArgb, byte bpp = 4) + { + bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, format); + return GetSpan(bmpData.Scan0, bmp.Width * bmp.Height * bpp); + } + + public void GetBitmapData(Span data, PixelFormat format = PixelFormat.Format32bppArgb, byte bpp = 4) + { + var span = bmp.GetBitmapData(out var bmpData, format, bpp); + span.CopyTo(data); + bmp.UnlockBits(bmpData); + } + + public void GetBitmapData(Span data, PixelFormat format = PixelFormat.Format32bppArgb, byte bpp = 4) + { + var span = bmp.GetBitmapData(out var bmpData, format, bpp); + var src = MemoryMarshal.Cast(span); + src.CopyTo(data); + bmp.UnlockBits(bmpData); + } + + public void SetBitmapData(ReadOnlySpan data, PixelFormat format = PixelFormat.Format32bppArgb, byte bpp = 4) + { + var span = bmp.GetBitmapData(out var bmpData, format, bpp); + data.CopyTo(span); + bmp.UnlockBits(bmpData); + } + + public void SetBitmapData(Span data, PixelFormat format = PixelFormat.Format32bppArgb, byte bpp = 4) + { + var span = bmp.GetBitmapData(out var bmpData, format, bpp); + var dest = MemoryMarshal.Cast(span); + data.CopyTo(dest); + bmp.UnlockBits(bmpData); + } + } + + public static Bitmap GetBitmap(ReadOnlySpan data, int width, int height, PixelFormat format = PixelFormat.Format32bppArgb) + { + var span = MemoryMarshal.Cast(data); + return GetBitmap(span, width, height, format); + } + public static Bitmap GetBitmap(ReadOnlySpan data, int width, int height, PixelFormat format = PixelFormat.Format32bppArgb) { var bmp = new Bitmap(width, height, format); - var bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, format); - var length = data.Length; - var span = MemoryMarshal.CreateSpan(ref Unsafe.AddByteOffset(ref Unsafe.NullRef(), bmpData.Scan0), length); - data[..length].CopyTo(span); + var span = bmp.GetBitmapData(out var bmpData); + data[..span.Length].CopyTo(span); bmp.UnlockBits(bmpData); return bmp; } - public static Bitmap GetBitmap(int[] data, int width, int height, PixelFormat format = PixelFormat.Format32bppArgb) + public static Bitmap GetBitmap(Span data, int width, int height, PixelFormat format = PixelFormat.Format32bppArgb) { var bmp = new Bitmap(width, height, format); - SetBitmapData(bmp, data, format); + bmp.SetBitmapData(data, format); return bmp; } - public static void SetBitmapData(Bitmap bmp, int[] data, PixelFormat format = PixelFormat.Format32bppArgb) - { - var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, format); - var ptr = bmpData.Scan0; - Marshal.Copy(data, 0, ptr, data.Length); - bmp.UnlockBits(bmpData); - } - - public static void GetBitmapData(Bitmap bmp, int[] data, PixelFormat format = PixelFormat.Format32bppArgb) - { - var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, format); - var ptr = bmpData.Scan0; - Marshal.Copy(ptr, data, 0, data.Length); - bmp.UnlockBits(bmpData); - } + private static Span GetSpan(IntPtr ptr, int length) + => MemoryMarshal.CreateSpan(ref Unsafe.AddByteOffset(ref Unsafe.NullRef(), ptr), length); // https://stackoverflow.com/a/24199315 public static Bitmap ResizeImage(Image image, int width, int height) @@ -81,39 +111,37 @@ public static Bitmap ResizeImage(Image image, int width, int height) return destImage; } - public static int[] ScalePixelImage(ReadOnlySpan data, int scale, int w, int h, out int fW, out int fH) + public static int[] ScalePixelImage(ReadOnlySpan data, int imgScale, int imgWidthSingle, int imgHeightSingle, out int imgWidthUpscaled, out int imgHeightUpscaled) { - fW = scale * w; - fH = scale * h; - var scaled = new int[fW * fH]; - - ScalePixelImage(data, scaled, fW, fH, scale); - + imgWidthUpscaled = imgScale * imgWidthSingle; + imgHeightUpscaled = imgScale * imgHeightSingle; + var scaled = new int[imgWidthUpscaled * imgHeightUpscaled]; + ScalePixelImage(data, scaled, imgWidthUpscaled, imgHeightUpscaled, imgScale); return scaled; } - public static void ScalePixelImage(ReadOnlySpan data, Span scaled, int fW, int fH, int scale) + public static void ScalePixelImage(ReadOnlySpan data, Span scaled, int imgWidth, int imgHeight, int imgScale) { // For each pixel, copy to the X indexes, then block copy the row to the other rows. int i = 0; - for (int y = 0; y < fH; y += scale) + for (int y = 0; y < imgHeight; y += imgScale) { // Fill the X pixels - var baseIndex = y * fW; - for (int x = 0; x < fW; x += scale) + var baseIndex = y * imgWidth; + for (int x = 0; x < imgWidth; x += imgScale) { var v = data[i]; var xi = baseIndex + x; - for (int x1 = 0; x1 < scale; x1++) + for (int x1 = 0; x1 < imgScale; x1++) scaled[xi + x1] = v; i++; } // Copy entire pixel row down - for (int y1 = 1; y1 < scale; y1++) + for (int y1 = 1; y1 < imgScale; y1++) { - var src = scaled.Slice(baseIndex, fW); - var dest = scaled.Slice(baseIndex + (y1 * fW), fW); + var src = scaled.Slice(baseIndex, imgWidth); + var dest = scaled.Slice(baseIndex + (y1 * imgWidth), imgWidth); src.CopyTo(dest); } } @@ -127,4 +155,10 @@ public static void ClampAllTransparencyTo(Span data, int trans) for (int i = 0; i < data.Length; i++) data[i] &= trans; } + + public static void ClampAllTransparencyTo(Span data, byte trans) + { + for (int i = 0; i < data.Length; i += 4) + data[i + 3] &= trans; + } } \ No newline at end of file diff --git a/NHSE.Tests/ArrayUtilTests.cs b/NHSE.Tests/ArrayUtilTests.cs index f56588b..9e8c3dc 100644 --- a/NHSE.Tests/ArrayUtilTests.cs +++ b/NHSE.Tests/ArrayUtilTests.cs @@ -6,10 +6,10 @@ namespace NHSE.Tests; -public class ArrayUtilTests +public static class ArrayUtilTests { [Fact] - public void ReplaceOccurrences_WhenPatternNotFound_ReturnsZero() + public static void ReplaceOccurrences_WhenPatternNotFound_ReturnsZero() { byte[] array = [0x01, 0x02, 0x03, 0x04, 0x05]; byte[] pattern = [0xAA, 0xBB]; @@ -22,7 +22,7 @@ public void ReplaceOccurrences_WhenPatternNotFound_ReturnsZero() } [Fact] - public void ReplaceOccurrences_WhenSingleOccurrence_ReplacesAndReturnsOne() + public static void ReplaceOccurrences_WhenSingleOccurrence_ReplacesAndReturnsOne() { byte[] array = [0x01, 0xAA, 0xBB, 0x04, 0x05]; byte[] pattern = [0xAA, 0xBB]; @@ -35,7 +35,7 @@ public void ReplaceOccurrences_WhenSingleOccurrence_ReplacesAndReturnsOne() } [Fact] - public void ReplaceOccurrences_WhenMultipleOccurrences_ReplacesAllAndReturnsCount() + public static void ReplaceOccurrences_WhenMultipleOccurrences_ReplacesAllAndReturnsCount() { byte[] array = [0xAA, 0xBB, 0x03, 0xAA, 0xBB, 0x06, 0xAA, 0xBB]; byte[] pattern = [0xAA, 0xBB]; @@ -48,7 +48,7 @@ public void ReplaceOccurrences_WhenMultipleOccurrences_ReplacesAllAndReturnsCoun } [Fact] - public void ReplaceOccurrences_WhenConsecutiveOccurrences_ReplacesAll() + public static void ReplaceOccurrences_WhenConsecutiveOccurrences_ReplacesAll() { byte[] array = [0xAA, 0xBB, 0xAA, 0xBB, 0xAA, 0xBB]; byte[] pattern = [0xAA, 0xBB]; @@ -61,7 +61,7 @@ public void ReplaceOccurrences_WhenConsecutiveOccurrences_ReplacesAll() } [Fact] - public void ReplaceOccurrences_WhenSwapContainsPattern_DoesNotCauseInfiniteLoop() + public static void ReplaceOccurrences_WhenSwapContainsPattern_DoesNotCauseInfiniteLoop() { // Swap contains the original pattern - must skip past swapped data byte[] array = [0x01, 0xAA, 0xBB, 0x04]; @@ -75,7 +75,7 @@ public void ReplaceOccurrences_WhenSwapContainsPattern_DoesNotCauseInfiniteLoop( } [Fact] - public void ReplaceOccurrences_WhenLargeFileWithRandomPlacements_ReplacesAllOccurrences() + public static void ReplaceOccurrences_WhenLargeFileWithRandomPlacements_ReplacesAllOccurrences() { const int fileSize = 1024 * 1024; // 1 MB const int sequenceLength = 0x13; // 19 bytes diff --git a/NHSE.Tests/BuildingTests.cs b/NHSE.Tests/BuildingTests.cs index 7dd11fc..b6beb3c 100644 --- a/NHSE.Tests/BuildingTests.cs +++ b/NHSE.Tests/BuildingTests.cs @@ -1,14 +1,14 @@ -using System.Linq; +using System.Linq; using FluentAssertions; using NHSE.Core; using Xunit; namespace NHSE.Tests; -public class BuildingTests +public static class BuildingTests { [Fact] - public void BuildingMarshal() + public static void BuildingMarshal() { var building = new Building(); var bytes = building.ToBytesClass(); @@ -16,7 +16,7 @@ public void BuildingMarshal() } [Fact] - public void BuildingClear() + public static void BuildingClear() { var item = new Building {BuildingType = BuildingType.PlayerHouse1, X=5, Y=7}; var bytes = item.ToBytesClass(); diff --git a/NHSE.Tests/EncryptedIntTests.cs b/NHSE.Tests/EncryptedIntTests.cs index 2cb74fa..dd97782 100644 --- a/NHSE.Tests/EncryptedIntTests.cs +++ b/NHSE.Tests/EncryptedIntTests.cs @@ -5,10 +5,10 @@ namespace NHSE.Tests; -public class EncryptedIntTests +public static class EncryptedIntTests { [Fact] - public void TestParse() + public static void TestParse() { const int expect = 31_280; ReadOnlySpan data = [0x8A, 0xC4, 0xE3, 0xCF, 0x37, 0xD5, 0x1A, 0xD3]; diff --git a/NHSE.Tests/EnumHashTests.cs b/NHSE.Tests/EnumHashTests.cs index c5fc527..b552e74 100644 --- a/NHSE.Tests/EnumHashTests.cs +++ b/NHSE.Tests/EnumHashTests.cs @@ -4,13 +4,13 @@ namespace NHSE.Tests; -public class EnumHashTests +public static class EnumHashTests { [Theory] [InlineData("Base", 0x6086515F)] [InlineData("River", 0x3422482F)] [InlineData("RoadStone", 0x13011867)] - public void ChecksumMatches(string str, uint val) + public static void ChecksumMatches(string str, uint val) { var computed = CRC32.Compute(str); computed.Should().Be(val); diff --git a/NHSE.Tests/FancyMarshalTests.cs b/NHSE.Tests/FancyMarshalTests.cs index 68b84d9..2e6d948 100644 --- a/NHSE.Tests/FancyMarshalTests.cs +++ b/NHSE.Tests/FancyMarshalTests.cs @@ -1,10 +1,10 @@ -using FluentAssertions; +using FluentAssertions; using NHSE.Core; using Xunit; namespace NHSE.Tests; -public class FancyMarshalTests +public sealed class FancyMarshalTests { [Fact] public void MarshalGSaveBulletinBoard() => MarshalBytesTestS(GSaveBulletinBoard.SIZE); [Fact] public void MarshalBulletinBoard() => MarshalBytesTestS(BulletinBoardStock.SIZE); diff --git a/NHSE.Tests/MarshalTests.cs b/NHSE.Tests/MarshalTests.cs index 87a2e5d..56bbcfd 100644 --- a/NHSE.Tests/MarshalTests.cs +++ b/NHSE.Tests/MarshalTests.cs @@ -1,10 +1,10 @@ -using FluentAssertions; +using FluentAssertions; using NHSE.Core; using Xunit; namespace NHSE.Tests; -public class MarshalTests +public sealed class MarshalTests { [Fact] public void MarshalItem() => MarshalTest(Item.SIZE); [Fact] public void MarshalVillagerItem() => MarshalTest(VillagerItem.SIZE); diff --git a/NHSE.Villagers/VillagerData.cs b/NHSE.Villagers/VillagerData.cs index cc8007e..428eba8 100644 --- a/NHSE.Villagers/VillagerData.cs +++ b/NHSE.Villagers/VillagerData.cs @@ -2,14 +2,7 @@ namespace NHSE.Villagers; -public class VillagerData -{ - public readonly Memory Villager; - public readonly Memory House; - - public VillagerData(Memory villager, Memory house) - { - Villager = villager; - House = house; - } -} \ No newline at end of file +/// +/// Tuple-like record struct to hold villager and house memory segments. +/// +public readonly record struct VillagerData(Memory Villager, Memory House); \ No newline at end of file diff --git a/NHSE.Villagers/VillagerInfo.cs b/NHSE.Villagers/VillagerInfo.cs index efb7746..3480612 100644 --- a/NHSE.Villagers/VillagerInfo.cs +++ b/NHSE.Villagers/VillagerInfo.cs @@ -2,14 +2,4 @@ namespace NHSE.Villagers; -public class VillagerInfo -{ - public readonly Villager2 Villager; - public readonly IVillagerHouse House; - - public VillagerInfo(Villager2 villager, IVillagerHouse house) - { - Villager = villager; - House = house; - } -} \ No newline at end of file +public readonly record struct VillagerInfo(Villager2 Villager, IVillagerHouse House); \ No newline at end of file diff --git a/NHSE.WinForms/Controls/ItemEditor.cs b/NHSE.WinForms/Controls/ItemEditor.cs index 74d7d7e..2a92be3 100644 --- a/NHSE.WinForms/Controls/ItemEditor.cs +++ b/NHSE.WinForms/Controls/ItemEditor.cs @@ -111,6 +111,8 @@ private Item LoadExtensionItem(Item item) return item; } + public Item LoadFieldsToNewItem() => SetItem(new Item()); + public Item SetItem(Item item) { if (CHK_IsExtension.Checked) diff --git a/NHSE.WinForms/Controls/ItemGridEditor.cs b/NHSE.WinForms/Controls/ItemGridEditor.cs index 695d0a4..8107bbf 100644 --- a/NHSE.WinForms/Controls/ItemGridEditor.cs +++ b/NHSE.WinForms/Controls/ItemGridEditor.cs @@ -11,7 +11,11 @@ namespace NHSE.WinForms; public partial class ItemGridEditor : UserControl { - private static readonly GridSize Sprites = new(); + private static readonly GridSize Sprites = new() + { + Width = 64, + Height = 64, + }; private readonly ItemEditor Editor; private readonly IReadOnlyList Items; @@ -310,9 +314,9 @@ private void SetEditorItems(List items) private void B_ClearFish_Click(object sender, EventArgs e) => ClearItemIf(z => GameLists.Fish.Contains(z.ItemId)); private void B_ClearDive_Click(object sender, EventArgs e) => ClearItemIf(z => GameLists.Dive.Contains(z.ItemId)); - private class GridSize : IGridItem + private sealed record GridSize : IGridItem { - public int Width { get; set; } = 64; - public int Height { get; set; } = 64; + public required int Width { get; set; } + public required int Height { get; set; } } } \ No newline at end of file diff --git a/NHSE.WinForms/Editor.Designer.cs b/NHSE.WinForms/Editor.Designer.cs index 403dc2a..8b6ddc5 100644 --- a/NHSE.WinForms/Editor.Designer.cs +++ b/NHSE.WinForms/Editor.Designer.cs @@ -136,7 +136,7 @@ private void InitializeComponent() Menu_Editor.Location = new System.Drawing.Point(0, 0); Menu_Editor.Name = "Menu_Editor"; Menu_Editor.Padding = new System.Windows.Forms.Padding(7, 2, 0, 2); - Menu_Editor.Size = new System.Drawing.Size(471, 24); + Menu_Editor.Size = new System.Drawing.Size(471, 25); Menu_Editor.TabIndex = 0; Menu_Editor.Text = "menuStrip1"; // @@ -144,14 +144,14 @@ private void InitializeComponent() // Menu_File.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { Menu_Save }); Menu_File.Name = "Menu_File"; - Menu_File.Size = new System.Drawing.Size(37, 20); + Menu_File.Size = new System.Drawing.Size(39, 21); Menu_File.Text = "File"; // // Menu_Save // Menu_Save.Name = "Menu_Save"; Menu_Save.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S; - Menu_Save.Size = new System.Drawing.Size(138, 22); + Menu_Save.Size = new System.Drawing.Size(147, 22); Menu_Save.Text = "Save"; Menu_Save.Click += Menu_Save_Click; // @@ -159,14 +159,14 @@ private void InitializeComponent() // Menu_Tools.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { Menu_DumpDecrypted, Menu_VerifyHashes, Menu_LoadDecrypted, Menu_RAMEdit, Menu_ItemImages }); Menu_Tools.Name = "Menu_Tools"; - Menu_Tools.Size = new System.Drawing.Size(47, 20); + Menu_Tools.Size = new System.Drawing.Size(51, 21); Menu_Tools.Text = "Tools"; // // Menu_DumpDecrypted // Menu_DumpDecrypted.Name = "Menu_DumpDecrypted"; Menu_DumpDecrypted.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.D; - Menu_DumpDecrypted.Size = new System.Drawing.Size(206, 22); + Menu_DumpDecrypted.Size = new System.Drawing.Size(221, 22); Menu_DumpDecrypted.Text = "Dump Decrypted"; Menu_DumpDecrypted.Click += Menu_DumpDecrypted_Click; // @@ -174,7 +174,7 @@ private void InitializeComponent() // Menu_VerifyHashes.Name = "Menu_VerifyHashes"; Menu_VerifyHashes.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.H; - Menu_VerifyHashes.Size = new System.Drawing.Size(206, 22); + Menu_VerifyHashes.Size = new System.Drawing.Size(221, 22); Menu_VerifyHashes.Text = "Verify Hashes"; Menu_VerifyHashes.Click += Menu_VerifyHashes_Click; // @@ -182,7 +182,7 @@ private void InitializeComponent() // Menu_LoadDecrypted.Name = "Menu_LoadDecrypted"; Menu_LoadDecrypted.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.L; - Menu_LoadDecrypted.Size = new System.Drawing.Size(206, 22); + Menu_LoadDecrypted.Size = new System.Drawing.Size(221, 22); Menu_LoadDecrypted.Text = "Load Decrypted"; Menu_LoadDecrypted.Click += Menu_LoadDecrypted_Click; // @@ -190,7 +190,7 @@ private void InitializeComponent() // Menu_RAMEdit.Name = "Menu_RAMEdit"; Menu_RAMEdit.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.R; - Menu_RAMEdit.Size = new System.Drawing.Size(206, 22); + Menu_RAMEdit.Size = new System.Drawing.Size(221, 22); Menu_RAMEdit.Text = "RAM Edit"; Menu_RAMEdit.Click += Menu_RAMEdit_Click; // @@ -198,7 +198,7 @@ private void InitializeComponent() // Menu_ItemImages.Name = "Menu_ItemImages"; Menu_ItemImages.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.M; - Menu_ItemImages.Size = new System.Drawing.Size(206, 22); + Menu_ItemImages.Size = new System.Drawing.Size(221, 22); Menu_ItemImages.Text = "Item Images"; Menu_ItemImages.Click += Menu_ItemImages_Click; // @@ -206,7 +206,7 @@ private void InitializeComponent() // Menu_Options.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { Menu_Language, Menu_Theme, Menu_Settings }); Menu_Options.Name = "Menu_Options"; - Menu_Options.Size = new System.Drawing.Size(61, 20); + Menu_Options.Size = new System.Drawing.Size(66, 21); Menu_Options.Text = "Options"; // // Menu_Language @@ -214,7 +214,7 @@ private void InitializeComponent() Menu_Language.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; Menu_Language.Items.AddRange(new object[] { "English", "日本語", "Deutsch", "Español", "Français", "Italiano", "한국어", "简体中文", "繁體中文" }); Menu_Language.Name = "Menu_Language"; - Menu_Language.Size = new System.Drawing.Size(115, 23); + Menu_Language.Size = new System.Drawing.Size(115, 25); Menu_Language.SelectedIndexChanged += Menu_Language_SelectedIndexChanged; // // Menu_Theme @@ -227,21 +227,21 @@ private void InitializeComponent() // Menu_Theme_System // Menu_Theme_System.Name = "Menu_Theme_System"; - Menu_Theme_System.Size = new System.Drawing.Size(152, 22); + Menu_Theme_System.Size = new System.Drawing.Size(160, 22); Menu_Theme_System.Text = "System Theme"; Menu_Theme_System.Click += Menu_Theme_System_Click; // // Menu_Theme_Classic // Menu_Theme_Classic.Name = "Menu_Theme_Classic"; - Menu_Theme_Classic.Size = new System.Drawing.Size(152, 22); + Menu_Theme_Classic.Size = new System.Drawing.Size(160, 22); Menu_Theme_Classic.Text = "Light (Classic)"; Menu_Theme_Classic.Click += Menu_Theme_Classic_Click; // // Menu_Theme_Dark // Menu_Theme_Dark.Name = "Menu_Theme_Dark"; - Menu_Theme_Dark.Size = new System.Drawing.Size(152, 22); + Menu_Theme_Dark.Size = new System.Drawing.Size(160, 22); Menu_Theme_Dark.Text = "Dark"; Menu_Theme_Dark.Click += Menu_Theme_Dark_Click; // @@ -259,12 +259,12 @@ private void InitializeComponent() CM_Picture.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { Menu_SavePNG }); CM_Picture.Name = "CM_Picture"; CM_Picture.ShowImageMargin = false; - CM_Picture.Size = new System.Drawing.Size(101, 26); + CM_Picture.Size = new System.Drawing.Size(109, 26); // // Menu_SavePNG // Menu_SavePNG.Name = "Menu_SavePNG"; - Menu_SavePNG.Size = new System.Drawing.Size(100, 22); + Menu_SavePNG.Size = new System.Drawing.Size(108, 22); Menu_SavePNG.Text = "Save .png"; Menu_SavePNG.Click += Menu_SavePNG_Click; // @@ -286,11 +286,11 @@ private void InitializeComponent() Tab_Map.Controls.Add(B_EditPatterns); Tab_Map.Controls.Add(B_EditTurnipExchange); Tab_Map.Controls.Add(B_RecycleBin); - Tab_Map.Location = new System.Drawing.Point(4, 24); + Tab_Map.Location = new System.Drawing.Point(4, 36); Tab_Map.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); Tab_Map.Name = "Tab_Map"; Tab_Map.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3); - Tab_Map.Size = new System.Drawing.Size(463, 303); + Tab_Map.Size = new System.Drawing.Size(463, 342); Tab_Map.TabIndex = 2; Tab_Map.Text = "Map"; Tab_Map.UseVisualStyleBackColor = true; @@ -298,10 +298,10 @@ private void InitializeComponent() // B_EditFruitFlower // B_EditFruitFlower.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; - B_EditFruitFlower.Location = new System.Drawing.Point(236, 194); + B_EditFruitFlower.Location = new System.Drawing.Point(236, 270); B_EditFruitFlower.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_EditFruitFlower.Name = "B_EditFruitFlower"; - B_EditFruitFlower.Size = new System.Drawing.Size(107, 46); + B_EditFruitFlower.Size = new System.Drawing.Size(107, 64); B_EditFruitFlower.TabIndex = 67; B_EditFruitFlower.Text = "Edit Island Fruits + Flowers"; B_EditFruitFlower.UseVisualStyleBackColor = true; @@ -309,10 +309,11 @@ private void InitializeComponent() // // B_EditCampsite // - B_EditCampsite.Location = new System.Drawing.Point(121, 195); + B_EditCampsite.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + B_EditCampsite.Location = new System.Drawing.Point(121, 270); B_EditCampsite.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_EditCampsite.Name = "B_EditCampsite"; - B_EditCampsite.Size = new System.Drawing.Size(107, 46); + B_EditCampsite.Size = new System.Drawing.Size(107, 64); B_EditCampsite.TabIndex = 66; B_EditCampsite.Text = "Edit Campsite"; B_EditCampsite.UseVisualStyleBackColor = true; @@ -321,7 +322,7 @@ private void InitializeComponent() // NUD_WeatherSeed // NUD_WeatherSeed.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0); - NUD_WeatherSeed.Location = new System.Drawing.Point(236, 68); + NUD_WeatherSeed.Location = new System.Drawing.Point(236, 77); NUD_WeatherSeed.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); NUD_WeatherSeed.Maximum = new decimal(new int[] { -1, 0, 0, 0 }); NUD_WeatherSeed.Name = "NUD_WeatherSeed"; @@ -332,19 +333,19 @@ private void InitializeComponent() // L_WeatherSeed // L_WeatherSeed.AutoSize = true; - L_WeatherSeed.Location = new System.Drawing.Point(351, 70); + L_WeatherSeed.Location = new System.Drawing.Point(351, 79); L_WeatherSeed.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); L_WeatherSeed.Name = "L_WeatherSeed"; - L_WeatherSeed.Size = new System.Drawing.Size(79, 15); + L_WeatherSeed.Size = new System.Drawing.Size(89, 17); L_WeatherSeed.TabIndex = 64; L_WeatherSeed.Text = "Weather Seed"; // // B_EditDesignsTailor // - B_EditDesignsTailor.Location = new System.Drawing.Point(350, 113); + B_EditDesignsTailor.Location = new System.Drawing.Point(350, 128); B_EditDesignsTailor.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_EditDesignsTailor.Name = "B_EditDesignsTailor"; - B_EditDesignsTailor.Size = new System.Drawing.Size(107, 46); + B_EditDesignsTailor.Size = new System.Drawing.Size(107, 64); B_EditDesignsTailor.TabIndex = 63; B_EditDesignsTailor.Text = "Edit Tailor Designs"; B_EditDesignsTailor.UseVisualStyleBackColor = true; @@ -352,10 +353,10 @@ private void InitializeComponent() // // B_EditPatternFlag // - B_EditPatternFlag.Location = new System.Drawing.Point(236, 113); + B_EditPatternFlag.Location = new System.Drawing.Point(236, 128); B_EditPatternFlag.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_EditPatternFlag.Name = "B_EditPatternFlag"; - B_EditPatternFlag.Size = new System.Drawing.Size(107, 46); + B_EditPatternFlag.Size = new System.Drawing.Size(107, 64); B_EditPatternFlag.TabIndex = 62; B_EditPatternFlag.Text = "Edit Flag Design"; B_EditPatternFlag.UseVisualStyleBackColor = true; @@ -365,29 +366,29 @@ private void InitializeComponent() // CB_AirportColor.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; CB_AirportColor.FormattingEnabled = true; - CB_AirportColor.Location = new System.Drawing.Point(236, 37); + CB_AirportColor.Location = new System.Drawing.Point(236, 42); CB_AirportColor.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); CB_AirportColor.Name = "CB_AirportColor"; - CB_AirportColor.Size = new System.Drawing.Size(107, 23); + CB_AirportColor.Size = new System.Drawing.Size(107, 25); CB_AirportColor.TabIndex = 61; // // L_AirportColor // L_AirportColor.AutoSize = true; - L_AirportColor.Location = new System.Drawing.Point(350, 40); + L_AirportColor.Location = new System.Drawing.Point(350, 45); L_AirportColor.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); L_AirportColor.Name = "L_AirportColor"; - L_AirportColor.Size = new System.Drawing.Size(76, 15); + L_AirportColor.Size = new System.Drawing.Size(85, 17); L_AirportColor.TabIndex = 60; L_AirportColor.Text = "Airport Color"; // // L_Hemisphere // L_Hemisphere.AutoSize = true; - L_Hemisphere.Location = new System.Drawing.Point(350, 10); + L_Hemisphere.Location = new System.Drawing.Point(350, 11); L_Hemisphere.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); L_Hemisphere.Name = "L_Hemisphere"; - L_Hemisphere.Size = new System.Drawing.Size(71, 15); + L_Hemisphere.Size = new System.Drawing.Size(78, 17); L_Hemisphere.TabIndex = 58; L_Hemisphere.Text = "Hemisphere"; // @@ -395,19 +396,19 @@ private void InitializeComponent() // CB_Hemisphere.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; CB_Hemisphere.FormattingEnabled = true; - CB_Hemisphere.Location = new System.Drawing.Point(236, 7); + CB_Hemisphere.Location = new System.Drawing.Point(236, 8); CB_Hemisphere.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); CB_Hemisphere.Name = "CB_Hemisphere"; - CB_Hemisphere.Size = new System.Drawing.Size(107, 23); + CB_Hemisphere.Size = new System.Drawing.Size(107, 25); CB_Hemisphere.TabIndex = 57; // // B_EditPlayerHouses // - B_EditPlayerHouses.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left; - B_EditPlayerHouses.Location = new System.Drawing.Point(7, 195); + B_EditPlayerHouses.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + B_EditPlayerHouses.Location = new System.Drawing.Point(7, 270); B_EditPlayerHouses.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_EditPlayerHouses.Name = "B_EditPlayerHouses"; - B_EditPlayerHouses.Size = new System.Drawing.Size(107, 46); + B_EditPlayerHouses.Size = new System.Drawing.Size(107, 64); B_EditPlayerHouses.TabIndex = 56; B_EditPlayerHouses.Text = "Edit Player Houses"; B_EditPlayerHouses.UseVisualStyleBackColor = true; @@ -415,11 +416,12 @@ private void InitializeComponent() // // B_EditMap // + B_EditMap.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; B_EditMap.ContextMenuStrip = CM_EditMap; - B_EditMap.Location = new System.Drawing.Point(350, 194); + B_EditMap.Location = new System.Drawing.Point(350, 270); B_EditMap.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_EditMap.Name = "B_EditMap"; - B_EditMap.Size = new System.Drawing.Size(107, 46); + B_EditMap.Size = new System.Drawing.Size(107, 64); B_EditMap.TabIndex = 2; B_EditMap.Text = "Edit Map..."; B_EditMap.UseVisualStyleBackColor = true; @@ -430,49 +432,49 @@ private void InitializeComponent() CM_EditMap.ImageScalingSize = new System.Drawing.Size(20, 20); CM_EditMap.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { B_EditLandFlags, B_EditFieldItems, B_EditBulletin, B_EditMuseum_Click, B_EditVisitors }); CM_EditMap.Name = "CM_EditMap"; - CM_EditMap.Size = new System.Drawing.Size(172, 114); + CM_EditMap.Size = new System.Drawing.Size(183, 114); // // B_EditLandFlags // B_EditLandFlags.Name = "B_EditLandFlags"; - B_EditLandFlags.Size = new System.Drawing.Size(171, 22); + B_EditLandFlags.Size = new System.Drawing.Size(182, 22); B_EditLandFlags.Text = "Edit Flags"; B_EditLandFlags.Click += B_EditLandFlags_Click; // // B_EditFieldItems // B_EditFieldItems.Name = "B_EditFieldItems"; - B_EditFieldItems.Size = new System.Drawing.Size(171, 22); + B_EditFieldItems.Size = new System.Drawing.Size(182, 22); B_EditFieldItems.Text = "Edit Field Items"; B_EditFieldItems.Click += B_EditFieldItems_Click; // // B_EditBulletin // B_EditBulletin.Name = "B_EditBulletin"; - B_EditBulletin.Size = new System.Drawing.Size(171, 22); + B_EditBulletin.Size = new System.Drawing.Size(182, 22); B_EditBulletin.Text = "Edit Bulletin Board"; B_EditBulletin.Click += B_EditBulletin_Click; // // B_EditMuseum_Click // B_EditMuseum_Click.Name = "B_EditMuseum_Click"; - B_EditMuseum_Click.Size = new System.Drawing.Size(171, 22); + B_EditMuseum_Click.Size = new System.Drawing.Size(182, 22); B_EditMuseum_Click.Text = "Edit Museum"; B_EditMuseum_Click.Click += B_EditMuseum_Click_Click; // // B_EditVisitors // B_EditVisitors.Name = "B_EditVisitors"; - B_EditVisitors.Size = new System.Drawing.Size(171, 22); + B_EditVisitors.Size = new System.Drawing.Size(182, 22); B_EditVisitors.Text = "Edit Visitors"; B_EditVisitors.Click += B_EditVisitors_Click; // // B_EditPRODesigns // - B_EditPRODesigns.Location = new System.Drawing.Point(121, 113); + B_EditPRODesigns.Location = new System.Drawing.Point(121, 128); B_EditPRODesigns.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_EditPRODesigns.Name = "B_EditPRODesigns"; - B_EditPRODesigns.Size = new System.Drawing.Size(107, 46); + B_EditPRODesigns.Size = new System.Drawing.Size(107, 64); B_EditPRODesigns.TabIndex = 55; B_EditPRODesigns.Text = "Edit PRO Designs"; B_EditPRODesigns.UseVisualStyleBackColor = true; @@ -480,10 +482,10 @@ private void InitializeComponent() // // B_EditPatterns // - B_EditPatterns.Location = new System.Drawing.Point(7, 113); + B_EditPatterns.Location = new System.Drawing.Point(7, 128); B_EditPatterns.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_EditPatterns.Name = "B_EditPatterns"; - B_EditPatterns.Size = new System.Drawing.Size(107, 46); + B_EditPatterns.Size = new System.Drawing.Size(107, 64); B_EditPatterns.TabIndex = 54; B_EditPatterns.Text = "Edit Patterns"; B_EditPatterns.UseVisualStyleBackColor = true; @@ -491,10 +493,10 @@ private void InitializeComponent() // // B_EditTurnipExchange // - B_EditTurnipExchange.Location = new System.Drawing.Point(7, 7); + B_EditTurnipExchange.Location = new System.Drawing.Point(7, 8); B_EditTurnipExchange.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_EditTurnipExchange.Name = "B_EditTurnipExchange"; - B_EditTurnipExchange.Size = new System.Drawing.Size(107, 46); + B_EditTurnipExchange.Size = new System.Drawing.Size(107, 52); B_EditTurnipExchange.TabIndex = 15; B_EditTurnipExchange.Text = "Edit Turnip Exchange"; B_EditTurnipExchange.UseVisualStyleBackColor = true; @@ -502,10 +504,10 @@ private void InitializeComponent() // // B_RecycleBin // - B_RecycleBin.Location = new System.Drawing.Point(7, 60); + B_RecycleBin.Location = new System.Drawing.Point(7, 68); B_RecycleBin.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_RecycleBin.Name = "B_RecycleBin"; - B_RecycleBin.Size = new System.Drawing.Size(107, 46); + B_RecycleBin.Size = new System.Drawing.Size(107, 52); B_RecycleBin.TabIndex = 13; B_RecycleBin.Text = "Edit Recycle Bin"; B_RecycleBin.UseVisualStyleBackColor = true; @@ -513,11 +515,11 @@ private void InitializeComponent() // // Tab_Villagers // - Tab_Villagers.Location = new System.Drawing.Point(4, 24); + Tab_Villagers.Location = new System.Drawing.Point(4, 36); Tab_Villagers.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); Tab_Villagers.Name = "Tab_Villagers"; Tab_Villagers.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3); - Tab_Villagers.Size = new System.Drawing.Size(463, 303); + Tab_Villagers.Size = new System.Drawing.Size(463, 342); Tab_Villagers.TabIndex = 0; Tab_Villagers.Text = "Villagers"; Tab_Villagers.UseVisualStyleBackColor = true; @@ -550,100 +552,100 @@ private void InitializeComponent() Tab_Players.Controls.Add(L_PlayerName); Tab_Players.Controls.Add(CB_Players); Tab_Players.Controls.Add(PB_Player); - Tab_Players.Location = new System.Drawing.Point(4, 24); + Tab_Players.Location = new System.Drawing.Point(4, 36); Tab_Players.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); Tab_Players.Name = "Tab_Players"; Tab_Players.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3); - Tab_Players.Size = new System.Drawing.Size(463, 303); + Tab_Players.Size = new System.Drawing.Size(463, 342); Tab_Players.TabIndex = 1; Tab_Players.Text = "Players"; Tab_Players.UseVisualStyleBackColor = true; // // L_HotelTickets // - L_HotelTickets.Location = new System.Drawing.Point(166, 273); + L_HotelTickets.Location = new System.Drawing.Point(166, 309); L_HotelTickets.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); L_HotelTickets.Name = "L_HotelTickets"; - L_HotelTickets.Size = new System.Drawing.Size(98, 23); + L_HotelTickets.Size = new System.Drawing.Size(98, 26); L_HotelTickets.TabIndex = 30; L_HotelTickets.Text = "Hotel Tickets:"; L_HotelTickets.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // NUD_HotelTickets // - NUD_HotelTickets.Location = new System.Drawing.Point(271, 273); + NUD_HotelTickets.Location = new System.Drawing.Point(271, 309); NUD_HotelTickets.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); NUD_HotelTickets.Maximum = new decimal(new int[] { int.MaxValue, 0, 0, 0 }); NUD_HotelTickets.Name = "NUD_HotelTickets"; - NUD_HotelTickets.Size = new System.Drawing.Size(117, 23); + NUD_HotelTickets.Size = new System.Drawing.Size(117, 25); NUD_HotelTickets.TabIndex = 29; // // L_Poki // - L_Poki.Location = new System.Drawing.Point(166, 245); + L_Poki.Location = new System.Drawing.Point(166, 278); L_Poki.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); L_Poki.Name = "L_Poki"; - L_Poki.Size = new System.Drawing.Size(98, 23); + L_Poki.Size = new System.Drawing.Size(98, 26); L_Poki.TabIndex = 28; L_Poki.Text = "Poki:"; L_Poki.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // NUD_Poki // - NUD_Poki.Location = new System.Drawing.Point(271, 245); + NUD_Poki.Location = new System.Drawing.Point(271, 278); NUD_Poki.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); NUD_Poki.Maximum = new decimal(new int[] { int.MaxValue, 0, 0, 0 }); NUD_Poki.Name = "NUD_Poki"; - NUD_Poki.Size = new System.Drawing.Size(117, 23); + NUD_Poki.Size = new System.Drawing.Size(117, 25); NUD_Poki.TabIndex = 27; // // L_EarnedMiles // - L_EarnedMiles.Location = new System.Drawing.Point(166, 135); + L_EarnedMiles.Location = new System.Drawing.Point(166, 153); L_EarnedMiles.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); L_EarnedMiles.Name = "L_EarnedMiles"; - L_EarnedMiles.Size = new System.Drawing.Size(98, 23); + L_EarnedMiles.Size = new System.Drawing.Size(98, 26); L_EarnedMiles.TabIndex = 26; L_EarnedMiles.Text = "Earned Miles:"; L_EarnedMiles.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // NUD_TotalNookMiles // - NUD_TotalNookMiles.Location = new System.Drawing.Point(271, 135); + NUD_TotalNookMiles.Location = new System.Drawing.Point(271, 153); NUD_TotalNookMiles.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); NUD_TotalNookMiles.Maximum = new decimal(new int[] { int.MaxValue, 0, 0, 0 }); NUD_TotalNookMiles.Name = "NUD_TotalNookMiles"; - NUD_TotalNookMiles.Size = new System.Drawing.Size(117, 23); + NUD_TotalNookMiles.Size = new System.Drawing.Size(117, 25); NUD_TotalNookMiles.TabIndex = 25; // // L_StorageCount // L_StorageCount.AutoSize = true; - L_StorageCount.Location = new System.Drawing.Point(233, 167); + L_StorageCount.Location = new System.Drawing.Point(233, 189); L_StorageCount.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); L_StorageCount.Name = "L_StorageCount"; - L_StorageCount.Size = new System.Drawing.Size(83, 15); + L_StorageCount.Size = new System.Drawing.Size(92, 17); L_StorageCount.TabIndex = 24; L_StorageCount.Text = "Storage Count"; L_StorageCount.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // NUD_StorageCount // - NUD_StorageCount.Location = new System.Drawing.Point(176, 164); + NUD_StorageCount.Location = new System.Drawing.Point(176, 186); NUD_StorageCount.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); NUD_StorageCount.Maximum = new decimal(new int[] { int.MaxValue, 0, 0, 0 }); NUD_StorageCount.Name = "NUD_StorageCount"; - NUD_StorageCount.Size = new System.Drawing.Size(54, 23); + NUD_StorageCount.Size = new System.Drawing.Size(54, 25); NUD_StorageCount.TabIndex = 23; NUD_StorageCount.Value = new decimal(new int[] { 5000, 0, 0, 0 }); // // L_PocketCount2 // L_PocketCount2.AutoSize = true; - L_PocketCount2.Location = new System.Drawing.Point(233, 222); + L_PocketCount2.Location = new System.Drawing.Point(233, 252); L_PocketCount2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); L_PocketCount2.Name = "L_PocketCount2"; - L_PocketCount2.Size = new System.Drawing.Size(88, 15); + L_PocketCount2.Size = new System.Drawing.Size(95, 17); L_PocketCount2.TabIndex = 22; L_PocketCount2.Text = "Pocket Count 2"; L_PocketCount2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; @@ -651,32 +653,32 @@ private void InitializeComponent() // L_PocketCount1 // L_PocketCount1.AutoSize = true; - L_PocketCount1.Location = new System.Drawing.Point(233, 197); + L_PocketCount1.Location = new System.Drawing.Point(233, 223); L_PocketCount1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); L_PocketCount1.Name = "L_PocketCount1"; - L_PocketCount1.Size = new System.Drawing.Size(88, 15); + L_PocketCount1.Size = new System.Drawing.Size(95, 17); L_PocketCount1.TabIndex = 21; L_PocketCount1.Text = "Pocket Count 1"; L_PocketCount1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // NUD_PocketCount2 // - NUD_PocketCount2.Location = new System.Drawing.Point(176, 218); + NUD_PocketCount2.Location = new System.Drawing.Point(176, 247); NUD_PocketCount2.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); NUD_PocketCount2.Maximum = new decimal(new int[] { int.MaxValue, 0, 0, 0 }); NUD_PocketCount2.Name = "NUD_PocketCount2"; - NUD_PocketCount2.Size = new System.Drawing.Size(54, 23); + NUD_PocketCount2.Size = new System.Drawing.Size(54, 25); NUD_PocketCount2.TabIndex = 20; NUD_PocketCount2.Value = new decimal(new int[] { 20, 0, 0, 0 }); NUD_PocketCount2.ValueChanged += NUD_PocketCount_ValueChanged; // // NUD_PocketCount1 // - NUD_PocketCount1.Location = new System.Drawing.Point(176, 194); + NUD_PocketCount1.Location = new System.Drawing.Point(176, 220); NUD_PocketCount1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); NUD_PocketCount1.Maximum = new decimal(new int[] { int.MaxValue, 0, 0, 0 }); NUD_PocketCount1.Name = "NUD_PocketCount1"; - NUD_PocketCount1.Size = new System.Drawing.Size(54, 23); + NUD_PocketCount1.Size = new System.Drawing.Size(54, 25); NUD_PocketCount1.TabIndex = 19; NUD_PocketCount1.Value = new decimal(new int[] { 20, 0, 0, 0 }); NUD_PocketCount1.ValueChanged += NUD_PocketCount_ValueChanged; @@ -684,10 +686,10 @@ private void InitializeComponent() // B_EditPlayer // B_EditPlayer.ContextMenuStrip = CM_EditPlayer; - B_EditPlayer.Location = new System.Drawing.Point(7, 247); + B_EditPlayer.Location = new System.Drawing.Point(7, 280); B_EditPlayer.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_EditPlayer.Name = "B_EditPlayer"; - B_EditPlayer.Size = new System.Drawing.Size(152, 46); + B_EditPlayer.Size = new System.Drawing.Size(152, 52); B_EditPlayer.TabIndex = 18; B_EditPlayer.Text = "Edit Player..."; B_EditPlayer.UseVisualStyleBackColor = true; @@ -698,63 +700,63 @@ private void InitializeComponent() CM_EditPlayer.ImageScalingSize = new System.Drawing.Size(20, 20); CM_EditPlayer.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { B_EditPlayerStorage, B_EditPlayerReceivedItems, B_EditAchievements, B_EditPlayerRecipes, B_EditPlayerFlags, B_EditPlayerReactions, B_EditPlayerMisc }); CM_EditPlayer.Name = "CM_EditPlayer"; - CM_EditPlayer.Size = new System.Drawing.Size(177, 158); + CM_EditPlayer.Size = new System.Drawing.Size(190, 158); // // B_EditPlayerStorage // B_EditPlayerStorage.Name = "B_EditPlayerStorage"; - B_EditPlayerStorage.Size = new System.Drawing.Size(176, 22); + B_EditPlayerStorage.Size = new System.Drawing.Size(189, 22); B_EditPlayerStorage.Text = "Edit Storage"; B_EditPlayerStorage.Click += B_Storage_Click; // // B_EditPlayerReceivedItems // B_EditPlayerReceivedItems.Name = "B_EditPlayerReceivedItems"; - B_EditPlayerReceivedItems.Size = new System.Drawing.Size(176, 22); + B_EditPlayerReceivedItems.Size = new System.Drawing.Size(189, 22); B_EditPlayerReceivedItems.Text = "Edit Received Items"; B_EditPlayerReceivedItems.Click += B_EditPlayerReceivedItems_Click; // // B_EditAchievements // B_EditAchievements.Name = "B_EditAchievements"; - B_EditAchievements.Size = new System.Drawing.Size(176, 22); + B_EditAchievements.Size = new System.Drawing.Size(189, 22); B_EditAchievements.Text = "Edit Achievements"; B_EditAchievements.Click += B_EditAchievements_Click; // // B_EditPlayerRecipes // B_EditPlayerRecipes.Name = "B_EditPlayerRecipes"; - B_EditPlayerRecipes.Size = new System.Drawing.Size(176, 22); + B_EditPlayerRecipes.Size = new System.Drawing.Size(189, 22); B_EditPlayerRecipes.Text = "Edit Recipes"; B_EditPlayerRecipes.Click += B_EditPlayerRecipes_Click; // // B_EditPlayerFlags // B_EditPlayerFlags.Name = "B_EditPlayerFlags"; - B_EditPlayerFlags.Size = new System.Drawing.Size(176, 22); + B_EditPlayerFlags.Size = new System.Drawing.Size(189, 22); B_EditPlayerFlags.Text = "Edit Flags"; B_EditPlayerFlags.Click += B_EditPlayerFlags_Click; // // B_EditPlayerReactions // B_EditPlayerReactions.Name = "B_EditPlayerReactions"; - B_EditPlayerReactions.Size = new System.Drawing.Size(176, 22); + B_EditPlayerReactions.Size = new System.Drawing.Size(189, 22); B_EditPlayerReactions.Text = "Edit Reactions"; B_EditPlayerReactions.Click += B_EditPlayerReactions_Click; // // B_EditPlayerMisc // B_EditPlayerMisc.Name = "B_EditPlayerMisc"; - B_EditPlayerMisc.Size = new System.Drawing.Size(176, 22); + B_EditPlayerMisc.Size = new System.Drawing.Size(189, 22); B_EditPlayerMisc.Text = "Edit Misc"; B_EditPlayerMisc.Click += B_EditPlayerMisc_Click; // // B_EditPlayerItems // - B_EditPlayerItems.Location = new System.Drawing.Point(7, 194); + B_EditPlayerItems.Location = new System.Drawing.Point(7, 220); B_EditPlayerItems.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_EditPlayerItems.Name = "B_EditPlayerItems"; - B_EditPlayerItems.Size = new System.Drawing.Size(152, 46); + B_EditPlayerItems.Size = new System.Drawing.Size(152, 52); B_EditPlayerItems.TabIndex = 12; B_EditPlayerItems.Text = "Edit Items"; B_EditPlayerItems.UseVisualStyleBackColor = true; @@ -762,94 +764,94 @@ private void InitializeComponent() // // L_Wallet // - L_Wallet.Location = new System.Drawing.Point(166, 59); + L_Wallet.Location = new System.Drawing.Point(166, 67); L_Wallet.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); L_Wallet.Name = "L_Wallet"; - L_Wallet.Size = new System.Drawing.Size(98, 23); + L_Wallet.Size = new System.Drawing.Size(98, 26); L_Wallet.TabIndex = 11; L_Wallet.Text = "Wallet:"; L_Wallet.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // NUD_Wallet // - NUD_Wallet.Location = new System.Drawing.Point(271, 59); + NUD_Wallet.Location = new System.Drawing.Point(271, 67); NUD_Wallet.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); NUD_Wallet.Maximum = new decimal(new int[] { int.MaxValue, 0, 0, 0 }); NUD_Wallet.Name = "NUD_Wallet"; - NUD_Wallet.Size = new System.Drawing.Size(117, 23); + NUD_Wallet.Size = new System.Drawing.Size(117, 25); NUD_Wallet.TabIndex = 10; NUD_Wallet.ValueChanged += NUD_Wallet_ValueChanged; // // L_NookMiles // - L_NookMiles.Location = new System.Drawing.Point(166, 110); + L_NookMiles.Location = new System.Drawing.Point(166, 125); L_NookMiles.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); L_NookMiles.Name = "L_NookMiles"; - L_NookMiles.Size = new System.Drawing.Size(98, 23); + L_NookMiles.Size = new System.Drawing.Size(98, 26); L_NookMiles.TabIndex = 9; L_NookMiles.Text = "Nook Miles:"; L_NookMiles.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // NUD_NookMiles // - NUD_NookMiles.Location = new System.Drawing.Point(271, 110); + NUD_NookMiles.Location = new System.Drawing.Point(271, 125); NUD_NookMiles.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); NUD_NookMiles.Maximum = new decimal(new int[] { int.MaxValue, 0, 0, 0 }); NUD_NookMiles.Name = "NUD_NookMiles"; - NUD_NookMiles.Size = new System.Drawing.Size(117, 23); + NUD_NookMiles.Size = new System.Drawing.Size(117, 25); NUD_NookMiles.TabIndex = 8; // // L_BankBells // - L_BankBells.Location = new System.Drawing.Point(166, 84); + L_BankBells.Location = new System.Drawing.Point(166, 95); L_BankBells.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); L_BankBells.Name = "L_BankBells"; - L_BankBells.Size = new System.Drawing.Size(98, 23); + L_BankBells.Size = new System.Drawing.Size(98, 26); L_BankBells.TabIndex = 7; L_BankBells.Text = "Bank Bells:"; L_BankBells.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // NUD_BankBells // - NUD_BankBells.Location = new System.Drawing.Point(271, 84); + NUD_BankBells.Location = new System.Drawing.Point(271, 95); NUD_BankBells.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); NUD_BankBells.Maximum = new decimal(new int[] { int.MaxValue, 0, 0, 0 }); NUD_BankBells.Name = "NUD_BankBells"; - NUD_BankBells.Size = new System.Drawing.Size(117, 23); + NUD_BankBells.Size = new System.Drawing.Size(117, 25); NUD_BankBells.TabIndex = 6; // // TB_TownName // - TB_TownName.Location = new System.Drawing.Point(271, 33); + TB_TownName.Location = new System.Drawing.Point(271, 37); TB_TownName.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); TB_TownName.Name = "TB_TownName"; - TB_TownName.Size = new System.Drawing.Size(116, 23); + TB_TownName.Size = new System.Drawing.Size(116, 25); TB_TownName.TabIndex = 5; // // TB_Name // - TB_Name.Location = new System.Drawing.Point(271, 8); + TB_Name.Location = new System.Drawing.Point(271, 9); TB_Name.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); TB_Name.Name = "TB_Name"; - TB_Name.Size = new System.Drawing.Size(116, 23); + TB_Name.Size = new System.Drawing.Size(116, 25); TB_Name.TabIndex = 3; // // L_TownName // - L_TownName.Location = new System.Drawing.Point(166, 33); + L_TownName.Location = new System.Drawing.Point(166, 37); L_TownName.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); L_TownName.Name = "L_TownName"; - L_TownName.Size = new System.Drawing.Size(98, 23); + L_TownName.Size = new System.Drawing.Size(98, 26); L_TownName.TabIndex = 4; L_TownName.Text = "Town Name:"; L_TownName.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // L_PlayerName // - L_PlayerName.Location = new System.Drawing.Point(166, 8); + L_PlayerName.Location = new System.Drawing.Point(166, 9); L_PlayerName.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); L_PlayerName.Name = "L_PlayerName"; - L_PlayerName.Size = new System.Drawing.Size(98, 23); + L_PlayerName.Size = new System.Drawing.Size(98, 26); L_PlayerName.TabIndex = 2; L_PlayerName.Text = "Player Name:"; L_PlayerName.TextAlign = System.Drawing.ContentAlignment.MiddleRight; @@ -858,10 +860,10 @@ private void InitializeComponent() // CB_Players.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; CB_Players.FormattingEnabled = true; - CB_Players.Location = new System.Drawing.Point(7, 7); + CB_Players.Location = new System.Drawing.Point(7, 8); CB_Players.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); CB_Players.Name = "CB_Players"; - CB_Players.Size = new System.Drawing.Size(151, 23); + CB_Players.Size = new System.Drawing.Size(151, 25); CB_Players.TabIndex = 1; CB_Players.SelectedIndexChanged += LoadPlayer; // @@ -869,10 +871,10 @@ private void InitializeComponent() // PB_Player.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; PB_Player.ContextMenuStrip = CM_Picture; - PB_Player.Location = new System.Drawing.Point(7, 38); + PB_Player.Location = new System.Drawing.Point(7, 43); PB_Player.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); PB_Player.Name = "PB_Player"; - PB_Player.Size = new System.Drawing.Size(151, 150); + PB_Player.Size = new System.Drawing.Size(151, 170); PB_Player.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; PB_Player.TabIndex = 0; PB_Player.TabStop = false; @@ -882,18 +884,20 @@ private void InitializeComponent() TC_Editors.Controls.Add(Tab_Players); TC_Editors.Controls.Add(Tab_Villagers); TC_Editors.Controls.Add(Tab_Map); - TC_Editors.Location = new System.Drawing.Point(0, 28); - TC_Editors.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + TC_Editors.Dock = System.Windows.Forms.DockStyle.Fill; + TC_Editors.Location = new System.Drawing.Point(0, 25); + TC_Editors.Margin = new System.Windows.Forms.Padding(4); TC_Editors.Name = "TC_Editors"; + TC_Editors.Padding = new System.Drawing.Point(8, 8); TC_Editors.SelectedIndex = 0; - TC_Editors.Size = new System.Drawing.Size(471, 331); + TC_Editors.Size = new System.Drawing.Size(471, 382); TC_Editors.TabIndex = 1; // // Editor // - AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - ClientSize = new System.Drawing.Size(471, 359); + ClientSize = new System.Drawing.Size(471, 407); Controls.Add(TC_Editors); Controls.Add(Menu_Editor); Icon = Properties.Resources.icon; diff --git a/NHSE.WinForms/Editor.cs b/NHSE.WinForms/Editor.cs index 5a93cb8..bab7483 100644 --- a/NHSE.WinForms/Editor.cs +++ b/NHSE.WinForms/Editor.cs @@ -271,14 +271,17 @@ private void SaveMain() #region Player Editing private void LoadPlayers() { - if (SAV.Players.Length == 0) + // A valid save file has at least one player (main player). + if (SAV.Players.Count == 0) throw new Exception("No players found in the loaded directory."); + // Load the player names to the selection box. CB_Players.Items.Clear(); var playerList = SAV.Players.Select(z => z.DirectoryName); foreach (var p in playerList) CB_Players.Items.Add(p); + // Trigger a load. PlayerIndex = -1; CB_Players.SelectedIndex = 0; } diff --git a/NHSE.WinForms/Main.cs b/NHSE.WinForms/Main.cs index 60ad1bd..a6eb8fe 100644 --- a/NHSE.WinForms/Main.cs +++ b/NHSE.WinForms/Main.cs @@ -32,7 +32,7 @@ public Main() Show(); WindowState = FormWindowState.Normal; - var args = Environment.GetCommandLineArgs().AsSpan(); + var args = Environment.GetCommandLineArgs(); foreach (var arg in args) { if (Directory.Exists(arg)) diff --git a/NHSE.WinForms/Subforms/Map/BulkSpawn.cs b/NHSE.WinForms/Subforms/Map/BulkSpawn.cs index abc146e..8b91bf4 100644 --- a/NHSE.WinForms/Subforms/Map/BulkSpawn.cs +++ b/NHSE.WinForms/Subforms/Map/BulkSpawn.cs @@ -88,7 +88,7 @@ private void B_Apply_Click(object sender, EventArgs e) if (sizeY % 2 == 1) sizeY++; - var ctr = SpawnItems(Editor.SpawnLayer, items, x, y, arrange, sizeX, sizeY, true); + var ctr = SpawnItems(Editor.Spawn, items, x, y, arrange, sizeX, sizeY, true); if (ctr == 0) { WinFormsUtil.Alert(MessageStrings.MsgFieldItemModifyNone); @@ -98,7 +98,7 @@ private void B_Apply_Click(object sender, EventArgs e) WinFormsUtil.Alert(string.Format(MessageStrings.MsgFieldItemModifyCount, count)); } - private static int SpawnItems(ItemLayer layer, IReadOnlyList items, int x, int y, SpawnArrangement arrange, int sizeX, int sizeY, bool noOverwrite) + private static int SpawnItems(LayerItem layer, IReadOnlyList items, int x, int y, SpawnArrangement arrange, int sizeX, int sizeY, bool noOverwrite) { // every {setting} tiles, we jump down to the next available row of tiles. int x0 = x; @@ -116,7 +116,7 @@ private static int SpawnItems(ItemLayer layer, IReadOnlyList 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: diff --git a/NHSE.WinForms/Subforms/Map/FieldItemEditor.Designer.cs b/NHSE.WinForms/Subforms/Map/FieldItemEditor.Designer.cs index 3818241..af256a3 100644 --- a/NHSE.WinForms/Subforms/Map/FieldItemEditor.Designer.cs +++ b/NHSE.WinForms/Subforms/Map/FieldItemEditor.Designer.cs @@ -14,10 +14,7 @@ partial class FieldItemEditor protected override void Dispose(bool disposing) { if (disposing && (components != null)) - { components.Dispose(); - View.Dispose(); - } base.Dispose(disposing); } @@ -29,1473 +26,1447 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - this.components = new System.ComponentModel.Container(); - this.B_Cancel = new System.Windows.Forms.Button(); - this.B_Save = new System.Windows.Forms.Button(); - this.CB_Acre = new System.Windows.Forms.ComboBox(); - this.L_Acre = new System.Windows.Forms.Label(); - this.CM_Click = new System.Windows.Forms.ContextMenuStrip(this.components); - this.Menu_View = new System.Windows.Forms.ToolStripMenuItem(); - this.Menu_Set = new System.Windows.Forms.ToolStripMenuItem(); - this.Menu_Reset = new System.Windows.Forms.ToolStripMenuItem(); - this.Menu_Activate = new System.Windows.Forms.ToolStripMenuItem(); - this.B_Up = new System.Windows.Forms.Button(); - this.B_Left = new System.Windows.Forms.Button(); - this.B_Right = new System.Windows.Forms.Button(); - this.B_Down = new System.Windows.Forms.Button(); - this.PB_Map = new System.Windows.Forms.PictureBox(); - this.CM_Picture = new System.Windows.Forms.ContextMenuStrip(this.components); - this.Menu_SavePNG = new System.Windows.Forms.ToolStripMenuItem(); - this.Menu_SavePNGItems = new System.Windows.Forms.ToolStripMenuItem(); - this.Menu_SavePNGTerrain = new System.Windows.Forms.ToolStripMenuItem(); - this.CHK_SnapToAcre = new System.Windows.Forms.CheckBox(); - this.L_Coordinates = new System.Windows.Forms.Label(); - this.NUD_Layer = new System.Windows.Forms.NumericUpDown(); - this.L_Layer = new System.Windows.Forms.Label(); - this.TT_Hover = new System.Windows.Forms.ToolTip(this.components); - this.PB_Acre = new System.Windows.Forms.PictureBox(); - this.TR_Transparency = new System.Windows.Forms.TrackBar(); - this.CHK_NoOverwrite = new System.Windows.Forms.CheckBox(); - this.CHK_AutoExtension = new System.Windows.Forms.CheckBox(); - this.B_RemoveItemDropDown = new System.Windows.Forms.Button(); - this.CM_Remove = new System.Windows.Forms.ContextMenuStrip(this.components); - this.B_RemoveAllWeeds = new System.Windows.Forms.ToolStripMenuItem(); - this.B_RemoveAllTrees = new System.Windows.Forms.ToolStripMenuItem(); - this.B_RemovePlants = new System.Windows.Forms.ToolStripMenuItem(); - this.B_RemoveObjects = new System.Windows.Forms.ToolStripMenuItem(); - this.B_RemovePlacedItems = new System.Windows.Forms.ToolStripMenuItem(); - this.B_RemoveFences = new System.Windows.Forms.ToolStripMenuItem(); - this.B_RemoveBranches = new System.Windows.Forms.ToolStripMenuItem(); - this.B_RemoveShells = new System.Windows.Forms.ToolStripMenuItem(); - this.B_RemoveFlowers = new System.Windows.Forms.ToolStripMenuItem(); - this.B_RemoveBushes = new System.Windows.Forms.ToolStripMenuItem(); - this.B_FillHoles = new System.Windows.Forms.ToolStripMenuItem(); - this.B_RemoveEditor = new System.Windows.Forms.ToolStripMenuItem(); - this.B_RemoveAll = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); - this.B_WaterFlowers = new System.Windows.Forms.ToolStripMenuItem(); - this.Menu_Spawn = new System.Windows.Forms.ToolStripMenuItem(); - this.Menu_Batch = new System.Windows.Forms.ToolStripMenuItem(); - this.GB_Remove = new System.Windows.Forms.Label(); - this.TC_Editor = new System.Windows.Forms.TabControl(); - this.Tab_Item = new System.Windows.Forms.TabPage(); - this.ItemEdit = new NHSE.WinForms.ItemEditor(); - this.B_DumpLoadField = new System.Windows.Forms.Button(); - this.CM_DLField = new System.Windows.Forms.ContextMenuStrip(this.components); - this.B_DumpAcre = new System.Windows.Forms.ToolStripMenuItem(); - this.B_DumpAllAcres = new System.Windows.Forms.ToolStripMenuItem(); - this.B_ImportAcre = new System.Windows.Forms.ToolStripMenuItem(); - this.B_ImportAllAcres = new System.Windows.Forms.ToolStripMenuItem(); - this.Tab_Building = new System.Windows.Forms.TabPage(); - this.B_DumpLoadBuildings = new System.Windows.Forms.Button(); - this.CM_DLBuilding = new System.Windows.Forms.ContextMenuStrip(this.components); - this.B_DumpBuildings = new System.Windows.Forms.ToolStripMenuItem(); - this.B_ImportBuildings = new System.Windows.Forms.ToolStripMenuItem(); - this.L_Bit = new System.Windows.Forms.Label(); - this.NUD_Bit = new System.Windows.Forms.NumericUpDown(); - this.L_BuildingType = new System.Windows.Forms.Label(); - this.NUD_BuildingType = new System.Windows.Forms.NumericUpDown(); - this.NUD_UniqueID = new System.Windows.Forms.NumericUpDown(); - this.L_BuildingX = new System.Windows.Forms.Label(); - this.L_BuildingUniqueID = new System.Windows.Forms.Label(); - this.NUD_X = new System.Windows.Forms.NumericUpDown(); - this.NUD_TypeArg = new System.Windows.Forms.NumericUpDown(); - this.L_BuildingY = new System.Windows.Forms.Label(); - this.L_BuildingStructureArg = new System.Windows.Forms.Label(); - this.NUD_Y = new System.Windows.Forms.NumericUpDown(); - this.NUD_Type = new System.Windows.Forms.NumericUpDown(); - this.L_BuildingRotation = new System.Windows.Forms.Label(); - this.L_BuildingStructureType = new System.Windows.Forms.Label(); - this.NUD_Angle = new System.Windows.Forms.NumericUpDown(); - this.L_PlazaX = new System.Windows.Forms.Label(); - this.NUD_PlazaX = new System.Windows.Forms.NumericUpDown(); - this.L_PlazaY = new System.Windows.Forms.Label(); - this.NUD_PlazaY = new System.Windows.Forms.NumericUpDown(); - this.B_Help = new System.Windows.Forms.Button(); - this.LB_Items = new System.Windows.Forms.ListBox(); - this.Tab_Terrain = new System.Windows.Forms.TabPage(); - this.B_TerrainBrush = new System.Windows.Forms.Button(); - this.L_TerrainTileLabelTransparency = new System.Windows.Forms.Label(); - this.TR_Terrain = new System.Windows.Forms.TrackBar(); - this.TR_BuildingTransparency = new System.Windows.Forms.TrackBar(); - this.L_BuildingTransparency = new System.Windows.Forms.Label(); - this.PG_TerrainTile = new System.Windows.Forms.PropertyGrid(); - this.L_FieldItemTransparency = new System.Windows.Forms.Label(); - this.B_DumpLoadTerrain = new System.Windows.Forms.Button(); - this.B_ModifyAllTerrain = new System.Windows.Forms.Button(); - this.Tab_Acres = new System.Windows.Forms.TabPage(); - this.NUD_MapAcreTemplateField = new System.Windows.Forms.NumericUpDown(); - this.L_MapAcreTemplateField = new System.Windows.Forms.Label(); - this.L_MapAcreTemplateOutside = new System.Windows.Forms.Label(); - this.NUD_MapAcreTemplateOutside = new System.Windows.Forms.NumericUpDown(); - this.CB_MapAcreSelect = new System.Windows.Forms.ComboBox(); - this.B_DumpLoadAcres = new System.Windows.Forms.Button(); - this.CM_DLMapAcres = new System.Windows.Forms.ContextMenuStrip(this.components); - this.B_DumpMapAcres = new System.Windows.Forms.ToolStripMenuItem(); - this.B_ImportMapAcres = new System.Windows.Forms.ToolStripMenuItem(); - this.L_MapAcre = new System.Windows.Forms.Label(); - this.CB_MapAcre = new System.Windows.Forms.ComboBox(); - this.CM_DLTerrain = new System.Windows.Forms.ContextMenuStrip(this.components); - this.B_DumpTerrainAcre = new System.Windows.Forms.ToolStripMenuItem(); - this.B_DumpTerrainAll = new System.Windows.Forms.ToolStripMenuItem(); - this.B_ImportTerrainAcre = new System.Windows.Forms.ToolStripMenuItem(); - this.B_ImportTerrainAll = new System.Windows.Forms.ToolStripMenuItem(); - this.CM_Terrain = new System.Windows.Forms.ContextMenuStrip(this.components); - this.B_ZeroElevation = new System.Windows.Forms.ToolStripMenuItem(); - this.B_SetAllTerrain = new System.Windows.Forms.ToolStripMenuItem(); - this.B_SetAllRoadTiles = new System.Windows.Forms.ToolStripMenuItem(); - this.B_ClearPlacedDesigns = new System.Windows.Forms.ToolStripMenuItem(); - this.B_ImportPlacedDesigns = new System.Windows.Forms.ToolStripMenuItem(); - this.B_ExportPlacedDesigns = new System.Windows.Forms.ToolStripMenuItem(); - this.RB_Item = new System.Windows.Forms.RadioButton(); - this.RB_Terrain = new System.Windows.Forms.RadioButton(); - this.L_TileMode = new System.Windows.Forms.Label(); - this.CHK_RedirectExtensionLoad = new System.Windows.Forms.CheckBox(); - this.CHK_MoveOnDrag = new System.Windows.Forms.CheckBox(); - this.CHK_FieldItemSnap = new System.Windows.Forms.CheckBox(); - this.CM_Click.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.PB_Map)).BeginInit(); - this.CM_Picture.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_Layer)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.PB_Acre)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.TR_Transparency)).BeginInit(); - this.CM_Remove.SuspendLayout(); - this.TC_Editor.SuspendLayout(); - this.Tab_Item.SuspendLayout(); - this.CM_DLField.SuspendLayout(); - this.Tab_Building.SuspendLayout(); - this.CM_DLBuilding.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_Bit)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_BuildingType)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_UniqueID)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_X)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_TypeArg)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_Y)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_Type)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_Angle)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_PlazaX)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_PlazaY)).BeginInit(); - this.Tab_Terrain.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.TR_Terrain)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.TR_BuildingTransparency)).BeginInit(); - this.Tab_Acres.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_MapAcreTemplateField)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_MapAcreTemplateOutside)).BeginInit(); - this.CM_DLMapAcres.SuspendLayout(); - this.CM_DLTerrain.SuspendLayout(); - this.CM_Terrain.SuspendLayout(); - this.SuspendLayout(); + components = new System.ComponentModel.Container(); + B_Cancel = new System.Windows.Forms.Button(); + B_Save = new System.Windows.Forms.Button(); + CM_Click = new System.Windows.Forms.ContextMenuStrip(components); + Menu_View = new System.Windows.Forms.ToolStripMenuItem(); + Menu_Set = new System.Windows.Forms.ToolStripMenuItem(); + Menu_Reset = new System.Windows.Forms.ToolStripMenuItem(); + Menu_Activate = new System.Windows.Forms.ToolStripMenuItem(); + B_Up = new System.Windows.Forms.Button(); + B_Left = new System.Windows.Forms.Button(); + B_Right = new System.Windows.Forms.Button(); + B_Down = new System.Windows.Forms.Button(); + PB_Map = new System.Windows.Forms.PictureBox(); + CM_Picture = new System.Windows.Forms.ContextMenuStrip(components); + Menu_SavePNG = new System.Windows.Forms.ToolStripMenuItem(); + Menu_SavePNGItems = new System.Windows.Forms.ToolStripMenuItem(); + Menu_SavePNGTerrain = new System.Windows.Forms.ToolStripMenuItem(); + CHK_SnapToAcre = new System.Windows.Forms.CheckBox(); + L_Coordinates = new System.Windows.Forms.Label(); + NUD_Layer = new System.Windows.Forms.NumericUpDown(); + L_Layer = new System.Windows.Forms.Label(); + TT_Hover = new System.Windows.Forms.ToolTip(components); + PB_Viewport = new System.Windows.Forms.PictureBox(); + TR_Transparency = new System.Windows.Forms.TrackBar(); + CHK_NoOverwrite = new System.Windows.Forms.CheckBox(); + CHK_AutoExtension = new System.Windows.Forms.CheckBox(); + B_RemoveItemDropDown = new System.Windows.Forms.Button(); + CM_Remove = new System.Windows.Forms.ContextMenuStrip(components); + B_RemoveAllWeeds = new System.Windows.Forms.ToolStripMenuItem(); + B_RemoveAllTrees = new System.Windows.Forms.ToolStripMenuItem(); + B_RemovePlants = new System.Windows.Forms.ToolStripMenuItem(); + B_RemoveObjects = new System.Windows.Forms.ToolStripMenuItem(); + B_RemovePlacedItems = new System.Windows.Forms.ToolStripMenuItem(); + B_RemoveFences = new System.Windows.Forms.ToolStripMenuItem(); + B_RemoveBranches = new System.Windows.Forms.ToolStripMenuItem(); + B_RemoveShells = new System.Windows.Forms.ToolStripMenuItem(); + B_RemoveFlowers = new System.Windows.Forms.ToolStripMenuItem(); + B_RemoveBushes = new System.Windows.Forms.ToolStripMenuItem(); + B_FillHoles = new System.Windows.Forms.ToolStripMenuItem(); + B_RemoveEditor = new System.Windows.Forms.ToolStripMenuItem(); + B_RemoveAll = new System.Windows.Forms.ToolStripMenuItem(); + toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + B_WaterFlowers = new System.Windows.Forms.ToolStripMenuItem(); + Menu_Spawn = new System.Windows.Forms.ToolStripMenuItem(); + Menu_Batch = new System.Windows.Forms.ToolStripMenuItem(); + GB_Remove = new System.Windows.Forms.Label(); + TC_Editor = new System.Windows.Forms.TabControl(); + Tab_Item = new System.Windows.Forms.TabPage(); + ItemEdit = new ItemEditor(); + B_DumpLoadField = new System.Windows.Forms.Button(); + CM_DLField = new System.Windows.Forms.ContextMenuStrip(components); + B_DumpAcre = new System.Windows.Forms.ToolStripMenuItem(); + B_DumpAllAcres = new System.Windows.Forms.ToolStripMenuItem(); + B_ImportAcre = new System.Windows.Forms.ToolStripMenuItem(); + B_ImportAllAcres = new System.Windows.Forms.ToolStripMenuItem(); + Tab_Building = new System.Windows.Forms.TabPage(); + B_DumpLoadBuildings = new System.Windows.Forms.Button(); + CM_DLBuilding = new System.Windows.Forms.ContextMenuStrip(components); + B_DumpBuildings = new System.Windows.Forms.ToolStripMenuItem(); + B_ImportBuildings = new System.Windows.Forms.ToolStripMenuItem(); + L_Bit = new System.Windows.Forms.Label(); + NUD_Bit = new System.Windows.Forms.NumericUpDown(); + L_BuildingType = new System.Windows.Forms.Label(); + NUD_BuildingType = new System.Windows.Forms.NumericUpDown(); + NUD_UniqueID = new System.Windows.Forms.NumericUpDown(); + L_BuildingX = new System.Windows.Forms.Label(); + L_BuildingUniqueID = new System.Windows.Forms.Label(); + NUD_X = new System.Windows.Forms.NumericUpDown(); + NUD_TypeArg = new System.Windows.Forms.NumericUpDown(); + L_BuildingY = new System.Windows.Forms.Label(); + L_BuildingStructureArg = new System.Windows.Forms.Label(); + NUD_Y = new System.Windows.Forms.NumericUpDown(); + NUD_Type = new System.Windows.Forms.NumericUpDown(); + L_BuildingRotation = new System.Windows.Forms.Label(); + L_BuildingStructureType = new System.Windows.Forms.Label(); + NUD_Angle = new System.Windows.Forms.NumericUpDown(); + L_PlazaX = new System.Windows.Forms.Label(); + NUD_PlazaX = new System.Windows.Forms.NumericUpDown(); + L_PlazaY = new System.Windows.Forms.Label(); + NUD_PlazaY = new System.Windows.Forms.NumericUpDown(); + B_Help = new System.Windows.Forms.Button(); + LB_Items = new System.Windows.Forms.ListBox(); + Tab_Terrain = new System.Windows.Forms.TabPage(); + B_TerrainBrush = new System.Windows.Forms.Button(); + L_TerrainTileLabelTransparency = new System.Windows.Forms.Label(); + TR_Terrain = new System.Windows.Forms.TrackBar(); + TR_BuildingTransparency = new System.Windows.Forms.TrackBar(); + L_BuildingTransparency = new System.Windows.Forms.Label(); + PG_TerrainTile = new System.Windows.Forms.PropertyGrid(); + L_FieldItemTransparency = new System.Windows.Forms.Label(); + B_DumpLoadTerrain = new System.Windows.Forms.Button(); + B_ModifyAllTerrain = new System.Windows.Forms.Button(); + Tab_Acres = new System.Windows.Forms.TabPage(); + NUD_MapAcreTemplateField = new System.Windows.Forms.NumericUpDown(); + L_MapAcreTemplateField = new System.Windows.Forms.Label(); + L_MapAcreTemplateOutside = new System.Windows.Forms.Label(); + NUD_MapAcreTemplateOutside = new System.Windows.Forms.NumericUpDown(); + CB_MapAcreSelect = new System.Windows.Forms.ComboBox(); + B_DumpLoadAcres = new System.Windows.Forms.Button(); + CM_DLMapAcres = new System.Windows.Forms.ContextMenuStrip(components); + B_DumpMapAcres = new System.Windows.Forms.ToolStripMenuItem(); + B_ImportMapAcres = new System.Windows.Forms.ToolStripMenuItem(); + L_MapAcre = new System.Windows.Forms.Label(); + CB_MapAcre = new System.Windows.Forms.ComboBox(); + CM_DLTerrain = new System.Windows.Forms.ContextMenuStrip(components); + B_DumpTerrainAcre = new System.Windows.Forms.ToolStripMenuItem(); + B_DumpTerrainAll = new System.Windows.Forms.ToolStripMenuItem(); + B_ImportTerrainAcre = new System.Windows.Forms.ToolStripMenuItem(); + B_ImportTerrainAll = new System.Windows.Forms.ToolStripMenuItem(); + CM_Terrain = new System.Windows.Forms.ContextMenuStrip(components); + B_ZeroElevation = new System.Windows.Forms.ToolStripMenuItem(); + B_SetAllTerrain = new System.Windows.Forms.ToolStripMenuItem(); + B_SetAllRoadTiles = new System.Windows.Forms.ToolStripMenuItem(); + B_ClearPlacedDesigns = new System.Windows.Forms.ToolStripMenuItem(); + B_ImportPlacedDesigns = new System.Windows.Forms.ToolStripMenuItem(); + B_ExportPlacedDesigns = new System.Windows.Forms.ToolStripMenuItem(); + RB_Item = new System.Windows.Forms.RadioButton(); + RB_Terrain = new System.Windows.Forms.RadioButton(); + L_TileMode = new System.Windows.Forms.Label(); + CHK_RedirectExtensionLoad = new System.Windows.Forms.CheckBox(); + CHK_MoveOnDrag = new System.Windows.Forms.CheckBox(); + CHK_FieldItemSnap = new System.Windows.Forms.CheckBox(); + flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + L_Acre = new System.Windows.Forms.Label(); + CB_Acre = new System.Windows.Forms.ComboBox(); + CM_Click.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)PB_Map).BeginInit(); + CM_Picture.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)NUD_Layer).BeginInit(); + ((System.ComponentModel.ISupportInitialize)PB_Viewport).BeginInit(); + ((System.ComponentModel.ISupportInitialize)TR_Transparency).BeginInit(); + CM_Remove.SuspendLayout(); + TC_Editor.SuspendLayout(); + Tab_Item.SuspendLayout(); + CM_DLField.SuspendLayout(); + Tab_Building.SuspendLayout(); + CM_DLBuilding.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)NUD_Bit).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_BuildingType).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_UniqueID).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_X).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_TypeArg).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_Y).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_Type).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_Angle).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_PlazaX).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_PlazaY).BeginInit(); + Tab_Terrain.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)TR_Terrain).BeginInit(); + ((System.ComponentModel.ISupportInitialize)TR_BuildingTransparency).BeginInit(); + Tab_Acres.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)NUD_MapAcreTemplateField).BeginInit(); + ((System.ComponentModel.ISupportInitialize)NUD_MapAcreTemplateOutside).BeginInit(); + CM_DLMapAcres.SuspendLayout(); + CM_DLTerrain.SuspendLayout(); + CM_Terrain.SuspendLayout(); + flowLayoutPanel1.SuspendLayout(); + SuspendLayout(); // // B_Cancel // - this.B_Cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.B_Cancel.Location = new System.Drawing.Point(866, 502); - this.B_Cancel.Name = "B_Cancel"; - this.B_Cancel.Size = new System.Drawing.Size(72, 23); - this.B_Cancel.TabIndex = 7; - this.B_Cancel.Text = "Cancel"; - this.B_Cancel.UseVisualStyleBackColor = true; - this.B_Cancel.Click += new System.EventHandler(this.B_Cancel_Click); + B_Cancel.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + B_Cancel.Location = new System.Drawing.Point(950, 656); + B_Cancel.Margin = new System.Windows.Forms.Padding(4); + B_Cancel.Name = "B_Cancel"; + B_Cancel.Size = new System.Drawing.Size(84, 30); + B_Cancel.TabIndex = 7; + B_Cancel.Text = "Cancel"; + B_Cancel.UseVisualStyleBackColor = true; + B_Cancel.Click += B_Cancel_Click; // // B_Save // - this.B_Save.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.B_Save.Location = new System.Drawing.Point(944, 502); - this.B_Save.Name = "B_Save"; - this.B_Save.Size = new System.Drawing.Size(72, 23); - this.B_Save.TabIndex = 6; - this.B_Save.Text = "Save"; - this.B_Save.UseVisualStyleBackColor = true; - this.B_Save.Click += new System.EventHandler(this.B_Save_Click); - // - // CB_Acre - // - this.CB_Acre.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.CB_Acre.FormattingEnabled = true; - this.CB_Acre.Location = new System.Drawing.Point(712, 358); - this.CB_Acre.Name = "CB_Acre"; - this.CB_Acre.Size = new System.Drawing.Size(49, 21); - this.CB_Acre.TabIndex = 10; - this.CB_Acre.SelectedIndexChanged += new System.EventHandler(this.ChangeAcre); - // - // L_Acre - // - this.L_Acre.Location = new System.Drawing.Point(619, 360); - this.L_Acre.Name = "L_Acre"; - this.L_Acre.Size = new System.Drawing.Size(87, 19); - this.L_Acre.TabIndex = 11; - this.L_Acre.Text = "Acre:"; - this.L_Acre.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + B_Save.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + B_Save.Location = new System.Drawing.Point(1041, 656); + B_Save.Margin = new System.Windows.Forms.Padding(4); + B_Save.Name = "B_Save"; + B_Save.Size = new System.Drawing.Size(84, 30); + B_Save.TabIndex = 6; + B_Save.Text = "Save"; + B_Save.UseVisualStyleBackColor = true; + B_Save.Click += B_Save_Click; // // CM_Click // - this.CM_Click.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.Menu_View, - this.Menu_Set, - this.Menu_Reset, - this.Menu_Activate}); - this.CM_Click.Name = "CM_Click"; - this.CM_Click.Size = new System.Drawing.Size(181, 114); - this.CM_Click.Opening += new System.ComponentModel.CancelEventHandler(this.CM_Click_Opening); + CM_Click.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { Menu_View, Menu_Set, Menu_Reset, Menu_Activate }); + CM_Click.Name = "CM_Click"; + CM_Click.Size = new System.Drawing.Size(122, 92); + CM_Click.Opening += CM_Click_Opening; // // Menu_View // - this.Menu_View.Name = "Menu_View"; - this.Menu_View.Size = new System.Drawing.Size(180, 22); - this.Menu_View.Text = "View"; - this.Menu_View.Click += new System.EventHandler(this.Menu_View_Click); + Menu_View.Name = "Menu_View"; + Menu_View.Size = new System.Drawing.Size(121, 22); + Menu_View.Text = "View"; + Menu_View.Click += Menu_View_Click; // // Menu_Set // - this.Menu_Set.Name = "Menu_Set"; - this.Menu_Set.Size = new System.Drawing.Size(180, 22); - this.Menu_Set.Text = "Set"; - this.Menu_Set.Click += new System.EventHandler(this.Menu_Set_Click); + Menu_Set.Name = "Menu_Set"; + Menu_Set.Size = new System.Drawing.Size(121, 22); + Menu_Set.Text = "Set"; + Menu_Set.Click += Menu_Set_Click; // // Menu_Reset // - this.Menu_Reset.Name = "Menu_Reset"; - this.Menu_Reset.Size = new System.Drawing.Size(180, 22); - this.Menu_Reset.Text = "Reset"; - this.Menu_Reset.Click += new System.EventHandler(this.Menu_Reset_Click); + Menu_Reset.Name = "Menu_Reset"; + Menu_Reset.Size = new System.Drawing.Size(121, 22); + Menu_Reset.Text = "Reset"; + Menu_Reset.Click += Menu_Reset_Click; // // Menu_Activate // - this.Menu_Activate.Name = "Menu_Activate"; - this.Menu_Activate.Size = new System.Drawing.Size(180, 22); - this.Menu_Activate.Text = "Activate"; - this.Menu_Activate.Click += new System.EventHandler(this.Menu_Activate_Click); + Menu_Activate.Name = "Menu_Activate"; + Menu_Activate.Size = new System.Drawing.Size(121, 22); + Menu_Activate.Text = "Activate"; + Menu_Activate.Click += Menu_Activate_Click; // // B_Up // - this.B_Up.Location = new System.Drawing.Point(578, 287); - this.B_Up.Name = "B_Up"; - this.B_Up.Size = new System.Drawing.Size(32, 32); - this.B_Up.TabIndex = 18; - this.B_Up.Text = "↑"; - this.B_Up.UseVisualStyleBackColor = true; - this.B_Up.Click += new System.EventHandler(this.B_Up_Click); + B_Up.Location = new System.Drawing.Point(575, 307); + B_Up.Margin = new System.Windows.Forms.Padding(4); + B_Up.Name = "B_Up"; + B_Up.Size = new System.Drawing.Size(40, 40); + B_Up.TabIndex = 18; + B_Up.Text = "↑"; + B_Up.UseVisualStyleBackColor = true; + B_Up.Click += B_Up_Click; // // B_Left // - this.B_Left.Location = new System.Drawing.Point(548, 317); - this.B_Left.Name = "B_Left"; - this.B_Left.Size = new System.Drawing.Size(32, 32); - this.B_Left.TabIndex = 19; - this.B_Left.Text = "←"; - this.B_Left.UseVisualStyleBackColor = true; - this.B_Left.Click += new System.EventHandler(this.B_Left_Click); + B_Left.Location = new System.Drawing.Point(536, 347); + B_Left.Margin = new System.Windows.Forms.Padding(4); + B_Left.Name = "B_Left"; + B_Left.Size = new System.Drawing.Size(40, 40); + B_Left.TabIndex = 19; + B_Left.Text = "←"; + B_Left.UseVisualStyleBackColor = true; + B_Left.Click += B_Left_Click; // // B_Right // - this.B_Right.Location = new System.Drawing.Point(608, 317); - this.B_Right.Name = "B_Right"; - this.B_Right.Size = new System.Drawing.Size(32, 32); - this.B_Right.TabIndex = 20; - this.B_Right.Text = "→"; - this.B_Right.UseVisualStyleBackColor = true; - this.B_Right.Click += new System.EventHandler(this.B_Right_Click); + B_Right.Location = new System.Drawing.Point(614, 347); + B_Right.Margin = new System.Windows.Forms.Padding(4); + B_Right.Name = "B_Right"; + B_Right.Size = new System.Drawing.Size(40, 40); + B_Right.TabIndex = 20; + B_Right.Text = "→"; + B_Right.UseVisualStyleBackColor = true; + B_Right.Click += B_Right_Click; // // B_Down // - this.B_Down.Location = new System.Drawing.Point(578, 347); - this.B_Down.Name = "B_Down"; - this.B_Down.Size = new System.Drawing.Size(32, 32); - this.B_Down.TabIndex = 22; - this.B_Down.Text = "↓"; - this.B_Down.UseVisualStyleBackColor = true; - this.B_Down.Click += new System.EventHandler(this.B_Down_Click); + B_Down.Location = new System.Drawing.Point(575, 386); + B_Down.Margin = new System.Windows.Forms.Padding(4); + B_Down.Name = "B_Down"; + B_Down.Size = new System.Drawing.Size(40, 40); + B_Down.TabIndex = 22; + B_Down.Text = "↓"; + B_Down.UseVisualStyleBackColor = true; + B_Down.Click += B_Down_Click; // // PB_Map // - this.PB_Map.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.PB_Map.ContextMenuStrip = this.CM_Picture; - this.PB_Map.Location = new System.Drawing.Point(535, 35); - this.PB_Map.Name = "PB_Map"; - this.PB_Map.Size = new System.Drawing.Size(226, 194); - this.PB_Map.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; - this.PB_Map.TabIndex = 23; - this.PB_Map.TabStop = false; - this.PB_Map.MouseDown += new System.Windows.Forms.MouseEventHandler(this.PB_Map_MouseDown); - this.PB_Map.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PB_Map_MouseMove); + PB_Map.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + PB_Map.ContextMenuStrip = CM_Picture; + PB_Map.Location = new System.Drawing.Point(536, 43); + PB_Map.Margin = new System.Windows.Forms.Padding(4); + PB_Map.Name = "PB_Map"; + PB_Map.Size = new System.Drawing.Size(288, 256); + PB_Map.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + PB_Map.TabIndex = 23; + PB_Map.TabStop = false; + PB_Map.MouseDown += PB_Map_MouseDown; + PB_Map.MouseMove += PB_Map_MouseMove; // // CM_Picture // - this.CM_Picture.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.Menu_SavePNG, - this.Menu_SavePNGItems, - this.Menu_SavePNGTerrain}); - this.CM_Picture.Name = "CM_Picture"; - this.CM_Picture.Size = new System.Drawing.Size(152, 70); - this.CM_Picture.Closing += new System.Windows.Forms.ToolStripDropDownClosingEventHandler(this.CM_Picture_Closing); + CM_Picture.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { Menu_SavePNG, Menu_SavePNGItems, Menu_SavePNGTerrain }); + CM_Picture.Name = "CM_Picture"; + CM_Picture.Size = new System.Drawing.Size(162, 70); + CM_Picture.Closing += CM_Picture_Closing; // // Menu_SavePNG // - this.Menu_SavePNG.Name = "Menu_SavePNG"; - this.Menu_SavePNG.Size = new System.Drawing.Size(151, 22); - this.Menu_SavePNG.Text = "Save .png"; - this.Menu_SavePNG.Click += new System.EventHandler(this.Menu_SavePNG_Click); + Menu_SavePNG.Name = "Menu_SavePNG"; + Menu_SavePNG.Size = new System.Drawing.Size(161, 22); + Menu_SavePNG.Text = "Save .png"; + Menu_SavePNG.Click += Menu_SavePNG_Click; // // Menu_SavePNGItems // - this.Menu_SavePNGItems.Checked = true; - this.Menu_SavePNGItems.CheckOnClick = true; - this.Menu_SavePNGItems.CheckState = System.Windows.Forms.CheckState.Checked; - this.Menu_SavePNGItems.Name = "Menu_SavePNGItems"; - this.Menu_SavePNGItems.Size = new System.Drawing.Size(151, 22); - this.Menu_SavePNGItems.Text = "Include Items"; + Menu_SavePNGItems.Checked = true; + Menu_SavePNGItems.CheckOnClick = true; + Menu_SavePNGItems.CheckState = System.Windows.Forms.CheckState.Checked; + Menu_SavePNGItems.Name = "Menu_SavePNGItems"; + Menu_SavePNGItems.Size = new System.Drawing.Size(161, 22); + Menu_SavePNGItems.Text = "Include Items"; // // Menu_SavePNGTerrain // - this.Menu_SavePNGTerrain.Checked = true; - this.Menu_SavePNGTerrain.CheckOnClick = true; - this.Menu_SavePNGTerrain.CheckState = System.Windows.Forms.CheckState.Checked; - this.Menu_SavePNGTerrain.Name = "Menu_SavePNGTerrain"; - this.Menu_SavePNGTerrain.Size = new System.Drawing.Size(151, 22); - this.Menu_SavePNGTerrain.Text = "Include Terrain"; + Menu_SavePNGTerrain.Checked = true; + Menu_SavePNGTerrain.CheckOnClick = true; + Menu_SavePNGTerrain.CheckState = System.Windows.Forms.CheckState.Checked; + Menu_SavePNGTerrain.Name = "Menu_SavePNGTerrain"; + Menu_SavePNGTerrain.Size = new System.Drawing.Size(161, 22); + Menu_SavePNGTerrain.Text = "Include Terrain"; // // CHK_SnapToAcre // - this.CHK_SnapToAcre.AutoSize = true; - this.CHK_SnapToAcre.Location = new System.Drawing.Point(534, 12); - this.CHK_SnapToAcre.Name = "CHK_SnapToAcre"; - this.CHK_SnapToAcre.Size = new System.Drawing.Size(167, 17); - this.CHK_SnapToAcre.TabIndex = 24; - this.CHK_SnapToAcre.Text = "Snap to nearest Acre on Click"; - this.CHK_SnapToAcre.UseVisualStyleBackColor = true; + CHK_SnapToAcre.AutoSize = true; + CHK_SnapToAcre.Location = new System.Drawing.Point(536, 13); + CHK_SnapToAcre.Margin = new System.Windows.Forms.Padding(4); + CHK_SnapToAcre.Name = "CHK_SnapToAcre"; + CHK_SnapToAcre.Size = new System.Drawing.Size(198, 21); + CHK_SnapToAcre.TabIndex = 24; + CHK_SnapToAcre.Text = "Snap to nearest Acre on Click"; + CHK_SnapToAcre.UseVisualStyleBackColor = true; // // L_Coordinates // - this.L_Coordinates.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.L_Coordinates.Location = new System.Drawing.Point(590, 232); - this.L_Coordinates.Name = "L_Coordinates"; - this.L_Coordinates.Size = new System.Drawing.Size(173, 15); - this.L_Coordinates.TabIndex = 25; - this.L_Coordinates.Text = "(000,000) = (0x00,0x00)"; - this.L_Coordinates.TextAlign = System.Drawing.ContentAlignment.TopRight; + L_Coordinates.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0); + L_Coordinates.Location = new System.Drawing.Point(622, 303); + L_Coordinates.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_Coordinates.Name = "L_Coordinates"; + L_Coordinates.Size = new System.Drawing.Size(202, 20); + L_Coordinates.TabIndex = 25; + L_Coordinates.Text = "(000,000) = (0x00,0x00)"; + L_Coordinates.TextAlign = System.Drawing.ContentAlignment.TopRight; // // NUD_Layer // - this.NUD_Layer.Location = new System.Drawing.Point(712, 385); - this.NUD_Layer.Maximum = new decimal(new int[] { - 2, - 0, - 0, - 0}); - this.NUD_Layer.Minimum = new decimal(new int[] { - 1, - 0, - 0, - 0}); - this.NUD_Layer.Name = "NUD_Layer"; - this.NUD_Layer.Size = new System.Drawing.Size(49, 20); - this.NUD_Layer.TabIndex = 26; - this.NUD_Layer.Value = new decimal(new int[] { - 1, - 0, - 0, - 0}); - this.NUD_Layer.ValueChanged += new System.EventHandler(this.NUD_Layer_ValueChanged); + NUD_Layer.Location = new System.Drawing.Point(767, 409); + NUD_Layer.Margin = new System.Windows.Forms.Padding(4); + NUD_Layer.Maximum = new decimal(new int[] { 2, 0, 0, 0 }); + NUD_Layer.Minimum = new decimal(new int[] { 1, 0, 0, 0 }); + NUD_Layer.Name = "NUD_Layer"; + NUD_Layer.Size = new System.Drawing.Size(57, 25); + NUD_Layer.TabIndex = 26; + NUD_Layer.Value = new decimal(new int[] { 1, 0, 0, 0 }); + NUD_Layer.ValueChanged += NUD_Layer_ValueChanged; // // L_Layer // - this.L_Layer.Location = new System.Drawing.Point(619, 385); - this.L_Layer.Name = "L_Layer"; - this.L_Layer.Size = new System.Drawing.Size(87, 19); - this.L_Layer.TabIndex = 27; - this.L_Layer.Text = "Item Layer:"; - this.L_Layer.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + L_Layer.Location = new System.Drawing.Point(658, 409); + L_Layer.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_Layer.Name = "L_Layer"; + L_Layer.Size = new System.Drawing.Size(102, 25); + L_Layer.TabIndex = 27; + L_Layer.Text = "Item Layer:"; + L_Layer.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // TT_Hover // - this.TT_Hover.AutomaticDelay = 100; + TT_Hover.AutomaticDelay = 100; // // PB_Acre // - this.PB_Acre.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.PB_Acre.ContextMenuStrip = this.CM_Click; - this.PB_Acre.Location = new System.Drawing.Point(12, 12); - this.PB_Acre.Name = "PB_Acre"; - this.PB_Acre.Size = new System.Drawing.Size(514, 514); - this.PB_Acre.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; - this.PB_Acre.TabIndex = 28; - this.PB_Acre.TabStop = false; - this.PB_Acre.MouseClick += new System.Windows.Forms.MouseEventHandler(this.PB_Acre_MouseClick); - this.PB_Acre.MouseDown += new System.Windows.Forms.MouseEventHandler(this.PB_Acre_MouseDown); - this.PB_Acre.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PB_Acre_MouseMove); + PB_Viewport.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + PB_Viewport.ContextMenuStrip = CM_Click; + PB_Viewport.Location = new System.Drawing.Point(14, 16); + PB_Viewport.Margin = new System.Windows.Forms.Padding(4); + PB_Viewport.Name = "PB_Viewport"; + PB_Viewport.Size = new System.Drawing.Size(514, 514); + PB_Viewport.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + PB_Viewport.TabIndex = 28; + PB_Viewport.TabStop = false; + PB_Viewport.MouseClick += ViewportMouseClick; + PB_Viewport.MouseDown += ViewportMouseDown; + PB_Viewport.MouseMove += ViewportMouseMove; // // TR_Transparency // - this.TR_Transparency.AutoSize = false; - this.TR_Transparency.Location = new System.Drawing.Point(3, 332); - this.TR_Transparency.Maximum = 100; - this.TR_Transparency.Name = "TR_Transparency"; - this.TR_Transparency.Size = new System.Drawing.Size(237, 28); - this.TR_Transparency.TabIndex = 36; - this.TR_Transparency.TickFrequency = 10; - this.TR_Transparency.Value = 90; - this.TR_Transparency.Scroll += new System.EventHandler(this.TR_Transparency_Scroll); + TR_Transparency.AutoSize = false; + TR_Transparency.Location = new System.Drawing.Point(4, 434); + TR_Transparency.Margin = new System.Windows.Forms.Padding(4); + TR_Transparency.Maximum = 100; + TR_Transparency.Name = "TR_Transparency"; + TR_Transparency.Size = new System.Drawing.Size(276, 37); + TR_Transparency.TabIndex = 36; + TR_Transparency.TickFrequency = 10; + TR_Transparency.Value = 90; + TR_Transparency.Scroll += TR_Transparency_Scroll; // // CHK_NoOverwrite // - this.CHK_NoOverwrite.AutoSize = true; - this.CHK_NoOverwrite.Checked = true; - this.CHK_NoOverwrite.CheckState = System.Windows.Forms.CheckState.Checked; - this.CHK_NoOverwrite.Location = new System.Drawing.Point(535, 478); - this.CHK_NoOverwrite.Name = "CHK_NoOverwrite"; - this.CHK_NoOverwrite.Size = new System.Drawing.Size(196, 17); - this.CHK_NoOverwrite.TabIndex = 37; - this.CHK_NoOverwrite.Text = "Prevent Writing Occupied Item Tiles"; - this.CHK_NoOverwrite.UseVisualStyleBackColor = true; + CHK_NoOverwrite.AutoSize = true; + CHK_NoOverwrite.Checked = true; + CHK_NoOverwrite.CheckState = System.Windows.Forms.CheckState.Checked; + CHK_NoOverwrite.Location = new System.Drawing.Point(0, 21); + CHK_NoOverwrite.Margin = new System.Windows.Forms.Padding(0); + CHK_NoOverwrite.Name = "CHK_NoOverwrite"; + CHK_NoOverwrite.Size = new System.Drawing.Size(234, 21); + CHK_NoOverwrite.TabIndex = 37; + CHK_NoOverwrite.Text = "Prevent Writing Occupied Item Tiles"; + CHK_NoOverwrite.UseVisualStyleBackColor = true; // // CHK_AutoExtension // - this.CHK_AutoExtension.AutoSize = true; - this.CHK_AutoExtension.Checked = true; - this.CHK_AutoExtension.CheckState = System.Windows.Forms.CheckState.Checked; - this.CHK_AutoExtension.Location = new System.Drawing.Point(535, 460); - this.CHK_AutoExtension.Name = "CHK_AutoExtension"; - this.CHK_AutoExtension.Size = new System.Drawing.Size(202, 17); - this.CHK_AutoExtension.TabIndex = 38; - this.CHK_AutoExtension.Text = "Handle Item Extensions Automatically"; - this.CHK_AutoExtension.UseVisualStyleBackColor = true; + CHK_AutoExtension.AutoSize = true; + CHK_AutoExtension.Checked = true; + CHK_AutoExtension.CheckState = System.Windows.Forms.CheckState.Checked; + CHK_AutoExtension.Location = new System.Drawing.Point(0, 42); + CHK_AutoExtension.Margin = new System.Windows.Forms.Padding(0); + CHK_AutoExtension.Name = "CHK_AutoExtension"; + CHK_AutoExtension.Size = new System.Drawing.Size(243, 21); + CHK_AutoExtension.TabIndex = 38; + CHK_AutoExtension.Text = "Handle Item Extensions Automatically"; + CHK_AutoExtension.UseVisualStyleBackColor = true; // // B_RemoveItemDropDown // - this.B_RemoveItemDropDown.ContextMenuStrip = this.CM_Remove; - this.B_RemoveItemDropDown.Location = new System.Drawing.Point(126, 413); - this.B_RemoveItemDropDown.Name = "B_RemoveItemDropDown"; - this.B_RemoveItemDropDown.Size = new System.Drawing.Size(112, 40); - this.B_RemoveItemDropDown.TabIndex = 37; - this.B_RemoveItemDropDown.Text = "Remove Items..."; - this.B_RemoveItemDropDown.UseVisualStyleBackColor = true; - this.B_RemoveItemDropDown.Click += new System.EventHandler(this.B_RemoveItemDropDown_Click); + B_RemoveItemDropDown.ContextMenuStrip = CM_Remove; + B_RemoveItemDropDown.Location = new System.Drawing.Point(147, 540); + B_RemoveItemDropDown.Margin = new System.Windows.Forms.Padding(4); + B_RemoveItemDropDown.Name = "B_RemoveItemDropDown"; + B_RemoveItemDropDown.Size = new System.Drawing.Size(131, 52); + B_RemoveItemDropDown.TabIndex = 37; + B_RemoveItemDropDown.Text = "Remove Items..."; + B_RemoveItemDropDown.UseVisualStyleBackColor = true; + B_RemoveItemDropDown.Click += B_RemoveItemDropDown_Click; // // CM_Remove // - this.CM_Remove.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.B_RemoveAllWeeds, - this.B_RemoveAllTrees, - this.B_RemovePlants, - this.B_RemoveObjects, - this.B_RemovePlacedItems, - this.B_RemoveFences, - this.B_RemoveBranches, - this.B_RemoveShells, - this.B_RemoveFlowers, - this.B_RemoveBushes, - this.B_FillHoles, - this.B_RemoveEditor, - this.B_RemoveAll, - this.toolStripSeparator1, - this.B_WaterFlowers, - this.Menu_Spawn, - this.Menu_Batch}); - this.CM_Remove.Name = "CM_Picture"; - this.CM_Remove.ShowImageMargin = false; - this.CM_Remove.Size = new System.Drawing.Size(124, 362); + CM_Remove.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { B_RemoveAllWeeds, B_RemoveAllTrees, B_RemovePlants, B_RemoveObjects, B_RemovePlacedItems, B_RemoveFences, B_RemoveBranches, B_RemoveShells, B_RemoveFlowers, B_RemoveBushes, B_FillHoles, B_RemoveEditor, B_RemoveAll, toolStripSeparator1, B_WaterFlowers, Menu_Spawn, Menu_Batch }); + CM_Remove.Name = "CM_Picture"; + CM_Remove.ShowImageMargin = false; + CM_Remove.Size = new System.Drawing.Size(134, 362); // // B_RemoveAllWeeds // - this.B_RemoveAllWeeds.Name = "B_RemoveAllWeeds"; - this.B_RemoveAllWeeds.Size = new System.Drawing.Size(123, 22); - this.B_RemoveAllWeeds.Text = "Weeds"; - this.B_RemoveAllWeeds.Click += new System.EventHandler(this.B_RemoveAllWeeds_Click); + B_RemoveAllWeeds.Name = "B_RemoveAllWeeds"; + B_RemoveAllWeeds.Size = new System.Drawing.Size(133, 22); + B_RemoveAllWeeds.Text = "Weeds"; + B_RemoveAllWeeds.Click += B_RemoveAllWeeds_Click; // // B_RemoveAllTrees // - this.B_RemoveAllTrees.Name = "B_RemoveAllTrees"; - this.B_RemoveAllTrees.Size = new System.Drawing.Size(123, 22); - this.B_RemoveAllTrees.Text = "Trees"; - this.B_RemoveAllTrees.Click += new System.EventHandler(this.B_RemoveAllTrees_Click); + B_RemoveAllTrees.Name = "B_RemoveAllTrees"; + B_RemoveAllTrees.Size = new System.Drawing.Size(133, 22); + B_RemoveAllTrees.Text = "Trees"; + B_RemoveAllTrees.Click += B_RemoveAllTrees_Click; // // B_RemovePlants // - this.B_RemovePlants.Name = "B_RemovePlants"; - this.B_RemovePlants.Size = new System.Drawing.Size(123, 22); - this.B_RemovePlants.Text = "Plants"; - this.B_RemovePlants.Click += new System.EventHandler(this.B_RemovePlants_Click); + B_RemovePlants.Name = "B_RemovePlants"; + B_RemovePlants.Size = new System.Drawing.Size(133, 22); + B_RemovePlants.Text = "Plants"; + B_RemovePlants.Click += B_RemovePlants_Click; // // B_RemoveObjects // - this.B_RemoveObjects.Name = "B_RemoveObjects"; - this.B_RemoveObjects.Size = new System.Drawing.Size(123, 22); - this.B_RemoveObjects.Text = "Objects"; - this.B_RemoveObjects.Click += new System.EventHandler(this.B_RemoveObjects_Click); + B_RemoveObjects.Name = "B_RemoveObjects"; + B_RemoveObjects.Size = new System.Drawing.Size(133, 22); + B_RemoveObjects.Text = "Objects"; + B_RemoveObjects.Click += B_RemoveObjects_Click; // // B_RemovePlacedItems // - this.B_RemovePlacedItems.Name = "B_RemovePlacedItems"; - this.B_RemovePlacedItems.Size = new System.Drawing.Size(123, 22); - this.B_RemovePlacedItems.Text = "Placed Items"; - this.B_RemovePlacedItems.Click += new System.EventHandler(this.B_RemovePlacedItems_Click); + B_RemovePlacedItems.Name = "B_RemovePlacedItems"; + B_RemovePlacedItems.Size = new System.Drawing.Size(133, 22); + B_RemovePlacedItems.Text = "Placed Items"; + B_RemovePlacedItems.Click += B_RemovePlacedItems_Click; // // B_RemoveFences // - this.B_RemoveFences.Name = "B_RemoveFences"; - this.B_RemoveFences.Size = new System.Drawing.Size(123, 22); - this.B_RemoveFences.Text = "Fences"; - this.B_RemoveFences.Click += new System.EventHandler(this.B_RemoveFences_Click); + B_RemoveFences.Name = "B_RemoveFences"; + B_RemoveFences.Size = new System.Drawing.Size(133, 22); + B_RemoveFences.Text = "Fences"; + B_RemoveFences.Click += B_RemoveFences_Click; // // B_RemoveBranches // - this.B_RemoveBranches.Name = "B_RemoveBranches"; - this.B_RemoveBranches.Size = new System.Drawing.Size(123, 22); - this.B_RemoveBranches.Text = "Branches"; - this.B_RemoveBranches.Click += new System.EventHandler(this.B_RemoveBranches_Click); + B_RemoveBranches.Name = "B_RemoveBranches"; + B_RemoveBranches.Size = new System.Drawing.Size(133, 22); + B_RemoveBranches.Text = "Branches"; + B_RemoveBranches.Click += B_RemoveBranches_Click; // // B_RemoveShells // - this.B_RemoveShells.Name = "B_RemoveShells"; - this.B_RemoveShells.Size = new System.Drawing.Size(123, 22); - this.B_RemoveShells.Text = "Shells"; - this.B_RemoveShells.Click += new System.EventHandler(this.B_RemoveShells_Click); + B_RemoveShells.Name = "B_RemoveShells"; + B_RemoveShells.Size = new System.Drawing.Size(133, 22); + B_RemoveShells.Text = "Shells"; + B_RemoveShells.Click += B_RemoveShells_Click; // // B_RemoveFlowers // - this.B_RemoveFlowers.Name = "B_RemoveFlowers"; - this.B_RemoveFlowers.Size = new System.Drawing.Size(123, 22); - this.B_RemoveFlowers.Text = "Flowers"; - this.B_RemoveFlowers.Click += new System.EventHandler(this.B_RemoveFlowers_Click); + B_RemoveFlowers.Name = "B_RemoveFlowers"; + B_RemoveFlowers.Size = new System.Drawing.Size(133, 22); + B_RemoveFlowers.Text = "Flowers"; + B_RemoveFlowers.Click += B_RemoveFlowers_Click; // // B_RemoveBushes // - this.B_RemoveBushes.Name = "B_RemoveBushes"; - this.B_RemoveBushes.Size = new System.Drawing.Size(123, 22); - this.B_RemoveBushes.Text = "Bushes"; - this.B_RemoveBushes.Click += new System.EventHandler(this.B_RemoveBushes_Click); + B_RemoveBushes.Name = "B_RemoveBushes"; + B_RemoveBushes.Size = new System.Drawing.Size(133, 22); + B_RemoveBushes.Text = "Bushes"; + B_RemoveBushes.Click += B_RemoveBushes_Click; // // B_FillHoles // - this.B_FillHoles.Name = "B_FillHoles"; - this.B_FillHoles.Size = new System.Drawing.Size(123, 22); - this.B_FillHoles.Text = "Holes"; - this.B_FillHoles.Click += new System.EventHandler(this.B_FillHoles_Click); + B_FillHoles.Name = "B_FillHoles"; + B_FillHoles.Size = new System.Drawing.Size(133, 22); + B_FillHoles.Text = "Holes"; + B_FillHoles.Click += B_FillHoles_Click; // // B_RemoveEditor // - this.B_RemoveEditor.Name = "B_RemoveEditor"; - this.B_RemoveEditor.Size = new System.Drawing.Size(123, 22); - this.B_RemoveEditor.Text = "Editor Item"; - this.B_RemoveEditor.Click += new System.EventHandler(this.B_RemoveEditor_Click); + B_RemoveEditor.Name = "B_RemoveEditor"; + B_RemoveEditor.Size = new System.Drawing.Size(133, 22); + B_RemoveEditor.Text = "Editor Item"; + B_RemoveEditor.Click += B_RemoveEditor_Click; // // B_RemoveAll // - this.B_RemoveAll.Name = "B_RemoveAll"; - this.B_RemoveAll.Size = new System.Drawing.Size(123, 22); - this.B_RemoveAll.Text = "All"; - this.B_RemoveAll.Click += new System.EventHandler(this.B_RemoveAll_Click); + B_RemoveAll.Name = "B_RemoveAll"; + B_RemoveAll.Size = new System.Drawing.Size(133, 22); + B_RemoveAll.Text = "All"; + B_RemoveAll.Click += B_RemoveAll_Click; // // toolStripSeparator1 // - this.toolStripSeparator1.Name = "toolStripSeparator1"; - this.toolStripSeparator1.Size = new System.Drawing.Size(120, 6); + toolStripSeparator1.Name = "toolStripSeparator1"; + toolStripSeparator1.Size = new System.Drawing.Size(130, 6); // // B_WaterFlowers // - this.B_WaterFlowers.Name = "B_WaterFlowers"; - this.B_WaterFlowers.Size = new System.Drawing.Size(123, 22); - this.B_WaterFlowers.Text = "Water Flowers"; - this.B_WaterFlowers.Click += new System.EventHandler(this.B_WaterFlowers_Click); + B_WaterFlowers.Name = "B_WaterFlowers"; + B_WaterFlowers.Size = new System.Drawing.Size(133, 22); + B_WaterFlowers.Text = "Water Flowers"; + B_WaterFlowers.Click += B_WaterFlowers_Click; // // Menu_Spawn // - this.Menu_Spawn.Name = "Menu_Spawn"; - this.Menu_Spawn.Size = new System.Drawing.Size(123, 22); - this.Menu_Spawn.Text = "Spawn..."; - this.Menu_Spawn.Click += new System.EventHandler(this.Menu_Spawn_Click); + Menu_Spawn.Name = "Menu_Spawn"; + Menu_Spawn.Size = new System.Drawing.Size(133, 22); + Menu_Spawn.Text = "Spawn..."; + Menu_Spawn.Click += Menu_Spawn_Click; // // Menu_Batch // - this.Menu_Batch.Name = "Menu_Batch"; - this.Menu_Batch.Size = new System.Drawing.Size(123, 22); - this.Menu_Batch.Text = "Batch Editor"; - this.Menu_Batch.Click += new System.EventHandler(this.Menu_Bulk_Click); + Menu_Batch.Name = "Menu_Batch"; + Menu_Batch.Size = new System.Drawing.Size(133, 22); + Menu_Batch.Text = "Batch Editor"; + Menu_Batch.Click += Menu_Bulk_Click; // // GB_Remove // - this.GB_Remove.AutoSize = true; - this.GB_Remove.Location = new System.Drawing.Point(60, 396); - this.GB_Remove.Name = "GB_Remove"; - this.GB_Remove.Size = new System.Drawing.Size(178, 13); - this.GB_Remove.TabIndex = 39; - this.GB_Remove.Text = "Remove from View (Hold Shift=Map)"; + GB_Remove.AutoSize = true; + GB_Remove.Location = new System.Drawing.Point(8, 499); + GB_Remove.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + GB_Remove.Name = "GB_Remove"; + GB_Remove.Size = new System.Drawing.Size(223, 17); + GB_Remove.TabIndex = 39; + GB_Remove.Text = "Remove from View (Hold Shift=Map)"; // // TC_Editor // - this.TC_Editor.Controls.Add(this.Tab_Item); - this.TC_Editor.Controls.Add(this.Tab_Building); - this.TC_Editor.Controls.Add(this.Tab_Terrain); - this.TC_Editor.Controls.Add(this.Tab_Acres); - this.TC_Editor.Location = new System.Drawing.Point(767, 12); - this.TC_Editor.Name = "TC_Editor"; - this.TC_Editor.SelectedIndex = 0; - this.TC_Editor.Size = new System.Drawing.Size(252, 484); - this.TC_Editor.TabIndex = 40; + TC_Editor.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right; + TC_Editor.Controls.Add(Tab_Item); + TC_Editor.Controls.Add(Tab_Building); + TC_Editor.Controls.Add(Tab_Terrain); + TC_Editor.Controls.Add(Tab_Acres); + TC_Editor.Location = new System.Drawing.Point(835, 16); + TC_Editor.Margin = new System.Windows.Forms.Padding(4); + TC_Editor.Name = "TC_Editor"; + TC_Editor.SelectedIndex = 0; + TC_Editor.Size = new System.Drawing.Size(294, 633); + TC_Editor.TabIndex = 40; // // Tab_Item // - this.Tab_Item.Controls.Add(this.ItemEdit); - this.Tab_Item.Controls.Add(this.B_DumpLoadField); - this.Tab_Item.Controls.Add(this.B_RemoveItemDropDown); - this.Tab_Item.Controls.Add(this.GB_Remove); - this.Tab_Item.Location = new System.Drawing.Point(4, 22); - this.Tab_Item.Name = "Tab_Item"; - this.Tab_Item.Padding = new System.Windows.Forms.Padding(3); - this.Tab_Item.Size = new System.Drawing.Size(244, 458); - this.Tab_Item.TabIndex = 0; - this.Tab_Item.Text = "Items"; - this.Tab_Item.UseVisualStyleBackColor = true; + Tab_Item.Controls.Add(ItemEdit); + Tab_Item.Controls.Add(B_DumpLoadField); + Tab_Item.Controls.Add(B_RemoveItemDropDown); + Tab_Item.Controls.Add(GB_Remove); + Tab_Item.Location = new System.Drawing.Point(4, 26); + Tab_Item.Margin = new System.Windows.Forms.Padding(4); + Tab_Item.Name = "Tab_Item"; + Tab_Item.Padding = new System.Windows.Forms.Padding(4); + Tab_Item.Size = new System.Drawing.Size(286, 603); + Tab_Item.TabIndex = 0; + Tab_Item.Text = "Items"; + Tab_Item.UseVisualStyleBackColor = true; // // ItemEdit // - this.ItemEdit.Dock = System.Windows.Forms.DockStyle.Top; - this.ItemEdit.Location = new System.Drawing.Point(3, 3); - this.ItemEdit.Name = "ItemEdit"; - this.ItemEdit.Size = new System.Drawing.Size(238, 390); - this.ItemEdit.TabIndex = 40; + ItemEdit.Dock = System.Windows.Forms.DockStyle.Top; + ItemEdit.Location = new System.Drawing.Point(4, 4); + ItemEdit.Margin = new System.Windows.Forms.Padding(5); + ItemEdit.Name = "ItemEdit"; + ItemEdit.Size = new System.Drawing.Size(278, 437); + ItemEdit.TabIndex = 40; // // B_DumpLoadField // - this.B_DumpLoadField.ContextMenuStrip = this.CM_DLField; - this.B_DumpLoadField.Location = new System.Drawing.Point(6, 413); - this.B_DumpLoadField.Name = "B_DumpLoadField"; - this.B_DumpLoadField.Size = new System.Drawing.Size(112, 40); - this.B_DumpLoadField.TabIndex = 38; - this.B_DumpLoadField.Text = "Dump/Import"; - this.B_DumpLoadField.UseVisualStyleBackColor = true; - this.B_DumpLoadField.Click += new System.EventHandler(this.B_DumpLoadField_Click); + B_DumpLoadField.ContextMenuStrip = CM_DLField; + B_DumpLoadField.Location = new System.Drawing.Point(7, 540); + B_DumpLoadField.Margin = new System.Windows.Forms.Padding(4); + B_DumpLoadField.Name = "B_DumpLoadField"; + B_DumpLoadField.Size = new System.Drawing.Size(131, 52); + B_DumpLoadField.TabIndex = 38; + B_DumpLoadField.Text = "Dump/Import"; + B_DumpLoadField.UseVisualStyleBackColor = true; + B_DumpLoadField.Click += B_DumpLoadField_Click; // // CM_DLField // - this.CM_DLField.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.B_DumpAcre, - this.B_DumpAllAcres, - this.B_ImportAcre, - this.B_ImportAllAcres}); - this.CM_DLField.Name = "CM_Picture"; - this.CM_DLField.ShowImageMargin = false; - this.CM_DLField.Size = new System.Drawing.Size(135, 92); + CM_DLField.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { B_DumpAcre, B_DumpAllAcres, B_ImportAcre, B_ImportAllAcres }); + CM_DLField.Name = "CM_Picture"; + CM_DLField.ShowImageMargin = false; + CM_DLField.Size = new System.Drawing.Size(145, 92); // // B_DumpAcre // - this.B_DumpAcre.Name = "B_DumpAcre"; - this.B_DumpAcre.Size = new System.Drawing.Size(134, 22); - this.B_DumpAcre.Text = "Dump Acre"; - this.B_DumpAcre.Click += new System.EventHandler(this.B_DumpAcre_Click); + B_DumpAcre.Name = "B_DumpAcre"; + B_DumpAcre.Size = new System.Drawing.Size(144, 22); + B_DumpAcre.Text = "Dump Acre"; + B_DumpAcre.Click += B_DumpAcreItem_Click; // // B_DumpAllAcres // - this.B_DumpAllAcres.Name = "B_DumpAllAcres"; - this.B_DumpAllAcres.Size = new System.Drawing.Size(134, 22); - this.B_DumpAllAcres.Text = "Dump All Acres"; - this.B_DumpAllAcres.Click += new System.EventHandler(this.B_DumpAllAcres_Click); + B_DumpAllAcres.Name = "B_DumpAllAcres"; + B_DumpAllAcres.Size = new System.Drawing.Size(144, 22); + B_DumpAllAcres.Text = "Dump All Acres"; + B_DumpAllAcres.Click += B_DumpAllAcres_Click; // // B_ImportAcre // - this.B_ImportAcre.Name = "B_ImportAcre"; - this.B_ImportAcre.Size = new System.Drawing.Size(134, 22); - this.B_ImportAcre.Text = "Import Acre"; - this.B_ImportAcre.Click += new System.EventHandler(this.B_ImportAcre_Click); + B_ImportAcre.Name = "B_ImportAcre"; + B_ImportAcre.Size = new System.Drawing.Size(144, 22); + B_ImportAcre.Text = "Import Acre"; + B_ImportAcre.Click += B_ImportAcreItem_Click; // // B_ImportAllAcres // - this.B_ImportAllAcres.Name = "B_ImportAllAcres"; - this.B_ImportAllAcres.Size = new System.Drawing.Size(134, 22); - this.B_ImportAllAcres.Text = "Import All Acres"; - this.B_ImportAllAcres.Click += new System.EventHandler(this.B_ImportAllAcres_Click); + B_ImportAllAcres.Name = "B_ImportAllAcres"; + B_ImportAllAcres.Size = new System.Drawing.Size(144, 22); + B_ImportAllAcres.Text = "Import All Acres"; + B_ImportAllAcres.Click += B_ImportAllAcres_Click; // // Tab_Building // - this.Tab_Building.Controls.Add(this.B_DumpLoadBuildings); - this.Tab_Building.Controls.Add(this.L_Bit); - this.Tab_Building.Controls.Add(this.NUD_Bit); - this.Tab_Building.Controls.Add(this.L_BuildingType); - this.Tab_Building.Controls.Add(this.NUD_BuildingType); - this.Tab_Building.Controls.Add(this.NUD_UniqueID); - this.Tab_Building.Controls.Add(this.L_BuildingX); - this.Tab_Building.Controls.Add(this.L_BuildingUniqueID); - this.Tab_Building.Controls.Add(this.NUD_X); - this.Tab_Building.Controls.Add(this.NUD_TypeArg); - this.Tab_Building.Controls.Add(this.L_BuildingY); - this.Tab_Building.Controls.Add(this.L_BuildingStructureArg); - this.Tab_Building.Controls.Add(this.NUD_Y); - this.Tab_Building.Controls.Add(this.NUD_Type); - this.Tab_Building.Controls.Add(this.L_BuildingRotation); - this.Tab_Building.Controls.Add(this.L_BuildingStructureType); - this.Tab_Building.Controls.Add(this.NUD_Angle); - this.Tab_Building.Controls.Add(this.L_PlazaX); - this.Tab_Building.Controls.Add(this.NUD_PlazaX); - this.Tab_Building.Controls.Add(this.L_PlazaY); - this.Tab_Building.Controls.Add(this.NUD_PlazaY); - this.Tab_Building.Controls.Add(this.B_Help); - this.Tab_Building.Controls.Add(this.LB_Items); - this.Tab_Building.Location = new System.Drawing.Point(4, 22); - this.Tab_Building.Name = "Tab_Building"; - this.Tab_Building.Padding = new System.Windows.Forms.Padding(3); - this.Tab_Building.Size = new System.Drawing.Size(244, 458); - this.Tab_Building.TabIndex = 1; - this.Tab_Building.Text = "Buildings"; - this.Tab_Building.UseVisualStyleBackColor = true; + Tab_Building.Controls.Add(B_DumpLoadBuildings); + Tab_Building.Controls.Add(L_Bit); + Tab_Building.Controls.Add(NUD_Bit); + Tab_Building.Controls.Add(L_BuildingType); + Tab_Building.Controls.Add(NUD_BuildingType); + Tab_Building.Controls.Add(NUD_UniqueID); + Tab_Building.Controls.Add(L_BuildingX); + Tab_Building.Controls.Add(L_BuildingUniqueID); + Tab_Building.Controls.Add(NUD_X); + Tab_Building.Controls.Add(NUD_TypeArg); + Tab_Building.Controls.Add(L_BuildingY); + Tab_Building.Controls.Add(L_BuildingStructureArg); + Tab_Building.Controls.Add(NUD_Y); + Tab_Building.Controls.Add(NUD_Type); + Tab_Building.Controls.Add(L_BuildingRotation); + Tab_Building.Controls.Add(L_BuildingStructureType); + Tab_Building.Controls.Add(NUD_Angle); + Tab_Building.Controls.Add(L_PlazaX); + Tab_Building.Controls.Add(NUD_PlazaX); + Tab_Building.Controls.Add(L_PlazaY); + Tab_Building.Controls.Add(NUD_PlazaY); + Tab_Building.Controls.Add(B_Help); + Tab_Building.Controls.Add(LB_Items); + Tab_Building.Location = new System.Drawing.Point(4, 26); + Tab_Building.Margin = new System.Windows.Forms.Padding(4); + Tab_Building.Name = "Tab_Building"; + Tab_Building.Padding = new System.Windows.Forms.Padding(4); + Tab_Building.Size = new System.Drawing.Size(286, 603); + Tab_Building.TabIndex = 1; + Tab_Building.Text = "Buildings"; + Tab_Building.UseVisualStyleBackColor = true; // // B_DumpLoadBuildings // - this.B_DumpLoadBuildings.ContextMenuStrip = this.CM_DLBuilding; - this.B_DumpLoadBuildings.Location = new System.Drawing.Point(6, 413); - this.B_DumpLoadBuildings.Name = "B_DumpLoadBuildings"; - this.B_DumpLoadBuildings.Size = new System.Drawing.Size(112, 40); - this.B_DumpLoadBuildings.TabIndex = 132; - this.B_DumpLoadBuildings.Text = "Dump/Import"; - this.B_DumpLoadBuildings.UseVisualStyleBackColor = true; - this.B_DumpLoadBuildings.Click += new System.EventHandler(this.B_DumpLoadBuildings_Click); + B_DumpLoadBuildings.ContextMenuStrip = CM_DLBuilding; + B_DumpLoadBuildings.Location = new System.Drawing.Point(7, 540); + B_DumpLoadBuildings.Margin = new System.Windows.Forms.Padding(4); + B_DumpLoadBuildings.Name = "B_DumpLoadBuildings"; + B_DumpLoadBuildings.Size = new System.Drawing.Size(131, 52); + B_DumpLoadBuildings.TabIndex = 132; + B_DumpLoadBuildings.Text = "Dump/Import"; + B_DumpLoadBuildings.UseVisualStyleBackColor = true; + B_DumpLoadBuildings.Click += B_DumpLoadBuildings_Click; // // CM_DLBuilding // - this.CM_DLBuilding.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.B_DumpBuildings, - this.B_ImportBuildings}); - this.CM_DLBuilding.Name = "CM_Picture"; - this.CM_DLBuilding.ShowImageMargin = false; - this.CM_DLBuilding.Size = new System.Drawing.Size(138, 48); + CM_DLBuilding.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { B_DumpBuildings, B_ImportBuildings }); + CM_DLBuilding.Name = "CM_Picture"; + CM_DLBuilding.ShowImageMargin = false; + CM_DLBuilding.Size = new System.Drawing.Size(147, 48); // // B_DumpBuildings // - this.B_DumpBuildings.Name = "B_DumpBuildings"; - this.B_DumpBuildings.Size = new System.Drawing.Size(137, 22); - this.B_DumpBuildings.Text = "Dump Buildings"; - this.B_DumpBuildings.Click += new System.EventHandler(this.B_DumpBuildings_Click); + B_DumpBuildings.Name = "B_DumpBuildings"; + B_DumpBuildings.Size = new System.Drawing.Size(146, 22); + B_DumpBuildings.Text = "Dump Buildings"; + B_DumpBuildings.Click += B_DumpBuildings_Click; // // B_ImportBuildings // - this.B_ImportBuildings.Name = "B_ImportBuildings"; - this.B_ImportBuildings.Size = new System.Drawing.Size(137, 22); - this.B_ImportBuildings.Text = "Import Buildings"; - this.B_ImportBuildings.Click += new System.EventHandler(this.B_ImportBuildings_Click); + B_ImportBuildings.Name = "B_ImportBuildings"; + B_ImportBuildings.Size = new System.Drawing.Size(146, 22); + B_ImportBuildings.Text = "Import Buildings"; + B_ImportBuildings.Click += B_ImportBuildings_Click; // // L_Bit // - this.L_Bit.Location = new System.Drawing.Point(36, 303); - this.L_Bit.Name = "L_Bit"; - this.L_Bit.Size = new System.Drawing.Size(100, 18); - this.L_Bit.TabIndex = 130; - this.L_Bit.Text = "Bit:"; - this.L_Bit.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + L_Bit.Location = new System.Drawing.Point(42, 396); + L_Bit.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_Bit.Name = "L_Bit"; + L_Bit.Size = new System.Drawing.Size(117, 24); + L_Bit.TabIndex = 130; + L_Bit.Text = "Bit:"; + L_Bit.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // NUD_Bit // - this.NUD_Bit.Location = new System.Drawing.Point(142, 304); - this.NUD_Bit.Maximum = new decimal(new int[] { - 200, - 0, - 0, - 0}); - this.NUD_Bit.Minimum = new decimal(new int[] { - 200, - 0, - 0, - -2147483648}); - this.NUD_Bit.Name = "NUD_Bit"; - this.NUD_Bit.Size = new System.Drawing.Size(69, 20); - this.NUD_Bit.TabIndex = 131; - this.NUD_Bit.ValueChanged += new System.EventHandler(this.NUD_BuildingType_ValueChanged); + NUD_Bit.Location = new System.Drawing.Point(166, 398); + NUD_Bit.Margin = new System.Windows.Forms.Padding(4); + NUD_Bit.Maximum = new decimal(new int[] { 200, 0, 0, 0 }); + NUD_Bit.Minimum = new decimal(new int[] { 200, 0, 0, int.MinValue }); + NUD_Bit.Name = "NUD_Bit"; + NUD_Bit.Size = new System.Drawing.Size(80, 25); + NUD_Bit.TabIndex = 131; + NUD_Bit.ValueChanged += NUD_BuildingType_ValueChanged; // // L_BuildingType // - this.L_BuildingType.Location = new System.Drawing.Point(36, 236); - this.L_BuildingType.Name = "L_BuildingType"; - this.L_BuildingType.Size = new System.Drawing.Size(100, 18); - this.L_BuildingType.TabIndex = 116; - this.L_BuildingType.Text = "Building Type:"; - this.L_BuildingType.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + L_BuildingType.Location = new System.Drawing.Point(42, 309); + L_BuildingType.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_BuildingType.Name = "L_BuildingType"; + L_BuildingType.Size = new System.Drawing.Size(117, 24); + L_BuildingType.TabIndex = 116; + L_BuildingType.Text = "Building Type:"; + L_BuildingType.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // NUD_BuildingType // - this.NUD_BuildingType.Location = new System.Drawing.Point(142, 237); - this.NUD_BuildingType.Name = "NUD_BuildingType"; - this.NUD_BuildingType.Size = new System.Drawing.Size(69, 20); - this.NUD_BuildingType.TabIndex = 117; - this.NUD_BuildingType.ValueChanged += new System.EventHandler(this.NUD_BuildingType_ValueChanged); + NUD_BuildingType.Location = new System.Drawing.Point(166, 310); + NUD_BuildingType.Margin = new System.Windows.Forms.Padding(4); + NUD_BuildingType.Name = "NUD_BuildingType"; + NUD_BuildingType.Size = new System.Drawing.Size(80, 25); + NUD_BuildingType.TabIndex = 117; + NUD_BuildingType.ValueChanged += NUD_BuildingType_ValueChanged; // // NUD_UniqueID // - this.NUD_UniqueID.Location = new System.Drawing.Point(142, 371); - this.NUD_UniqueID.Maximum = new decimal(new int[] { - 65535, - 0, - 0, - 0}); - this.NUD_UniqueID.Name = "NUD_UniqueID"; - this.NUD_UniqueID.Size = new System.Drawing.Size(69, 20); - this.NUD_UniqueID.TabIndex = 129; - this.NUD_UniqueID.ValueChanged += new System.EventHandler(this.NUD_BuildingType_ValueChanged); + NUD_UniqueID.Location = new System.Drawing.Point(166, 485); + NUD_UniqueID.Margin = new System.Windows.Forms.Padding(4); + NUD_UniqueID.Maximum = new decimal(new int[] { 65535, 0, 0, 0 }); + NUD_UniqueID.Name = "NUD_UniqueID"; + NUD_UniqueID.Size = new System.Drawing.Size(80, 25); + NUD_UniqueID.TabIndex = 129; + NUD_UniqueID.ValueChanged += NUD_BuildingType_ValueChanged; // // L_BuildingX // - this.L_BuildingX.Location = new System.Drawing.Point(65, 257); - this.L_BuildingX.Name = "L_BuildingX"; - this.L_BuildingX.Size = new System.Drawing.Size(20, 18); - this.L_BuildingX.TabIndex = 118; - this.L_BuildingX.Text = "X:"; - this.L_BuildingX.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + L_BuildingX.Location = new System.Drawing.Point(76, 336); + L_BuildingX.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_BuildingX.Name = "L_BuildingX"; + L_BuildingX.Size = new System.Drawing.Size(23, 24); + L_BuildingX.TabIndex = 118; + L_BuildingX.Text = "X:"; + L_BuildingX.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // L_BuildingUniqueID // - this.L_BuildingUniqueID.Location = new System.Drawing.Point(36, 370); - this.L_BuildingUniqueID.Name = "L_BuildingUniqueID"; - this.L_BuildingUniqueID.Size = new System.Drawing.Size(100, 18); - this.L_BuildingUniqueID.TabIndex = 128; - this.L_BuildingUniqueID.Text = "UniqueID:"; - this.L_BuildingUniqueID.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + L_BuildingUniqueID.Location = new System.Drawing.Point(42, 484); + L_BuildingUniqueID.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_BuildingUniqueID.Name = "L_BuildingUniqueID"; + L_BuildingUniqueID.Size = new System.Drawing.Size(117, 24); + L_BuildingUniqueID.TabIndex = 128; + L_BuildingUniqueID.Text = "UniqueID:"; + L_BuildingUniqueID.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // NUD_X // - this.NUD_X.Location = new System.Drawing.Point(91, 258); - this.NUD_X.Maximum = new decimal(new int[] { - 255, - 0, - 0, - 0}); - this.NUD_X.Name = "NUD_X"; - this.NUD_X.Size = new System.Drawing.Size(45, 20); - this.NUD_X.TabIndex = 119; - this.NUD_X.ValueChanged += new System.EventHandler(this.NUD_BuildingType_ValueChanged); + NUD_X.Location = new System.Drawing.Point(106, 337); + NUD_X.Margin = new System.Windows.Forms.Padding(4); + NUD_X.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_X.Name = "NUD_X"; + NUD_X.Size = new System.Drawing.Size(52, 25); + NUD_X.TabIndex = 119; + NUD_X.ValueChanged += NUD_BuildingType_ValueChanged; // // NUD_TypeArg // - this.NUD_TypeArg.Location = new System.Drawing.Point(142, 350); - this.NUD_TypeArg.Maximum = new decimal(new int[] { - 65535, - 0, - 0, - 0}); - this.NUD_TypeArg.Name = "NUD_TypeArg"; - this.NUD_TypeArg.Size = new System.Drawing.Size(69, 20); - this.NUD_TypeArg.TabIndex = 127; - this.NUD_TypeArg.ValueChanged += new System.EventHandler(this.NUD_BuildingType_ValueChanged); + NUD_TypeArg.Location = new System.Drawing.Point(166, 458); + NUD_TypeArg.Margin = new System.Windows.Forms.Padding(4); + NUD_TypeArg.Maximum = new decimal(new int[] { 65535, 0, 0, 0 }); + NUD_TypeArg.Name = "NUD_TypeArg"; + NUD_TypeArg.Size = new System.Drawing.Size(80, 25); + NUD_TypeArg.TabIndex = 127; + NUD_TypeArg.ValueChanged += NUD_BuildingType_ValueChanged; // // L_BuildingY // - this.L_BuildingY.Location = new System.Drawing.Point(137, 257); - this.L_BuildingY.Name = "L_BuildingY"; - this.L_BuildingY.Size = new System.Drawing.Size(23, 18); - this.L_BuildingY.TabIndex = 120; - this.L_BuildingY.Text = "Y:"; - this.L_BuildingY.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + L_BuildingY.Location = new System.Drawing.Point(160, 336); + L_BuildingY.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_BuildingY.Name = "L_BuildingY"; + L_BuildingY.Size = new System.Drawing.Size(27, 24); + L_BuildingY.TabIndex = 120; + L_BuildingY.Text = "Y:"; + L_BuildingY.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // L_BuildingStructureArg // - this.L_BuildingStructureArg.Location = new System.Drawing.Point(36, 349); - this.L_BuildingStructureArg.Name = "L_BuildingStructureArg"; - this.L_BuildingStructureArg.Size = new System.Drawing.Size(100, 18); - this.L_BuildingStructureArg.TabIndex = 126; - this.L_BuildingStructureArg.Text = "TypeArg:"; - this.L_BuildingStructureArg.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + L_BuildingStructureArg.Location = new System.Drawing.Point(42, 456); + L_BuildingStructureArg.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_BuildingStructureArg.Name = "L_BuildingStructureArg"; + L_BuildingStructureArg.Size = new System.Drawing.Size(117, 24); + L_BuildingStructureArg.TabIndex = 126; + L_BuildingStructureArg.Text = "TypeArg:"; + L_BuildingStructureArg.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // NUD_Y // - this.NUD_Y.Location = new System.Drawing.Point(166, 258); - this.NUD_Y.Maximum = new decimal(new int[] { - 255, - 0, - 0, - 0}); - this.NUD_Y.Name = "NUD_Y"; - this.NUD_Y.Size = new System.Drawing.Size(45, 20); - this.NUD_Y.TabIndex = 121; - this.NUD_Y.ValueChanged += new System.EventHandler(this.NUD_BuildingType_ValueChanged); + NUD_Y.Location = new System.Drawing.Point(194, 337); + NUD_Y.Margin = new System.Windows.Forms.Padding(4); + NUD_Y.Maximum = new decimal(new int[] { 255, 0, 0, 0 }); + NUD_Y.Name = "NUD_Y"; + NUD_Y.Size = new System.Drawing.Size(52, 25); + NUD_Y.TabIndex = 121; + NUD_Y.ValueChanged += NUD_BuildingType_ValueChanged; // // NUD_Type // - this.NUD_Type.Location = new System.Drawing.Point(142, 329); - this.NUD_Type.Maximum = new decimal(new int[] { - 65535, - 0, - 0, - 0}); - this.NUD_Type.Name = "NUD_Type"; - this.NUD_Type.Size = new System.Drawing.Size(69, 20); - this.NUD_Type.TabIndex = 125; - this.NUD_Type.ValueChanged += new System.EventHandler(this.NUD_BuildingType_ValueChanged); + NUD_Type.Location = new System.Drawing.Point(166, 430); + NUD_Type.Margin = new System.Windows.Forms.Padding(4); + NUD_Type.Maximum = new decimal(new int[] { 65535, 0, 0, 0 }); + NUD_Type.Name = "NUD_Type"; + NUD_Type.Size = new System.Drawing.Size(80, 25); + NUD_Type.TabIndex = 125; + NUD_Type.ValueChanged += NUD_BuildingType_ValueChanged; // // L_BuildingRotation // - this.L_BuildingRotation.Location = new System.Drawing.Point(36, 282); - this.L_BuildingRotation.Name = "L_BuildingRotation"; - this.L_BuildingRotation.Size = new System.Drawing.Size(100, 18); - this.L_BuildingRotation.TabIndex = 122; - this.L_BuildingRotation.Text = "Angle:"; - this.L_BuildingRotation.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + L_BuildingRotation.Location = new System.Drawing.Point(42, 369); + L_BuildingRotation.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_BuildingRotation.Name = "L_BuildingRotation"; + L_BuildingRotation.Size = new System.Drawing.Size(117, 24); + L_BuildingRotation.TabIndex = 122; + L_BuildingRotation.Text = "Angle:"; + L_BuildingRotation.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // L_BuildingStructureType // - this.L_BuildingStructureType.Location = new System.Drawing.Point(36, 328); - this.L_BuildingStructureType.Name = "L_BuildingStructureType"; - this.L_BuildingStructureType.Size = new System.Drawing.Size(100, 18); - this.L_BuildingStructureType.TabIndex = 124; - this.L_BuildingStructureType.Text = "Type:"; - this.L_BuildingStructureType.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + L_BuildingStructureType.Location = new System.Drawing.Point(42, 429); + L_BuildingStructureType.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_BuildingStructureType.Name = "L_BuildingStructureType"; + L_BuildingStructureType.Size = new System.Drawing.Size(117, 24); + L_BuildingStructureType.TabIndex = 124; + L_BuildingStructureType.Text = "Type:"; + L_BuildingStructureType.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // NUD_Angle // - this.NUD_Angle.Location = new System.Drawing.Point(142, 283); - this.NUD_Angle.Maximum = new decimal(new int[] { - 65535, - 0, - 0, - 0}); - this.NUD_Angle.Name = "NUD_Angle"; - this.NUD_Angle.Size = new System.Drawing.Size(69, 20); - this.NUD_Angle.TabIndex = 123; - this.NUD_Angle.ValueChanged += new System.EventHandler(this.NUD_BuildingType_ValueChanged); + NUD_Angle.Location = new System.Drawing.Point(166, 370); + NUD_Angle.Margin = new System.Windows.Forms.Padding(4); + NUD_Angle.Maximum = new decimal(new int[] { 65535, 0, 0, 0 }); + NUD_Angle.Name = "NUD_Angle"; + NUD_Angle.Size = new System.Drawing.Size(80, 25); + NUD_Angle.TabIndex = 123; + NUD_Angle.ValueChanged += NUD_BuildingType_ValueChanged; // // L_PlazaX // - this.L_PlazaX.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.L_PlazaX.Location = new System.Drawing.Point(65, 2); - this.L_PlazaX.Name = "L_PlazaX"; - this.L_PlazaX.Size = new System.Drawing.Size(62, 20); - this.L_PlazaX.TabIndex = 115; - this.L_PlazaX.Text = "Plaza X:"; - this.L_PlazaX.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + L_PlazaX.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + L_PlazaX.Location = new System.Drawing.Point(76, 3); + L_PlazaX.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_PlazaX.Name = "L_PlazaX"; + L_PlazaX.Size = new System.Drawing.Size(72, 26); + L_PlazaX.TabIndex = 115; + L_PlazaX.Text = "Plaza X:"; + L_PlazaX.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // NUD_PlazaX // - this.NUD_PlazaX.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.NUD_PlazaX.Location = new System.Drawing.Point(128, 3); - this.NUD_PlazaX.Maximum = new decimal(new int[] { - 1024, - 0, - 0, - 0}); - this.NUD_PlazaX.Name = "NUD_PlazaX"; - this.NUD_PlazaX.Size = new System.Drawing.Size(39, 20); - this.NUD_PlazaX.TabIndex = 114; - this.NUD_PlazaX.Value = new decimal(new int[] { - 555, - 0, - 0, - 0}); - this.NUD_PlazaX.ValueChanged += new System.EventHandler(this.NUD_PlazaX_ValueChanged); + NUD_PlazaX.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + NUD_PlazaX.Location = new System.Drawing.Point(149, 4); + NUD_PlazaX.Margin = new System.Windows.Forms.Padding(4); + NUD_PlazaX.Maximum = new decimal(new int[] { 1024, 0, 0, 0 }); + NUD_PlazaX.Name = "NUD_PlazaX"; + NUD_PlazaX.Size = new System.Drawing.Size(46, 25); + NUD_PlazaX.TabIndex = 114; + NUD_PlazaX.Value = new decimal(new int[] { 555, 0, 0, 0 }); + NUD_PlazaX.ValueChanged += NUD_PlazaX_ValueChanged; // // L_PlazaY // - this.L_PlazaY.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.L_PlazaY.Location = new System.Drawing.Point(65, 23); - this.L_PlazaY.Name = "L_PlazaY"; - this.L_PlazaY.Size = new System.Drawing.Size(62, 20); - this.L_PlazaY.TabIndex = 113; - this.L_PlazaY.Text = "Plaza Y:"; - this.L_PlazaY.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + L_PlazaY.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + L_PlazaY.Location = new System.Drawing.Point(76, 30); + L_PlazaY.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_PlazaY.Name = "L_PlazaY"; + L_PlazaY.Size = new System.Drawing.Size(72, 26); + L_PlazaY.TabIndex = 113; + L_PlazaY.Text = "Plaza Y:"; + L_PlazaY.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // NUD_PlazaY // - this.NUD_PlazaY.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.NUD_PlazaY.Location = new System.Drawing.Point(128, 24); - this.NUD_PlazaY.Maximum = new decimal(new int[] { - 1024, - 0, - 0, - 0}); - this.NUD_PlazaY.Name = "NUD_PlazaY"; - this.NUD_PlazaY.Size = new System.Drawing.Size(39, 20); - this.NUD_PlazaY.TabIndex = 112; - this.NUD_PlazaY.Value = new decimal(new int[] { - 555, - 0, - 0, - 0}); - this.NUD_PlazaY.ValueChanged += new System.EventHandler(this.NUD_PlazaY_ValueChanged); + NUD_PlazaY.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + NUD_PlazaY.Location = new System.Drawing.Point(149, 31); + NUD_PlazaY.Margin = new System.Windows.Forms.Padding(4); + NUD_PlazaY.Maximum = new decimal(new int[] { 1024, 0, 0, 0 }); + NUD_PlazaY.Name = "NUD_PlazaY"; + NUD_PlazaY.Size = new System.Drawing.Size(46, 25); + NUD_PlazaY.TabIndex = 112; + NUD_PlazaY.Value = new decimal(new int[] { 555, 0, 0, 0 }); + NUD_PlazaY.ValueChanged += NUD_PlazaY_ValueChanged; // // B_Help // - this.B_Help.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.B_Help.Location = new System.Drawing.Point(126, 413); - this.B_Help.Name = "B_Help"; - this.B_Help.Size = new System.Drawing.Size(112, 40); - this.B_Help.TabIndex = 111; - this.B_Help.Text = "Help"; - this.B_Help.UseVisualStyleBackColor = true; - this.B_Help.Click += new System.EventHandler(this.B_Help_Click); + B_Help.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + B_Help.Location = new System.Drawing.Point(147, 540); + B_Help.Margin = new System.Windows.Forms.Padding(4); + B_Help.Name = "B_Help"; + B_Help.Size = new System.Drawing.Size(131, 52); + B_Help.TabIndex = 111; + B_Help.Text = "Help"; + B_Help.UseVisualStyleBackColor = true; + B_Help.Click += B_Help_Click; // // LB_Items // - this.LB_Items.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.LB_Items.FormattingEnabled = true; - this.LB_Items.Location = new System.Drawing.Point(6, 45); - this.LB_Items.Name = "LB_Items"; - this.LB_Items.Size = new System.Drawing.Size(232, 186); - this.LB_Items.TabIndex = 109; - this.LB_Items.SelectedIndexChanged += new System.EventHandler(this.LB_Items_SelectedIndexChanged); + LB_Items.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + LB_Items.FormattingEnabled = true; + LB_Items.Location = new System.Drawing.Point(7, 59); + LB_Items.Margin = new System.Windows.Forms.Padding(4); + LB_Items.Name = "LB_Items"; + LB_Items.Size = new System.Drawing.Size(270, 242); + LB_Items.TabIndex = 109; + LB_Items.SelectedIndexChanged += LB_Items_SelectedIndexChanged; // // Tab_Terrain // - this.Tab_Terrain.Controls.Add(this.B_TerrainBrush); - this.Tab_Terrain.Controls.Add(this.L_TerrainTileLabelTransparency); - this.Tab_Terrain.Controls.Add(this.TR_Terrain); - this.Tab_Terrain.Controls.Add(this.TR_BuildingTransparency); - this.Tab_Terrain.Controls.Add(this.L_BuildingTransparency); - this.Tab_Terrain.Controls.Add(this.PG_TerrainTile); - this.Tab_Terrain.Controls.Add(this.L_FieldItemTransparency); - this.Tab_Terrain.Controls.Add(this.TR_Transparency); - this.Tab_Terrain.Controls.Add(this.B_DumpLoadTerrain); - this.Tab_Terrain.Controls.Add(this.B_ModifyAllTerrain); - this.Tab_Terrain.Location = new System.Drawing.Point(4, 22); - this.Tab_Terrain.Name = "Tab_Terrain"; - this.Tab_Terrain.Size = new System.Drawing.Size(244, 458); - this.Tab_Terrain.TabIndex = 2; - this.Tab_Terrain.Text = "Terrain"; - this.Tab_Terrain.UseVisualStyleBackColor = true; + Tab_Terrain.Controls.Add(B_TerrainBrush); + Tab_Terrain.Controls.Add(L_TerrainTileLabelTransparency); + Tab_Terrain.Controls.Add(TR_Terrain); + Tab_Terrain.Controls.Add(TR_BuildingTransparency); + Tab_Terrain.Controls.Add(L_BuildingTransparency); + Tab_Terrain.Controls.Add(PG_TerrainTile); + Tab_Terrain.Controls.Add(L_FieldItemTransparency); + Tab_Terrain.Controls.Add(TR_Transparency); + Tab_Terrain.Controls.Add(B_DumpLoadTerrain); + Tab_Terrain.Controls.Add(B_ModifyAllTerrain); + Tab_Terrain.Location = new System.Drawing.Point(4, 26); + Tab_Terrain.Margin = new System.Windows.Forms.Padding(4); + Tab_Terrain.Name = "Tab_Terrain"; + Tab_Terrain.Size = new System.Drawing.Size(286, 603); + Tab_Terrain.TabIndex = 2; + Tab_Terrain.Text = "Terrain"; + Tab_Terrain.UseVisualStyleBackColor = true; // // B_TerrainBrush // - this.B_TerrainBrush.Location = new System.Drawing.Point(155, 413); - this.B_TerrainBrush.Name = "B_TerrainBrush"; - this.B_TerrainBrush.Size = new System.Drawing.Size(83, 40); - this.B_TerrainBrush.TabIndex = 48; - this.B_TerrainBrush.Text = "Terrain brushes"; - this.B_TerrainBrush.UseVisualStyleBackColor = true; - this.B_TerrainBrush.Click += new System.EventHandler(this.B_TerrainBrush_Click); + B_TerrainBrush.Location = new System.Drawing.Point(181, 540); + B_TerrainBrush.Margin = new System.Windows.Forms.Padding(4); + B_TerrainBrush.Name = "B_TerrainBrush"; + B_TerrainBrush.Size = new System.Drawing.Size(97, 52); + B_TerrainBrush.TabIndex = 48; + B_TerrainBrush.Text = "Terrain brushes"; + B_TerrainBrush.UseVisualStyleBackColor = true; + B_TerrainBrush.Click += B_TerrainBrush_Click; // // L_TerrainTileLabelTransparency // - this.L_TerrainTileLabelTransparency.AutoSize = true; - this.L_TerrainTileLabelTransparency.Location = new System.Drawing.Point(8, 272); - this.L_TerrainTileLabelTransparency.Name = "L_TerrainTileLabelTransparency"; - this.L_TerrainTileLabelTransparency.Size = new System.Drawing.Size(157, 13); - this.L_TerrainTileLabelTransparency.TabIndex = 46; - this.L_TerrainTileLabelTransparency.Text = "Terrain Tile Label Transparency"; + L_TerrainTileLabelTransparency.AutoSize = true; + L_TerrainTileLabelTransparency.Location = new System.Drawing.Point(9, 356); + L_TerrainTileLabelTransparency.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_TerrainTileLabelTransparency.Name = "L_TerrainTileLabelTransparency"; + L_TerrainTileLabelTransparency.Size = new System.Drawing.Size(188, 17); + L_TerrainTileLabelTransparency.TabIndex = 46; + L_TerrainTileLabelTransparency.Text = "Terrain Tile Label Transparency"; // // TR_Terrain // - this.TR_Terrain.AutoSize = false; - this.TR_Terrain.Location = new System.Drawing.Point(3, 285); - this.TR_Terrain.Maximum = 255; - this.TR_Terrain.Name = "TR_Terrain"; - this.TR_Terrain.Size = new System.Drawing.Size(237, 28); - this.TR_Terrain.TabIndex = 45; - this.TR_Terrain.TickFrequency = 32; - this.TR_Terrain.Scroll += new System.EventHandler(this.TR_Terrain_Scroll); + TR_Terrain.AutoSize = false; + TR_Terrain.Location = new System.Drawing.Point(4, 373); + TR_Terrain.Margin = new System.Windows.Forms.Padding(4); + TR_Terrain.Maximum = 255; + TR_Terrain.Name = "TR_Terrain"; + TR_Terrain.Size = new System.Drawing.Size(276, 37); + TR_Terrain.TabIndex = 45; + TR_Terrain.TickFrequency = 32; + TR_Terrain.Scroll += TR_Terrain_Scroll; // // TR_BuildingTransparency // - this.TR_BuildingTransparency.AutoSize = false; - this.TR_BuildingTransparency.Location = new System.Drawing.Point(3, 381); - this.TR_BuildingTransparency.Maximum = 255; - this.TR_BuildingTransparency.Name = "TR_BuildingTransparency"; - this.TR_BuildingTransparency.Size = new System.Drawing.Size(237, 28); - this.TR_BuildingTransparency.TabIndex = 43; - this.TR_BuildingTransparency.TickFrequency = 16; - this.TR_BuildingTransparency.Value = 255; - this.TR_BuildingTransparency.Scroll += new System.EventHandler(this.TR_BuildingTransparency_Scroll); + TR_BuildingTransparency.AutoSize = false; + TR_BuildingTransparency.Location = new System.Drawing.Point(4, 498); + TR_BuildingTransparency.Margin = new System.Windows.Forms.Padding(4); + TR_BuildingTransparency.Maximum = 255; + TR_BuildingTransparency.Name = "TR_BuildingTransparency"; + TR_BuildingTransparency.Size = new System.Drawing.Size(276, 37); + TR_BuildingTransparency.TabIndex = 43; + TR_BuildingTransparency.TickFrequency = 16; + TR_BuildingTransparency.Value = 255; + TR_BuildingTransparency.Scroll += TR_BuildingTransparency_Scroll; // // L_BuildingTransparency // - this.L_BuildingTransparency.AutoSize = true; - this.L_BuildingTransparency.Location = new System.Drawing.Point(8, 365); - this.L_BuildingTransparency.Name = "L_BuildingTransparency"; - this.L_BuildingTransparency.Size = new System.Drawing.Size(112, 13); - this.L_BuildingTransparency.TabIndex = 44; - this.L_BuildingTransparency.Text = "Building Transparency"; + L_BuildingTransparency.AutoSize = true; + L_BuildingTransparency.Location = new System.Drawing.Point(9, 477); + L_BuildingTransparency.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_BuildingTransparency.Name = "L_BuildingTransparency"; + L_BuildingTransparency.Size = new System.Drawing.Size(135, 17); + L_BuildingTransparency.TabIndex = 44; + L_BuildingTransparency.Text = "Building Transparency"; // // PG_TerrainTile // - this.PG_TerrainTile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.PG_TerrainTile.Location = new System.Drawing.Point(3, 3); - this.PG_TerrainTile.Name = "PG_TerrainTile"; - this.PG_TerrainTile.PropertySort = System.Windows.Forms.PropertySort.Categorized; - this.PG_TerrainTile.Size = new System.Drawing.Size(238, 266); - this.PG_TerrainTile.TabIndex = 41; - this.PG_TerrainTile.ToolbarVisible = false; + PG_TerrainTile.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + PG_TerrainTile.BackColor = System.Drawing.SystemColors.Control; + PG_TerrainTile.Location = new System.Drawing.Point(4, 4); + PG_TerrainTile.Margin = new System.Windows.Forms.Padding(4); + PG_TerrainTile.Name = "PG_TerrainTile"; + PG_TerrainTile.PropertySort = System.Windows.Forms.PropertySort.Categorized; + PG_TerrainTile.Size = new System.Drawing.Size(278, 348); + PG_TerrainTile.TabIndex = 41; + PG_TerrainTile.ToolbarVisible = false; // // L_FieldItemTransparency // - this.L_FieldItemTransparency.AutoSize = true; - this.L_FieldItemTransparency.Location = new System.Drawing.Point(8, 316); - this.L_FieldItemTransparency.Name = "L_FieldItemTransparency"; - this.L_FieldItemTransparency.Size = new System.Drawing.Size(120, 13); - this.L_FieldItemTransparency.TabIndex = 42; - this.L_FieldItemTransparency.Text = "Field Item Transparency"; + L_FieldItemTransparency.AutoSize = true; + L_FieldItemTransparency.Location = new System.Drawing.Point(9, 413); + L_FieldItemTransparency.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_FieldItemTransparency.Name = "L_FieldItemTransparency"; + L_FieldItemTransparency.Size = new System.Drawing.Size(145, 17); + L_FieldItemTransparency.TabIndex = 42; + L_FieldItemTransparency.Text = "Field Item Transparency"; // // B_DumpLoadTerrain // - this.B_DumpLoadTerrain.Location = new System.Drawing.Point(6, 413); - this.B_DumpLoadTerrain.Name = "B_DumpLoadTerrain"; - this.B_DumpLoadTerrain.Size = new System.Drawing.Size(49, 40); - this.B_DumpLoadTerrain.TabIndex = 40; - this.B_DumpLoadTerrain.Text = "Dump/Import"; - this.B_DumpLoadTerrain.UseVisualStyleBackColor = true; - this.B_DumpLoadTerrain.Click += new System.EventHandler(this.B_DumpLoadTerrain_Click); + B_DumpLoadTerrain.Location = new System.Drawing.Point(7, 540); + B_DumpLoadTerrain.Margin = new System.Windows.Forms.Padding(4); + B_DumpLoadTerrain.Name = "B_DumpLoadTerrain"; + B_DumpLoadTerrain.Size = new System.Drawing.Size(57, 52); + B_DumpLoadTerrain.TabIndex = 40; + B_DumpLoadTerrain.Text = "Dump/Import"; + B_DumpLoadTerrain.UseVisualStyleBackColor = true; + B_DumpLoadTerrain.Click += B_DumpLoadTerrain_Click; // // B_ModifyAllTerrain // - this.B_ModifyAllTerrain.Location = new System.Drawing.Point(60, 413); - this.B_ModifyAllTerrain.Name = "B_ModifyAllTerrain"; - this.B_ModifyAllTerrain.Size = new System.Drawing.Size(89, 40); - this.B_ModifyAllTerrain.TabIndex = 39; - this.B_ModifyAllTerrain.Text = "Modify All..."; - this.B_ModifyAllTerrain.UseVisualStyleBackColor = true; - this.B_ModifyAllTerrain.Click += new System.EventHandler(this.B_ModifyAllTerrain_Click); + B_ModifyAllTerrain.Location = new System.Drawing.Point(70, 540); + B_ModifyAllTerrain.Margin = new System.Windows.Forms.Padding(4); + B_ModifyAllTerrain.Name = "B_ModifyAllTerrain"; + B_ModifyAllTerrain.Size = new System.Drawing.Size(104, 52); + B_ModifyAllTerrain.TabIndex = 39; + B_ModifyAllTerrain.Text = "Modify All..."; + B_ModifyAllTerrain.UseVisualStyleBackColor = true; + B_ModifyAllTerrain.Click += B_ModifyAllTerrain_Click; // // Tab_Acres // - this.Tab_Acres.Controls.Add(this.NUD_MapAcreTemplateField); - this.Tab_Acres.Controls.Add(this.L_MapAcreTemplateField); - this.Tab_Acres.Controls.Add(this.L_MapAcreTemplateOutside); - this.Tab_Acres.Controls.Add(this.NUD_MapAcreTemplateOutside); - this.Tab_Acres.Controls.Add(this.CB_MapAcreSelect); - this.Tab_Acres.Controls.Add(this.B_DumpLoadAcres); - this.Tab_Acres.Controls.Add(this.L_MapAcre); - this.Tab_Acres.Controls.Add(this.CB_MapAcre); - this.Tab_Acres.Location = new System.Drawing.Point(4, 22); - this.Tab_Acres.Name = "Tab_Acres"; - this.Tab_Acres.Padding = new System.Windows.Forms.Padding(3); - this.Tab_Acres.Size = new System.Drawing.Size(244, 458); - this.Tab_Acres.TabIndex = 3; - this.Tab_Acres.Text = "Acres"; - this.Tab_Acres.UseVisualStyleBackColor = true; + Tab_Acres.Controls.Add(NUD_MapAcreTemplateField); + Tab_Acres.Controls.Add(L_MapAcreTemplateField); + Tab_Acres.Controls.Add(L_MapAcreTemplateOutside); + Tab_Acres.Controls.Add(NUD_MapAcreTemplateOutside); + Tab_Acres.Controls.Add(CB_MapAcreSelect); + Tab_Acres.Controls.Add(B_DumpLoadAcres); + Tab_Acres.Controls.Add(L_MapAcre); + Tab_Acres.Controls.Add(CB_MapAcre); + Tab_Acres.Location = new System.Drawing.Point(4, 26); + Tab_Acres.Margin = new System.Windows.Forms.Padding(4); + Tab_Acres.Name = "Tab_Acres"; + Tab_Acres.Padding = new System.Windows.Forms.Padding(4); + Tab_Acres.Size = new System.Drawing.Size(286, 603); + Tab_Acres.TabIndex = 3; + Tab_Acres.Text = "Acres"; + Tab_Acres.UseVisualStyleBackColor = true; // // NUD_MapAcreTemplateField // - this.NUD_MapAcreTemplateField.Location = new System.Drawing.Point(169, 352); - this.NUD_MapAcreTemplateField.Maximum = new decimal(new int[] { - 65535, - 0, - 0, - 0}); - this.NUD_MapAcreTemplateField.Name = "NUD_MapAcreTemplateField"; - this.NUD_MapAcreTemplateField.Size = new System.Drawing.Size(69, 20); - this.NUD_MapAcreTemplateField.TabIndex = 127; + NUD_MapAcreTemplateField.Location = new System.Drawing.Point(197, 460); + NUD_MapAcreTemplateField.Margin = new System.Windows.Forms.Padding(4); + NUD_MapAcreTemplateField.Maximum = new decimal(new int[] { 65535, 0, 0, 0 }); + NUD_MapAcreTemplateField.Name = "NUD_MapAcreTemplateField"; + NUD_MapAcreTemplateField.Size = new System.Drawing.Size(80, 25); + NUD_MapAcreTemplateField.TabIndex = 127; // // L_MapAcreTemplateField // - this.L_MapAcreTemplateField.Location = new System.Drawing.Point(10, 351); - this.L_MapAcreTemplateField.Name = "L_MapAcreTemplateField"; - this.L_MapAcreTemplateField.Size = new System.Drawing.Size(154, 19); - this.L_MapAcreTemplateField.TabIndex = 126; - this.L_MapAcreTemplateField.Text = "Field Acre Template:"; - this.L_MapAcreTemplateField.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + L_MapAcreTemplateField.Location = new System.Drawing.Point(12, 459); + L_MapAcreTemplateField.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_MapAcreTemplateField.Name = "L_MapAcreTemplateField"; + L_MapAcreTemplateField.Size = new System.Drawing.Size(180, 25); + L_MapAcreTemplateField.TabIndex = 126; + L_MapAcreTemplateField.Text = "Field Acre Template:"; + L_MapAcreTemplateField.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // L_MapAcreTemplateOutside // - this.L_MapAcreTemplateOutside.Location = new System.Drawing.Point(10, 325); - this.L_MapAcreTemplateOutside.Name = "L_MapAcreTemplateOutside"; - this.L_MapAcreTemplateOutside.Size = new System.Drawing.Size(154, 19); - this.L_MapAcreTemplateOutside.TabIndex = 125; - this.L_MapAcreTemplateOutside.Text = "Outside Acre Template:"; - this.L_MapAcreTemplateOutside.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + L_MapAcreTemplateOutside.Location = new System.Drawing.Point(12, 425); + L_MapAcreTemplateOutside.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_MapAcreTemplateOutside.Name = "L_MapAcreTemplateOutside"; + L_MapAcreTemplateOutside.Size = new System.Drawing.Size(180, 25); + L_MapAcreTemplateOutside.TabIndex = 125; + L_MapAcreTemplateOutside.Text = "Outside Acre Template:"; + L_MapAcreTemplateOutside.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // NUD_MapAcreTemplateOutside // - this.NUD_MapAcreTemplateOutside.Location = new System.Drawing.Point(169, 327); - this.NUD_MapAcreTemplateOutside.Maximum = new decimal(new int[] { - 65535, - 0, - 0, - 0}); - this.NUD_MapAcreTemplateOutside.Name = "NUD_MapAcreTemplateOutside"; - this.NUD_MapAcreTemplateOutside.Size = new System.Drawing.Size(69, 20); - this.NUD_MapAcreTemplateOutside.TabIndex = 124; + NUD_MapAcreTemplateOutside.Location = new System.Drawing.Point(197, 428); + NUD_MapAcreTemplateOutside.Margin = new System.Windows.Forms.Padding(4); + NUD_MapAcreTemplateOutside.Maximum = new decimal(new int[] { 65535, 0, 0, 0 }); + NUD_MapAcreTemplateOutside.Name = "NUD_MapAcreTemplateOutside"; + NUD_MapAcreTemplateOutside.Size = new System.Drawing.Size(80, 25); + NUD_MapAcreTemplateOutside.TabIndex = 124; // // CB_MapAcreSelect // - this.CB_MapAcreSelect.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.CB_MapAcreSelect.FormattingEnabled = true; - this.CB_MapAcreSelect.Location = new System.Drawing.Point(9, 33); - this.CB_MapAcreSelect.Name = "CB_MapAcreSelect"; - this.CB_MapAcreSelect.Size = new System.Drawing.Size(213, 21); - this.CB_MapAcreSelect.TabIndex = 102; - this.CB_MapAcreSelect.SelectedValueChanged += new System.EventHandler(this.CB_MapAcreSelect_SelectedValueChanged); + CB_MapAcreSelect.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + CB_MapAcreSelect.FormattingEnabled = true; + CB_MapAcreSelect.Location = new System.Drawing.Point(10, 43); + CB_MapAcreSelect.Margin = new System.Windows.Forms.Padding(4); + CB_MapAcreSelect.Name = "CB_MapAcreSelect"; + CB_MapAcreSelect.Size = new System.Drawing.Size(248, 25); + CB_MapAcreSelect.TabIndex = 102; + CB_MapAcreSelect.SelectedValueChanged += CB_MapAcreSelect_SelectedValueChanged; // // B_DumpLoadAcres // - this.B_DumpLoadAcres.ContextMenuStrip = this.CM_DLMapAcres; - this.B_DumpLoadAcres.Location = new System.Drawing.Point(6, 413); - this.B_DumpLoadAcres.Name = "B_DumpLoadAcres"; - this.B_DumpLoadAcres.Size = new System.Drawing.Size(112, 40); - this.B_DumpLoadAcres.TabIndex = 101; - this.B_DumpLoadAcres.Text = "Dump/Import"; - this.B_DumpLoadAcres.UseVisualStyleBackColor = true; - this.B_DumpLoadAcres.Click += new System.EventHandler(this.B_DumpLoadAcres_Click); + B_DumpLoadAcres.ContextMenuStrip = CM_DLMapAcres; + B_DumpLoadAcres.Location = new System.Drawing.Point(7, 540); + B_DumpLoadAcres.Margin = new System.Windows.Forms.Padding(4); + B_DumpLoadAcres.Name = "B_DumpLoadAcres"; + B_DumpLoadAcres.Size = new System.Drawing.Size(131, 52); + B_DumpLoadAcres.TabIndex = 101; + B_DumpLoadAcres.Text = "Dump/Import"; + B_DumpLoadAcres.UseVisualStyleBackColor = true; + B_DumpLoadAcres.Click += B_DumpLoadAcres_Click; // // CM_DLMapAcres // - this.CM_DLMapAcres.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.B_DumpMapAcres, - this.B_ImportMapAcres}); - this.CM_DLMapAcres.Name = "CM_Picture"; - this.CM_DLMapAcres.ShowImageMargin = false; - this.CM_DLMapAcres.Size = new System.Drawing.Size(145, 48); + CM_DLMapAcres.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { B_DumpMapAcres, B_ImportMapAcres }); + CM_DLMapAcres.Name = "CM_Picture"; + CM_DLMapAcres.ShowImageMargin = false; + CM_DLMapAcres.Size = new System.Drawing.Size(158, 48); // // B_DumpMapAcres // - this.B_DumpMapAcres.Name = "B_DumpMapAcres"; - this.B_DumpMapAcres.Size = new System.Drawing.Size(144, 22); - this.B_DumpMapAcres.Text = "Dump Map Acres"; - this.B_DumpMapAcres.Click += new System.EventHandler(this.B_DumpMapAcres_Click); + B_DumpMapAcres.Name = "B_DumpMapAcres"; + B_DumpMapAcres.Size = new System.Drawing.Size(157, 22); + B_DumpMapAcres.Text = "Dump Map Acres"; + B_DumpMapAcres.Click += B_DumpMapAcres_Click; // // B_ImportMapAcres // - this.B_ImportMapAcres.Name = "B_ImportMapAcres"; - this.B_ImportMapAcres.Size = new System.Drawing.Size(144, 22); - this.B_ImportMapAcres.Text = "Import Map Acres"; - this.B_ImportMapAcres.Click += new System.EventHandler(this.B_ImportMapAcres_Click); + B_ImportMapAcres.Name = "B_ImportMapAcres"; + B_ImportMapAcres.Size = new System.Drawing.Size(157, 22); + B_ImportMapAcres.Text = "Import Map Acres"; + B_ImportMapAcres.Click += B_ImportMapAcres_Click; // // L_MapAcre // - this.L_MapAcre.Location = new System.Drawing.Point(6, 6); - this.L_MapAcre.Name = "L_MapAcre"; - this.L_MapAcre.Size = new System.Drawing.Size(89, 19); - this.L_MapAcre.TabIndex = 99; - this.L_MapAcre.Text = "Acre:"; - this.L_MapAcre.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + L_MapAcre.Location = new System.Drawing.Point(7, 8); + L_MapAcre.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_MapAcre.Name = "L_MapAcre"; + L_MapAcre.Size = new System.Drawing.Size(104, 25); + L_MapAcre.TabIndex = 99; + L_MapAcre.Text = "Acre:"; + L_MapAcre.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // CB_MapAcre // - this.CB_MapAcre.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.CB_MapAcre.FormattingEnabled = true; - this.CB_MapAcre.Location = new System.Drawing.Point(101, 6); - this.CB_MapAcre.Name = "CB_MapAcre"; - this.CB_MapAcre.Size = new System.Drawing.Size(49, 21); - this.CB_MapAcre.TabIndex = 98; - this.CB_MapAcre.SelectedIndexChanged += new System.EventHandler(this.CB_MapAcre_SelectedIndexChanged); + CB_MapAcre.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + CB_MapAcre.FormattingEnabled = true; + CB_MapAcre.Location = new System.Drawing.Point(118, 8); + CB_MapAcre.Margin = new System.Windows.Forms.Padding(4); + CB_MapAcre.Name = "CB_MapAcre"; + CB_MapAcre.Size = new System.Drawing.Size(56, 25); + CB_MapAcre.TabIndex = 98; + CB_MapAcre.SelectedIndexChanged += CB_MapAcre_SelectedIndexChanged; // // CM_DLTerrain // - this.CM_DLTerrain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.B_DumpTerrainAcre, - this.B_DumpTerrainAll, - this.B_ImportTerrainAcre, - this.B_ImportTerrainAll}); - this.CM_DLTerrain.Name = "CM_Picture"; - this.CM_DLTerrain.ShowImageMargin = false; - this.CM_DLTerrain.Size = new System.Drawing.Size(135, 92); + CM_DLTerrain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { B_DumpTerrainAcre, B_DumpTerrainAll, B_ImportTerrainAcre, B_ImportTerrainAll }); + CM_DLTerrain.Name = "CM_Picture"; + CM_DLTerrain.ShowImageMargin = false; + CM_DLTerrain.Size = new System.Drawing.Size(145, 92); // // B_DumpTerrainAcre // - this.B_DumpTerrainAcre.Name = "B_DumpTerrainAcre"; - this.B_DumpTerrainAcre.Size = new System.Drawing.Size(134, 22); - this.B_DumpTerrainAcre.Text = "Dump Acre"; - this.B_DumpTerrainAcre.Click += new System.EventHandler(this.B_DumpTerrainAcre_Click); + B_DumpTerrainAcre.Name = "B_DumpTerrainAcre"; + B_DumpTerrainAcre.Size = new System.Drawing.Size(144, 22); + B_DumpTerrainAcre.Text = "Dump Acre"; + B_DumpTerrainAcre.Click += B_DumpTerrainAcre_Click; // // B_DumpTerrainAll // - this.B_DumpTerrainAll.Name = "B_DumpTerrainAll"; - this.B_DumpTerrainAll.Size = new System.Drawing.Size(134, 22); - this.B_DumpTerrainAll.Text = "Dump All Acres"; - this.B_DumpTerrainAll.Click += new System.EventHandler(this.B_DumpTerrainAll_Click); + B_DumpTerrainAll.Name = "B_DumpTerrainAll"; + B_DumpTerrainAll.Size = new System.Drawing.Size(144, 22); + B_DumpTerrainAll.Text = "Dump All Acres"; + B_DumpTerrainAll.Click += B_DumpTerrainAll_Click; // // B_ImportTerrainAcre // - this.B_ImportTerrainAcre.Name = "B_ImportTerrainAcre"; - this.B_ImportTerrainAcre.Size = new System.Drawing.Size(134, 22); - this.B_ImportTerrainAcre.Text = "Import Acre"; - this.B_ImportTerrainAcre.Click += new System.EventHandler(this.B_ImportTerrainAcre_Click); + B_ImportTerrainAcre.Name = "B_ImportTerrainAcre"; + B_ImportTerrainAcre.Size = new System.Drawing.Size(144, 22); + B_ImportTerrainAcre.Text = "Import Acre"; + B_ImportTerrainAcre.Click += B_ImportTerrainAcre_Click; // // B_ImportTerrainAll // - this.B_ImportTerrainAll.Name = "B_ImportTerrainAll"; - this.B_ImportTerrainAll.Size = new System.Drawing.Size(134, 22); - this.B_ImportTerrainAll.Text = "Import All Acres"; - this.B_ImportTerrainAll.Click += new System.EventHandler(this.B_ImportTerrainAll_Click); + B_ImportTerrainAll.Name = "B_ImportTerrainAll"; + B_ImportTerrainAll.Size = new System.Drawing.Size(144, 22); + B_ImportTerrainAll.Text = "Import All Acres"; + B_ImportTerrainAll.Click += B_ImportTerrainAll_Click; // // CM_Terrain // - this.CM_Terrain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.B_ZeroElevation, - this.B_SetAllTerrain, - this.B_SetAllRoadTiles, - this.B_ClearPlacedDesigns, - this.B_ImportPlacedDesigns, - this.B_ExportPlacedDesigns}); - this.CM_Terrain.Name = "CM_Picture"; - this.CM_Terrain.ShowImageMargin = false; - this.CM_Terrain.Size = new System.Drawing.Size(225, 136); + CM_Terrain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { B_ZeroElevation, B_SetAllTerrain, B_SetAllRoadTiles, B_ClearPlacedDesigns, B_ImportPlacedDesigns, B_ExportPlacedDesigns }); + CM_Terrain.Name = "CM_Picture"; + CM_Terrain.ShowImageMargin = false; + CM_Terrain.Size = new System.Drawing.Size(248, 136); // // B_ZeroElevation // - this.B_ZeroElevation.Name = "B_ZeroElevation"; - this.B_ZeroElevation.Size = new System.Drawing.Size(224, 22); - this.B_ZeroElevation.Text = "Zero Elevation"; - this.B_ZeroElevation.Click += new System.EventHandler(this.B_ZeroElevation_Click); + B_ZeroElevation.Name = "B_ZeroElevation"; + B_ZeroElevation.Size = new System.Drawing.Size(247, 22); + B_ZeroElevation.Text = "Zero Elevation"; + B_ZeroElevation.Click += B_ZeroElevation_Click; // // B_SetAllTerrain // - this.B_SetAllTerrain.Name = "B_SetAllTerrain"; - this.B_SetAllTerrain.Size = new System.Drawing.Size(224, 22); - this.B_SetAllTerrain.Text = "Set All Tiles using Tile from Editor"; - this.B_SetAllTerrain.Click += new System.EventHandler(this.B_SetAllTerrain_Click); + B_SetAllTerrain.Name = "B_SetAllTerrain"; + B_SetAllTerrain.Size = new System.Drawing.Size(247, 22); + B_SetAllTerrain.Text = "Set All Tiles using Tile from Editor"; + B_SetAllTerrain.Click += B_SetAllTerrain_Click; // // B_SetAllRoadTiles // - this.B_SetAllRoadTiles.Name = "B_SetAllRoadTiles"; - this.B_SetAllRoadTiles.Size = new System.Drawing.Size(224, 22); - this.B_SetAllRoadTiles.Text = "Set All Road Tiles from Editor"; - this.B_SetAllRoadTiles.Click += new System.EventHandler(this.B_SetAllRoadTiles_Click); + B_SetAllRoadTiles.Name = "B_SetAllRoadTiles"; + B_SetAllRoadTiles.Size = new System.Drawing.Size(247, 22); + B_SetAllRoadTiles.Text = "Set All Road Tiles from Editor"; + B_SetAllRoadTiles.Click += B_SetAllRoadTiles_Click; // // B_ClearPlacedDesigns // - this.B_ClearPlacedDesigns.Name = "B_ClearPlacedDesigns"; - this.B_ClearPlacedDesigns.Size = new System.Drawing.Size(224, 22); - this.B_ClearPlacedDesigns.Text = "Clear all Placed Designs"; - this.B_ClearPlacedDesigns.Click += new System.EventHandler(this.B_ClearPlacedDesigns_Click); + B_ClearPlacedDesigns.Name = "B_ClearPlacedDesigns"; + B_ClearPlacedDesigns.Size = new System.Drawing.Size(247, 22); + B_ClearPlacedDesigns.Text = "Clear all Placed Designs"; + B_ClearPlacedDesigns.Click += B_ClearPlacedDesigns_Click; // // B_ImportPlacedDesigns // - this.B_ImportPlacedDesigns.Name = "B_ImportPlacedDesigns"; - this.B_ImportPlacedDesigns.Size = new System.Drawing.Size(224, 22); - this.B_ImportPlacedDesigns.Text = "Import all Placed Design Choices"; - this.B_ImportPlacedDesigns.Click += new System.EventHandler(this.B_ImportPlacedDesigns_Click); + B_ImportPlacedDesigns.Name = "B_ImportPlacedDesigns"; + B_ImportPlacedDesigns.Size = new System.Drawing.Size(247, 22); + B_ImportPlacedDesigns.Text = "Import all Placed Design Choices"; + B_ImportPlacedDesigns.Click += B_ImportPlacedDesigns_Click; // // B_ExportPlacedDesigns // - this.B_ExportPlacedDesigns.Name = "B_ExportPlacedDesigns"; - this.B_ExportPlacedDesigns.Size = new System.Drawing.Size(224, 22); - this.B_ExportPlacedDesigns.Text = "Export all Placed Design Choices"; - this.B_ExportPlacedDesigns.Click += new System.EventHandler(this.B_ExportPlacedDesigns_Click); + B_ExportPlacedDesigns.Name = "B_ExportPlacedDesigns"; + B_ExportPlacedDesigns.Size = new System.Drawing.Size(247, 22); + B_ExportPlacedDesigns.Text = "Export all Placed Design Choices"; + B_ExportPlacedDesigns.Click += B_ExportPlacedDesigns_Click; // // RB_Item // - this.RB_Item.CheckAlign = System.Drawing.ContentAlignment.MiddleRight; - this.RB_Item.Checked = true; - this.RB_Item.Location = new System.Drawing.Point(641, 299); - this.RB_Item.Name = "RB_Item"; - this.RB_Item.Size = new System.Drawing.Size(120, 20); - this.RB_Item.TabIndex = 43; - this.RB_Item.TabStop = true; - this.RB_Item.Text = "Items"; - this.RB_Item.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - this.RB_Item.UseVisualStyleBackColor = true; + RB_Item.CheckAlign = System.Drawing.ContentAlignment.MiddleRight; + RB_Item.Checked = true; + RB_Item.Location = new System.Drawing.Point(684, 375); + RB_Item.Margin = new System.Windows.Forms.Padding(4); + RB_Item.Name = "RB_Item"; + RB_Item.Size = new System.Drawing.Size(140, 26); + RB_Item.TabIndex = 43; + RB_Item.TabStop = true; + RB_Item.Text = "Items"; + RB_Item.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + RB_Item.UseVisualStyleBackColor = true; // // RB_Terrain // - this.RB_Terrain.CheckAlign = System.Drawing.ContentAlignment.MiddleRight; - this.RB_Terrain.Location = new System.Drawing.Point(641, 282); - this.RB_Terrain.Name = "RB_Terrain"; - this.RB_Terrain.Size = new System.Drawing.Size(120, 20); - this.RB_Terrain.TabIndex = 44; - this.RB_Terrain.Text = "Terrain"; - this.RB_Terrain.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - this.RB_Terrain.UseVisualStyleBackColor = true; + RB_Terrain.CheckAlign = System.Drawing.ContentAlignment.MiddleRight; + RB_Terrain.Location = new System.Drawing.Point(684, 353); + RB_Terrain.Margin = new System.Windows.Forms.Padding(4); + RB_Terrain.Name = "RB_Terrain"; + RB_Terrain.Size = new System.Drawing.Size(140, 26); + RB_Terrain.TabIndex = 44; + RB_Terrain.Text = "Terrain"; + RB_Terrain.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + RB_Terrain.UseVisualStyleBackColor = true; // // L_TileMode // - this.L_TileMode.Location = new System.Drawing.Point(641, 259); - this.L_TileMode.Name = "L_TileMode"; - this.L_TileMode.Size = new System.Drawing.Size(120, 20); - this.L_TileMode.TabIndex = 45; - this.L_TileMode.Text = "Tile Editor Mode"; - this.L_TileMode.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + L_TileMode.Location = new System.Drawing.Point(684, 323); + L_TileMode.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_TileMode.Name = "L_TileMode"; + L_TileMode.Size = new System.Drawing.Size(140, 26); + L_TileMode.TabIndex = 45; + L_TileMode.Text = "Tile Editor Mode"; + L_TileMode.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // CHK_RedirectExtensionLoad // - this.CHK_RedirectExtensionLoad.AutoSize = true; - this.CHK_RedirectExtensionLoad.Checked = true; - this.CHK_RedirectExtensionLoad.CheckState = System.Windows.Forms.CheckState.Checked; - this.CHK_RedirectExtensionLoad.Location = new System.Drawing.Point(535, 441); - this.CHK_RedirectExtensionLoad.Name = "CHK_RedirectExtensionLoad"; - this.CHK_RedirectExtensionLoad.Size = new System.Drawing.Size(173, 17); - this.CHK_RedirectExtensionLoad.TabIndex = 46; - this.CHK_RedirectExtensionLoad.Text = "View Root instead of Extension"; - this.CHK_RedirectExtensionLoad.UseVisualStyleBackColor = true; + CHK_RedirectExtensionLoad.AutoSize = true; + CHK_RedirectExtensionLoad.Checked = true; + CHK_RedirectExtensionLoad.CheckState = System.Windows.Forms.CheckState.Checked; + CHK_RedirectExtensionLoad.Location = new System.Drawing.Point(0, 63); + CHK_RedirectExtensionLoad.Margin = new System.Windows.Forms.Padding(0); + CHK_RedirectExtensionLoad.Name = "CHK_RedirectExtensionLoad"; + CHK_RedirectExtensionLoad.Size = new System.Drawing.Size(207, 21); + CHK_RedirectExtensionLoad.TabIndex = 46; + CHK_RedirectExtensionLoad.Text = "View Root instead of Extension"; + CHK_RedirectExtensionLoad.UseVisualStyleBackColor = true; // // CHK_MoveOnDrag // - this.CHK_MoveOnDrag.AutoSize = true; - this.CHK_MoveOnDrag.Checked = true; - this.CHK_MoveOnDrag.CheckState = System.Windows.Forms.CheckState.Checked; - this.CHK_MoveOnDrag.Location = new System.Drawing.Point(535, 423); - this.CHK_MoveOnDrag.Name = "CHK_MoveOnDrag"; - this.CHK_MoveOnDrag.Size = new System.Drawing.Size(204, 17); - this.CHK_MoveOnDrag.TabIndex = 46; - this.CHK_MoveOnDrag.Text = "Move Field Item Editor on mouse drag"; - this.CHK_MoveOnDrag.UseVisualStyleBackColor = true; + CHK_MoveOnDrag.AutoSize = true; + CHK_MoveOnDrag.Checked = true; + CHK_MoveOnDrag.CheckState = System.Windows.Forms.CheckState.Checked; + CHK_MoveOnDrag.Location = new System.Drawing.Point(0, 0); + CHK_MoveOnDrag.Margin = new System.Windows.Forms.Padding(0); + CHK_MoveOnDrag.Name = "CHK_MoveOnDrag"; + CHK_MoveOnDrag.Size = new System.Drawing.Size(253, 21); + CHK_MoveOnDrag.TabIndex = 46; + CHK_MoveOnDrag.Text = "Move Field Item Editor on mouse drag"; + CHK_MoveOnDrag.UseVisualStyleBackColor = true; // // CHK_FieldItemSnap // - this.CHK_FieldItemSnap.AutoSize = true; - this.CHK_FieldItemSnap.Checked = true; - this.CHK_FieldItemSnap.CheckState = System.Windows.Forms.CheckState.Checked; - this.CHK_FieldItemSnap.Location = new System.Drawing.Point(535, 496); - this.CHK_FieldItemSnap.Name = "CHK_FieldItemSnap"; - this.CHK_FieldItemSnap.Size = new System.Drawing.Size(172, 17); - this.CHK_FieldItemSnap.TabIndex = 47; - this.CHK_FieldItemSnap.Text = "Snap Field Items to Grid on Set"; - this.CHK_FieldItemSnap.UseVisualStyleBackColor = true; + CHK_FieldItemSnap.AutoSize = true; + CHK_FieldItemSnap.Checked = true; + CHK_FieldItemSnap.CheckState = System.Windows.Forms.CheckState.Checked; + CHK_FieldItemSnap.Location = new System.Drawing.Point(0, 84); + CHK_FieldItemSnap.Margin = new System.Windows.Forms.Padding(0); + CHK_FieldItemSnap.Name = "CHK_FieldItemSnap"; + CHK_FieldItemSnap.Size = new System.Drawing.Size(208, 21); + CHK_FieldItemSnap.TabIndex = 47; + CHK_FieldItemSnap.Text = "Snap Field Items to Grid on Set"; + CHK_FieldItemSnap.UseVisualStyleBackColor = true; + // + // flowLayoutPanel1 + // + flowLayoutPanel1.Controls.Add(CHK_MoveOnDrag); + flowLayoutPanel1.Controls.Add(CHK_NoOverwrite); + flowLayoutPanel1.Controls.Add(CHK_AutoExtension); + flowLayoutPanel1.Controls.Add(CHK_RedirectExtensionLoad); + flowLayoutPanel1.Controls.Add(CHK_FieldItemSnap); + flowLayoutPanel1.Location = new System.Drawing.Point(14, 537); + flowLayoutPanel1.Name = "flowLayoutPanel1"; + flowLayoutPanel1.Size = new System.Drawing.Size(269, 112); + flowLayoutPanel1.TabIndex = 48; + // + // L_Acre + // + L_Acre.Location = new System.Drawing.Point(657, 442); + L_Acre.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + L_Acre.Name = "L_Acre"; + L_Acre.Size = new System.Drawing.Size(104, 25); + L_Acre.TabIndex = 101; + L_Acre.Text = "Acre:"; + L_Acre.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // CB_Acre + // + CB_Acre.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + CB_Acre.FormattingEnabled = true; + CB_Acre.Location = new System.Drawing.Point(768, 442); + CB_Acre.Margin = new System.Windows.Forms.Padding(4); + CB_Acre.Name = "CB_Acre"; + CB_Acre.Size = new System.Drawing.Size(56, 25); + CB_Acre.TabIndex = 100; + CB_Acre.SelectedIndexChanged += ChangeAcre; // // FieldItemEditor // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(1027, 537); - this.Controls.Add(this.CHK_FieldItemSnap); - this.Controls.Add(this.CHK_RedirectExtensionLoad); - this.Controls.Add(this.CHK_MoveOnDrag); - this.Controls.Add(this.L_TileMode); - this.Controls.Add(this.RB_Terrain); - this.Controls.Add(this.RB_Item); - this.Controls.Add(this.TC_Editor); - this.Controls.Add(this.CHK_AutoExtension); - this.Controls.Add(this.CHK_NoOverwrite); - this.Controls.Add(this.PB_Acre); - this.Controls.Add(this.L_Layer); - this.Controls.Add(this.NUD_Layer); - this.Controls.Add(this.L_Coordinates); - this.Controls.Add(this.CHK_SnapToAcre); - this.Controls.Add(this.PB_Map); - this.Controls.Add(this.B_Down); - this.Controls.Add(this.B_Right); - this.Controls.Add(this.B_Left); - this.Controls.Add(this.B_Up); - this.Controls.Add(this.L_Acre); - this.Controls.Add(this.CB_Acre); - this.Controls.Add(this.B_Cancel); - this.Controls.Add(this.B_Save); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; - this.Icon = global::NHSE.WinForms.Properties.Resources.icon; - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "FieldItemEditor"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Field Item Editor"; - this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FieldItemEditor_FormClosed); - this.CM_Click.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.PB_Map)).EndInit(); - this.CM_Picture.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.NUD_Layer)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.PB_Acre)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.TR_Transparency)).EndInit(); - this.CM_Remove.ResumeLayout(false); - this.TC_Editor.ResumeLayout(false); - this.Tab_Item.ResumeLayout(false); - this.Tab_Item.PerformLayout(); - this.CM_DLField.ResumeLayout(false); - this.Tab_Building.ResumeLayout(false); - this.CM_DLBuilding.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.NUD_Bit)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_BuildingType)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_UniqueID)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_X)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_TypeArg)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_Y)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_Type)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_Angle)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_PlazaX)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_PlazaY)).EndInit(); - this.Tab_Terrain.ResumeLayout(false); - this.Tab_Terrain.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.TR_Terrain)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.TR_BuildingTransparency)).EndInit(); - this.Tab_Acres.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.NUD_MapAcreTemplateField)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.NUD_MapAcreTemplateOutside)).EndInit(); - this.CM_DLMapAcres.ResumeLayout(false); - this.CM_DLTerrain.ResumeLayout(false); - this.CM_Terrain.ResumeLayout(false); - this.ResumeLayout(false); - this.PerformLayout(); + AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + ClientSize = new System.Drawing.Size(1138, 702); + Controls.Add(L_Acre); + Controls.Add(CB_Acre); + Controls.Add(flowLayoutPanel1); + Controls.Add(L_TileMode); + Controls.Add(RB_Terrain); + Controls.Add(RB_Item); + Controls.Add(TC_Editor); + Controls.Add(PB_Viewport); + Controls.Add(L_Layer); + Controls.Add(NUD_Layer); + Controls.Add(L_Coordinates); + Controls.Add(CHK_SnapToAcre); + Controls.Add(PB_Map); + Controls.Add(B_Down); + Controls.Add(B_Right); + Controls.Add(B_Left); + Controls.Add(B_Up); + Controls.Add(B_Cancel); + Controls.Add(B_Save); + FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + Icon = Properties.Resources.icon; + Margin = new System.Windows.Forms.Padding(4); + MaximizeBox = false; + MinimizeBox = false; + Name = "FieldItemEditor"; + StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + Text = "Field Item Editor"; + FormClosed += FieldItemEditor_FormClosed; + CM_Click.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)PB_Map).EndInit(); + CM_Picture.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)NUD_Layer).EndInit(); + ((System.ComponentModel.ISupportInitialize)PB_Viewport).EndInit(); + ((System.ComponentModel.ISupportInitialize)TR_Transparency).EndInit(); + CM_Remove.ResumeLayout(false); + TC_Editor.ResumeLayout(false); + Tab_Item.ResumeLayout(false); + Tab_Item.PerformLayout(); + CM_DLField.ResumeLayout(false); + Tab_Building.ResumeLayout(false); + CM_DLBuilding.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)NUD_Bit).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_BuildingType).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_UniqueID).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_X).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_TypeArg).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_Y).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_Type).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_Angle).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_PlazaX).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_PlazaY).EndInit(); + Tab_Terrain.ResumeLayout(false); + Tab_Terrain.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)TR_Terrain).EndInit(); + ((System.ComponentModel.ISupportInitialize)TR_BuildingTransparency).EndInit(); + Tab_Acres.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)NUD_MapAcreTemplateField).EndInit(); + ((System.ComponentModel.ISupportInitialize)NUD_MapAcreTemplateOutside).EndInit(); + CM_DLMapAcres.ResumeLayout(false); + CM_DLTerrain.ResumeLayout(false); + CM_Terrain.ResumeLayout(false); + flowLayoutPanel1.ResumeLayout(false); + flowLayoutPanel1.PerformLayout(); + ResumeLayout(false); + PerformLayout(); } @@ -1503,8 +1474,6 @@ private void InitializeComponent() private System.Windows.Forms.Button B_Cancel; private System.Windows.Forms.Button B_Save; - private System.Windows.Forms.ComboBox CB_Acre; - private System.Windows.Forms.Label L_Acre; private System.Windows.Forms.ContextMenuStrip CM_Click; private System.Windows.Forms.ToolStripMenuItem Menu_View; private System.Windows.Forms.ToolStripMenuItem Menu_Set; @@ -1521,7 +1490,7 @@ private void InitializeComponent() private System.Windows.Forms.NumericUpDown NUD_Layer; private System.Windows.Forms.Label L_Layer; private System.Windows.Forms.ToolTip TT_Hover; - private System.Windows.Forms.PictureBox PB_Acre; + private System.Windows.Forms.PictureBox PB_Viewport; private System.Windows.Forms.TrackBar TR_Transparency; private System.Windows.Forms.CheckBox CHK_NoOverwrite; private System.Windows.Forms.CheckBox CHK_AutoExtension; @@ -1624,5 +1593,8 @@ private void InitializeComponent() private System.Windows.Forms.Button B_TerrainBrush; private System.Windows.Forms.ToolStripMenuItem B_RemoveEditor; private System.Windows.Forms.ToolStripMenuItem Menu_Activate; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + private System.Windows.Forms.Label L_Acre; + private System.Windows.Forms.ComboBox CB_Acre; } } \ No newline at end of file diff --git a/NHSE.WinForms/Subforms/Map/FieldItemEditor.cs b/NHSE.WinForms/Subforms/Map/FieldItemEditor.cs index 814f354..b131f1e 100644 --- a/NHSE.WinForms/Subforms/Map/FieldItemEditor.cs +++ b/NHSE.WinForms/Subforms/Map/FieldItemEditor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Drawing.Imaging; using System.IO; @@ -14,21 +15,34 @@ namespace NHSE.WinForms; public sealed partial class FieldItemEditor : Form, IItemLayerEditor { private readonly MainSave SAV; + private readonly MapEditor Editor; + private readonly MapRenderer Renderer; - private readonly MapManager Map; - private readonly MapViewer View; + private MapViewState View => Editor.Mutator.View; + private MapTileManager Map => Editor.Mutator.Manager; private bool Loading; private int SelectedBuildingIndex; + /// Cached current hover X coordinate within the acre. private int HoverX; + /// Cached current hover Y coordinate within the acre. private int HoverY; + private int DragX = -1; private int DragY = -1; - private bool Dragging; + private bool IsDragOperationActive; + + private bool IsMenuHasActivate = true; public ItemEditor ItemProvider => ItemEdit; - public ItemLayer SpawnLayer => Map.CurrentLayer; + + /// + /// Layer to spawn items into. + /// + public LayerItem Spawn => CurrentLayer; + + public LayerFieldItem CurrentLayer => Editor.Mutator.CurrentLayer; private TerrainBrushEditor? tbeForm; @@ -37,10 +51,13 @@ public FieldItemEditor(MainSave sav) InitializeComponent(); this.TranslateInterface(GameInfo.CurrentLanguage); - var scale = (PB_Acre.Width - 2) / 32; + // Read the expected scale from the control. + var scale = (PB_Viewport.Width - 2) / LayerFieldItem.TilesPerAcreDim; // 1px border SAV = sav; - Map = new MapManager(sav); - View = new MapViewer(Map, scale); + Editor = MapEditor.FromSaveFile(sav); + Editor.MapScale = 1; + Editor.ViewScale = scale; + Renderer = new MapRenderer(Editor); Loading = true; @@ -48,25 +65,28 @@ public FieldItemEditor(MainSave sav) LoadBuildings(sav); ReloadMapBackground(); LoadEditors(); - LB_Items.SelectedIndex = 0; + + // Set initial states CB_Acre.SelectedIndex = 0; CB_MapAcre.SelectedIndex = 0; + LB_Items.SelectedIndex = 0; // triggers a draw + Loading = false; - LoadItemGridAcre(); } private void LoadComboBoxes() { - foreach (var acre in MapGrid.Acres) + // Snap viewport to acre + foreach (var acre in AcreCoordinate.Exterior) CB_Acre.Items.Add(acre.Name); - var exterior = AcreCoordinate.GetGridWithExterior(9, 8); - foreach (var acre in exterior) + // Select acre type for current + foreach (var acre in AcreCoordinate.Exterior) CB_MapAcre.Items.Add(acre.Name); CB_MapAcreSelect.DisplayMember = nameof(ComboItem.Text); CB_MapAcreSelect.ValueMember = nameof(ComboItem.Value); - CB_MapAcreSelect.DataSource = ComboItemUtil.GetArray(typeof(OutsideAcre)); + CB_MapAcreSelect.DataSource = ComboItemUtil.GetArray(); NUD_MapAcreTemplateOutside.Value = SAV.OutsideFieldTemplateUniqueId; NUD_MapAcreTemplateField.Value = SAV.MainFieldParamUniqueID; @@ -77,7 +97,7 @@ private void LoadBuildings(MainSave sav) NUD_PlazaX.Value = sav.EventPlazaLeftUpX; NUD_PlazaY.Value = sav.EventPlazaLeftUpZ; - foreach (var obj in Map.Buildings) + foreach (var obj in Editor.Mutator.Manager.LayerBuildings.Buildings) LB_Items.Items.Add(obj.ToString()); } @@ -90,11 +110,13 @@ private void LoadEditors() PG_TerrainTile.SelectedObject = new TerrainTile(); } - private int AcreIndex => CB_Acre.SelectedIndex; + private int ExteriorAcreIndex => CB_Acre.SelectedIndex; private void ChangeAcre(object sender, EventArgs e) { - ChangeViewToAcre(AcreIndex); + if (Loading) + return; + ChangeViewToAcre(ExteriorAcreIndex); CB_MapAcre.Text = CB_Acre.Text; } @@ -107,7 +129,7 @@ private void ChangeViewToAcre(int acre) private void LoadItemGridAcre() { ReloadItems(); - ReloadAcreBackground(); + ReloadViewportBackground(); UpdateArrowVisibility(); } @@ -115,31 +137,43 @@ private void LoadItemGridAcre() private void ReloadMapBackground() { - PB_Map.BackgroundImage = View.GetBackgroundTerrain(SelectedBuildingIndex); + var img = Renderer.UpdateMapTerrain(SelectedBuildingIndex); + SetMapBackgroundImage(img); + } + + private void ReloadMapItemGrid() => SetMapForegroundImage(Renderer.UpdateMapItemsReticle(GetItemTransparency())); + + private void SetMapBackgroundImage(Bitmap img) + { + PB_Map.BackgroundImage = img; PB_Map.Invalidate(); // background image reassigning to same img doesn't redraw; force it } - private void ReloadAcreBackground() + private void SetMapForegroundImage(Bitmap img) + { + PB_Map.Image = img; + } + + private void ReloadViewportBackground() { var tbuild = (byte)TR_BuildingTransparency.Value; var tterrain = (byte)TR_Terrain.Value; - PB_Acre.BackgroundImage = View.GetBackgroundAcre(L_Coordinates.Font, tbuild, tterrain, SelectedBuildingIndex); - PB_Acre.Invalidate(); // background image reassigning to same img doesn't redraw; force it + var img = Renderer.UpdateViewportTerrain(L_Coordinates.Font, tbuild, tterrain, SelectedBuildingIndex); + PB_Viewport.BackgroundImage = img; + PB_Viewport.Invalidate(); // background image reassigning to same img doesn't redraw; force it } - private void ReloadMapItemGrid() => PB_Map.Image = View.GetMapWithReticle(GetItemTransparency()); - - private void ReloadAcreItemGrid() => PB_Acre.Image = View.GetLayerAcre(GetItemTransparency()); + private void ReloadViewportItems() => PB_Viewport.Image = Renderer.UpdateViewportItems(GetItemTransparency()); public void ReloadItems() { - ReloadAcreItemGrid(); + ReloadViewportItems(); ReloadMapItemGrid(); } private void ReloadBuildingsTerrain() { - ReloadAcreBackground(); + ReloadViewportBackground(); ReloadMapBackground(); } @@ -151,9 +185,9 @@ private void UpdateArrowVisibility() B_Right.Enabled = View.CanRight; } - private void PB_Acre_MouseClick(object sender, MouseEventArgs e) + private void ViewportMouseClick(object sender, MouseEventArgs e) { - if (Dragging) + if (IsDragOperationActive) { ResetDrag(); return; @@ -169,21 +203,25 @@ private void ResetDrag() { DragX = -1; DragY = -1; - Dragging = false; + IsDragOperationActive = false; } private void OmniTile(MouseEventArgs e) { - var tile = GetTile(Map.CurrentLayer, e, out var x, out var y); - OmniTile(tile, x, y); + if (!GetTile(e, CurrentLayer, out var tile)) + return; + OmniTile(tile.Tile, tile.RelativeX, tile.RelativeY); } private void OmniTileTerrain(MouseEventArgs e) { - SetHoveredItem(e); - var x = View.X + HoverX; - var y = View.Y + HoverY; - var tile = Map.Terrain.GetTile(x / 2, y / 2); + if (!GetTile(e, Editor.Mutator.Manager.LayerTerrain, out var meta)) + return; + + var tile = meta.Tile; + var relX = meta.RelativeX; + var relY = meta.RelativeY; + if (tbeForm?.IsBrushSelected != true) { OmniTileTerrain(tile); @@ -204,32 +242,32 @@ private void OmniTileTerrain(MouseEventArgs e) for (int j = -radius; j < radius; j++) { if ((i * i) + (j * j) < threshold) - selectedTiles.Add(Map.Terrain.GetTile((x / 2) + i, (y / 2) + j)); + selectedTiles.Add(Editor.Terrain.GetTile(relX + i, relY + j)); } } SetTiles(selectedTiles); } - private void OmniTile(Item tile, int x, int y) + private void OmniTile(Item tile, int relX, int relY) { switch (ModifierKeys) { default: - ViewTile(tile, x, y); + ViewTile(tile, relX, relY); return; case Keys.Alt | Keys.Control: case Keys.Alt | Keys.Control | Keys.Shift: - ReplaceTile(tile, x, y); + ReplaceTile(tile, relX, relY); return; case Keys.Shift: - SetTile(tile, x, y); + SetTile(tile, relX, relY); return; case Keys.Alt: - DeleteTile(tile, x, y); + DeleteTile(tile, relX, relY); return; } } @@ -256,32 +294,88 @@ private void OmniTileTerrain(TerrainTile tile) } } - private Item GetTile(FieldItemLayer layer, MouseEventArgs e, out int x, out int y) + private sealed record TileCheck(T Tile, int AbsoluteX, int AbsoluteY, int RelativeX, int RelativeY); + + private bool GetTile(MouseEventArgs e, LayerFieldItem layerField, [NotNullWhen(true)] out TileCheck? item) { - SetHoveredItem(e); - return layer.GetTile(x = View.X + HoverX, y = View.Y + HoverY); + UpdateHoveredCoordinates(e); + var (absX, absY) = GetAbsoluteCoordinatesHover(); + return GetTile(layerField, absX, absY, out item); } - private void SetHoveredItem(MouseEventArgs e) + private (int X, int Y) GetAbsoluteCoordinatesHover() { - GetAcreCoordinates(e, out HoverX, out HoverY); + var absX = View.X + HoverX; + var absY = View.Y + HoverY; + return (absX, absY); + } + + private (int X, int Y) GetAbsoluteCoordinatesHoverTerrain() + { + // Terrain tiles are 16x16, but the view caters to 32x32 for field items. + var absX = (View.X + HoverX) / 2; + var absY = (View.Y + HoverY) / 2; + return (absX, absY); + } + + private (int X, int Y) GetViewCoordinates(MouseEventArgs e) + { + var x = e.X / Editor.ViewScale; + var y = e.Y / Editor.ViewScale; + return (x, y); + } + + private bool GetTile(LayerFieldItem layerField, int absX, int absY, [NotNullWhen(true)] out TileCheck? item) + { + var cfg = Editor.Mutator.Manager.ConfigItems; + var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY); + if (!cfg.IsCoordinateValidRelative(relX, relY)) + { + item = null; + return false; + } + + var tile = layerField.GetTile(relX, relY); + item = new TileCheck(tile, absX, absY, relX, relY); + return true; + } + + private bool GetTile(MouseEventArgs e, LayerTerrain layerField, [NotNullWhen(true)] out TileCheck? item) + { + UpdateHoveredCoordinates(e); + var (absX, absY) = GetAbsoluteCoordinatesHoverTerrain(); + return GetTile(layerField, absX, absY, out item); + } + + private bool GetTile(LayerTerrain layerField, int absX, int absY, [NotNullWhen(true)] out TileCheck? item) + { + var cfg = Editor.Mutator.Manager.ConfigItems; + var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY); + if (!cfg.IsCoordinateValidRelative(relX, relY)) + { + item = null; + return false; + } + + var tile = layerField.GetTile(relX, relY); + item = new TileCheck(tile, absX, absY, relX, relY); + return true; + } + + private void UpdateHoveredCoordinates(MouseEventArgs e) + { + (HoverX, HoverY) = GetViewCoordinates(e); // Mouse event may fire with a slightly too large x/y; clamp just in case. HoverX &= 0x1F; HoverY &= 0x1F; } - private void GetAcreCoordinates(MouseEventArgs e, out int x, out int y) - { - x = e.X / View.AcreScale; - y = e.Y / View.AcreScale; - } + private void ViewportMouseDown(object sender, MouseEventArgs e) => ResetDrag(); - private void PB_Acre_MouseDown(object sender, MouseEventArgs e) => ResetDrag(); - - private void PB_Acre_MouseMove(object sender, MouseEventArgs e) + private void ViewportMouseMove(object sender, MouseEventArgs e) { - var l = Map.CurrentLayer; + var l = CurrentLayer; if (e.Button == MouseButtons.Left && CHK_MoveOnDrag.Checked) { MoveDrag(e); @@ -292,34 +386,49 @@ private void PB_Acre_MouseMove(object sender, MouseEventArgs e) OmniTileTerrain(e); } - var oldTile = l.GetTile(View.X + HoverX, View.Y + HoverY); - var tile = GetTile(l, e, out var x, out var y); - if (ReferenceEquals(tile, oldTile)) + // Update hover tooltip if it is a different tile + // Can't compare coordinates if redirection of extension tiles hijacks to return the root tile. + // Just check the (root) tile returns for each. + // Two different extension tiles can redirect to the same root tile, can skip updating in that case. + if (!GetTile(l, View.X + HoverX, View.Y + HoverY, out var old)) return; + + var oldTile = old.Tile; + if (!GetTile(e, l, out var dest)) + return; + + var tile = dest.Tile; + var x = dest.RelativeX; + var y = dest.RelativeY; + if (ReferenceEquals(tile, oldTile)) + return; // same tile, no change + + // Regenerate tooltip text var str = GameInfo.Strings; var name = str.GetItemName(tile); - bool active = Map.Items.GetIsActive(NUD_Layer.Value == 0, x, y); - if (active) + var flagLayer = NUD_Layer.Value == 0 ? Map.LayerItemFlag0 : Map.LayerItemFlag1; + var isActive = flagLayer.GetIsActive(x, y); + if (isActive) name = $"{name} [Active]"; - TT_Hover.SetToolTip(PB_Acre, name); + TT_Hover.SetToolTip(PB_Viewport, name); SetCoordinateText(x, y); } private void MoveDrag(MouseEventArgs e) { - GetAcreCoordinates(e, out var nhX, out var nhY); + var (viewX, viewY) = GetViewCoordinates(e); if (DragX == -1) { - DragX = nhX; - DragY = nhY; + DragX = viewX; + DragY = viewY; return; } - var dX = DragX - nhX; - var dY = DragY - nhY; + var dX = DragX - viewX; + var dY = DragY - viewY; - if (ModifierKeys == Keys.Control) + if (ModifierKeys == Keys.Control) // move in larger steps { dX *= 2; dY *= 2; @@ -330,30 +439,33 @@ private void MoveDrag(MouseEventArgs e) if ((dY & 1) == 1) dY ^= 1; + // Ensure movement is significant enough var aX = Math.Abs(dX); var aY = Math.Abs(dY); if (aX < 2 && aY < 2) return; - DragX = nhX; - DragY = nhY; - if (!View.SetViewTo(View.X + dX, View.Y + dY)) + DragX = viewX; + DragY = viewY; + if (!View.DragView(dX, dY)) return; - Dragging = true; + IsDragOperationActive = true; LoadItemGridAcre(); } - private void ViewTile(Item tile, int x, int y) + private void ViewTile(Item tile, int relX, int relY) { 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 redir = l.GetTile(rx, ry); - if (redir.IsRoot && redir.ItemId == tile.ExtensionItemId) - tile = redir; + var l = CurrentLayer; + relX -= tile.ExtensionX; + relY -= tile.ExtensionY; + l.TileInfo.ClampInside(ref relX, ref relY); + var redirectTile = l.GetTile(relX, relY); + + if (redirectTile.IsRoot && redirectTile.ItemId == tile.ExtensionItemId) + tile = redirectTile; } ViewTile(tile); @@ -373,21 +485,21 @@ private void ViewTile(TerrainTile tile) TC_Editor.SelectedTab = Tab_Terrain; } - private void SetTile(Item tile, int x, int y) + private void SetTile(Item tile, int relX, int relY) { - var l = Map.CurrentLayer; + var l = CurrentLayer; var pgt = new Item(); ItemEdit.SetItem(pgt); if (pgt.IsFieldItem && CHK_FieldItemSnap.Checked) { // coordinates must be even (not odd-half) - x &= 0xFFFE; - y &= 0xFFFE; - tile = l.GetTile(x, y); + relX &= 0xFFFE; + relY &= 0xFFFE; + tile = l.GetTile(relX, relY); } - var permission = l.IsOccupied(pgt, x, y); + var permission = l.IsOccupied(pgt, relX, relY); switch (permission) { case PlacedItemPermission.OutOfBounds: @@ -398,31 +510,31 @@ private void SetTile(Item tile, int x, int y) // Clean up original placed data if (tile.IsRoot && CHK_AutoExtension.Checked) - l.DeleteExtensionTiles(tile, x, y); + l.DeleteExtensionTiles(tile, relX, relY); // Set new placed data if (pgt.IsRoot && CHK_AutoExtension.Checked) - l.SetExtensionTiles(pgt, x, y); + l.SetExtensionTiles(pgt, relX, relY); tile.CopyFrom(pgt); ReloadItems(); } - private void ReplaceTile(Item tile, int x, int y) + private void ReplaceTile(Item tile, int relX, int relY) { - var l = Map.CurrentLayer; + var l = CurrentLayer; var pgt = new Item(); ItemEdit.SetItem(pgt); if (pgt.IsFieldItem && CHK_FieldItemSnap.Checked) { // coordinates must be even (not odd-half) - x &= 0xFFFE; - y &= 0xFFFE; - tile = l.GetTile(x, y); + relX &= 0xFFFE; + relY &= 0xFFFE; + tile = l.GetTile(relX, relY); } - var permission = l.IsOccupied(pgt, x, y); + var permission = l.IsOccupied(pgt, relX, relY); switch (permission) { case PlacedItemPermission.OutOfBounds: @@ -432,7 +544,7 @@ private void ReplaceTile(Item tile, int x, int y) bool wholeMap = (ModifierKeys & Keys.Shift) != 0; var copy = new Item(tile.RawValue); - var count = View.ReplaceFieldItems(copy, pgt, wholeMap); + var count = Editor.Mutator.ReplaceFieldItems(copy, pgt, wholeMap); if (count == 0) { WinFormsUtil.Alert(MessageStrings.MsgFieldItemModifyNone); @@ -444,8 +556,8 @@ private void ReplaceTile(Item tile, int x, int y) private void RotateTile(TerrainTile tile) { - bool rotated = tile.Rotate(); - if (!rotated) + bool wasRotated = tile.TryRotate(); + if (!wasRotated) { System.Media.SystemSounds.Asterisk.Play(); return; @@ -456,6 +568,8 @@ private void RotateTile(TerrainTile tile) private void SetTile(TerrainTile tile) { var pgt = (TerrainTile)PG_TerrainTile.SelectedObject!; + + // Apply randomization if enabled if (tbeForm?.RandomizeVariation == true) { switch (pgt.UnitModel) @@ -469,32 +583,30 @@ private void SetTile(TerrainTile tile) } tile.CopyFrom(pgt); - ReloadBuildingsTerrain(); } private void SetTiles(IEnumerable tiles) { var pgt = (TerrainTile)PG_TerrainTile.SelectedObject!; - foreach (TerrainTile tile in tiles) - { - tile.CopyFrom(pgt); - } + foreach (var tile in tiles) + tile.CopyFrom(pgt); ReloadBuildingsTerrain(); } - private void DeleteTile(Item tile, int x, int y) + private void DeleteTile(Item tile, int relX, int relY) { if (CHK_AutoExtension.Checked) { + var layer = CurrentLayer; if (!tile.IsRoot) { - x -= tile.ExtensionX; - y -= tile.ExtensionY; - tile = Map.CurrentLayer.GetTile(x, y); + relX -= tile.ExtensionX; + relY -= tile.ExtensionY; + tile = layer.GetTile(relX, relY); } - Map.CurrentLayer.DeleteExtensionTiles(tile, x, y); + layer.DeleteExtensionTiles(tile, relX, relY); } tile.Delete(); @@ -511,7 +623,8 @@ private void DeleteTile(TerrainTile tile) private void B_Save_Click(object sender, EventArgs e) { - var unsupported = Map.Items.GetUnsupportedTiles(); + var set = Map.FieldItems; + var unsupported = set.GetUnsupportedTiles(); if (unsupported.Count != 0) { var err = MessageStrings.MsgFieldItemUnsupportedLayer2Tile; @@ -521,102 +634,139 @@ private void B_Save_Click(object sender, EventArgs e) return; } - Map.Items.Save(); - SAV.SetTerrainTiles(Map.Terrain.Tiles); - - SAV.SetAcreBytes(Map.Terrain.BaseAcres); + Map.SetManager(SAV); SAV.OutsideFieldTemplateUniqueId = (ushort)NUD_MapAcreTemplateOutside.Value; SAV.MainFieldParamUniqueID = (ushort)NUD_MapAcreTemplateField.Value; - - SAV.Buildings = Map.Buildings; - SAV.EventPlazaLeftUpX = Map.PlazaX; - SAV.EventPlazaLeftUpZ = Map.PlazaY; Close(); } private void Menu_View_Click(object sender, EventArgs e) { - var x = View.X + HoverX; - var y = View.Y + HoverY; + var (absX, absY) = GetAbsoluteCoordinatesHover(); + var cfg = Editor.Mutator.Manager.ConfigItems; + var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY); + if (!cfg.IsCoordinateValidRelative(relX, relY)) + { + System.Media.SystemSounds.Asterisk.Play(); + return; + } if (RB_Item.Checked) { - var tile = Map.CurrentLayer.GetTile(x, y); - ViewTile(tile, x, y); + var tile = CurrentLayer.GetTile(relX, relY); + ViewTile(tile, relX, relY); } else if (RB_Terrain.Checked) { - var tile = Map.Terrain.GetTile(x / 2, y / 2); + var tile = Editor.Terrain.GetTile(relX, relY); ViewTile(tile); } } private void Menu_Set_Click(object sender, EventArgs e) { - var x = View.X + HoverX; - var y = View.Y + HoverY; - if (RB_Item.Checked) { - var tile = Map.CurrentLayer.GetTile(x, y); - SetTile(tile, x, y); + var (absX, absY) = GetAbsoluteCoordinatesHover(); + var cfg = Editor.Mutator.Manager.ConfigItems; + var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY); + if (!cfg.IsCoordinateValidRelative(relX, relY)) + { + System.Media.SystemSounds.Asterisk.Play(); + return; + } + + var tile = CurrentLayer.GetTile(relX, relY); + SetTile(tile, relX, relY); } else if (RB_Terrain.Checked) { - var tile = Map.Terrain.GetTile(x / 2, y / 2); + var (absX, absY) = GetAbsoluteCoordinatesHoverTerrain(); + var cfg = Editor.Mutator.Manager.ConfigTerrain; + var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY); + if (!cfg.IsCoordinateValidRelative(relX, relY)) + { + System.Media.SystemSounds.Asterisk.Play(); + return; + } + + var tile = Editor.Terrain.GetTile(relX, relY); SetTile(tile); } } private void Menu_Reset_Click(object sender, EventArgs e) { - var x = View.X + HoverX; - var y = View.Y + HoverY; - if (RB_Item.Checked) { - var tile = Map.CurrentLayer.GetTile(x, y); - DeleteTile(tile, x, y); + var (absX, absY) = GetAbsoluteCoordinatesHover(); + var cfg = Editor.Mutator.Manager.ConfigItems; + var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY); + if (!cfg.IsCoordinateValidRelative(relX, relY)) + { + System.Media.SystemSounds.Asterisk.Play(); + return; + } + + var tile = CurrentLayer.GetTile(relX, relY); + DeleteTile(tile, relX, relY); } else if (RB_Terrain.Checked) { - var tile = Map.Terrain.GetTile(x / 2, y / 2); + var (absX, absY) = GetAbsoluteCoordinatesHoverTerrain(); + var cfg = Editor.Mutator.Manager.ConfigTerrain; + var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY); + if (!cfg.IsCoordinateValidRelative(relX, relY)) + { + System.Media.SystemSounds.Asterisk.Play(); + return; + } + + var tile = Editor.Terrain.GetTile(relX, relY); DeleteTile(tile); } } - private bool hasActivate = true; - private void CM_Click_Opening(object sender, System.ComponentModel.CancelEventArgs e) { - if (!RB_Item.Checked) + if (!RB_Item.Checked) // not in Item edit mode, therefore no "Activate Flag" menu { - if (hasActivate) + if (IsMenuHasActivate) CM_Click.Items.Remove(Menu_Activate); - hasActivate = false; + IsMenuHasActivate = false; return; } - var isBase = NUD_Layer.Value == 0; - var x = View.X + HoverX; - var y = View.Y + HoverY; - Menu_Activate.Text = Map.Items.GetIsActive(isBase, x, y) ? "Inactivate" : "Activate"; + var (absX, absY) = GetAbsoluteCoordinatesHover(); + var cfg = Editor.Mutator.Manager.ConfigItems; + var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY); + if (!cfg.IsCoordinateValidRelative(relX, relY)) + return; + + var flagLayer = NUD_Layer.Value == 0 ? Map.LayerItemFlag0 : Map.LayerItemFlag1; + var isActive = flagLayer.GetIsActive(relX, relY); + Menu_Activate.Text = isActive ? "Inactivate" : "Activate"; CM_Click.Items.Add(Menu_Activate); - hasActivate = true; + IsMenuHasActivate = true; } private void Menu_Activate_Click(object sender, EventArgs e) { - var x = View.X + HoverX; - var y = View.Y + HoverY; - var isBase = NUD_Layer.Value == 0; - Map.Items.SetIsActive(isBase, x, y, !Map.Items.GetIsActive(isBase, x, y)); + var (absX, absY) = GetAbsoluteCoordinatesHover(); + var cfg = Editor.Mutator.Manager.ConfigItems; + var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY); + if (!cfg.IsCoordinateValidRelative(relX, relY)) + return; + + var flagLayer = NUD_Layer.Value == 0 ? Map.LayerItemFlag0 : Map.LayerItemFlag1; + var isActive = flagLayer.GetIsActive(relX, relY); + flagLayer.SetIsActive(relX, relY, !isActive); } 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 - Editor.Items.Layer0.TileInfo.Columns); else if (View.ArrowUp()) LoadItemGridAcre(); } @@ -640,63 +790,73 @@ 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 + Editor.Items.Layer0.TileInfo.Columns, CB_Acre.Items.Count - 1); else if (View.ArrowDown()) LoadItemGridAcre(); } - private void B_DumpAcre_Click(object sender, EventArgs e) => MapDumpHelper.DumpLayerAcreSingle(Map.CurrentLayer, AcreIndex, CB_Acre.Text, (int)NUD_Layer.Value); - - private void B_DumpAllAcres_Click(object sender, EventArgs e) => MapDumpHelper.DumpLayerAcreAll(Map.CurrentLayer); - - private void B_ImportAcre_Click(object sender, EventArgs e) + private void B_DumpAcreItem_Click(object sender, EventArgs e) { - var layer = Map.CurrentLayer; - if (!MapDumpHelper.ImportToLayerAcreSingle(layer, AcreIndex, CB_Acre.Text, (int)NUD_Layer.Value)) + var (relX, relY) = Editor.Mutator.Manager.ConfigItems.GetCoordinatesRelative(View.X, View.Y); + MapDumpHelper.DumpLayerAcreSingle(CurrentLayer, $"{View.X:000}-{View.Y:000}", (int)NUD_Layer.Value, relX, relY); + } + + private void B_DumpAllAcres_Click(object sender, EventArgs e) => MapDumpHelper.DumpLayerAcreAll(CurrentLayer); + + private void B_ImportAcreItem_Click(object sender, EventArgs e) + { + var (relX, relY) = Editor.Mutator.Manager.ConfigItems.GetCoordinatesRelative(View.X, View.Y); + var layer = CurrentLayer; + if (!MapDumpHelper.ImportToLayerAcreSingle(layer, $"{View.X:000}-{View.Y:000}", (int)NUD_Layer.Value, relX, relY)) return; - ChangeViewToAcre(AcreIndex); + ChangeViewToAcre(ExteriorAcreIndex); System.Media.SystemSounds.Asterisk.Play(); } private void B_ImportAllAcres_Click(object sender, EventArgs e) { - if (!MapDumpHelper.ImportToLayerAcreAll(Map.CurrentLayer)) + if (!MapDumpHelper.ImportToLayerAcreAll(CurrentLayer)) return; - ChangeViewToAcre(AcreIndex); + ChangeViewToAcre(ExteriorAcreIndex); System.Media.SystemSounds.Asterisk.Play(); } - private void B_DumpBuildings_Click(object sender, EventArgs e) => MapDumpHelper.DumpBuildings(Map.Buildings); + private void B_DumpBuildings_Click(object sender, EventArgs e) => MapDumpHelper.DumpBuildings(Editor.Buildings.Buildings); private void B_ImportBuildings_Click(object sender, EventArgs e) { - if (!MapDumpHelper.ImportBuildings(Map.Buildings)) + if (!MapDumpHelper.ImportBuildings(Editor.Buildings.Buildings)) return; - for (int i = 0; i < Map.Buildings.Count; i++) - LB_Items.Items[i] = Map.Buildings[i].ToString(); + for (int i = 0; i < Editor.Buildings.Count; i++) + LB_Items.Items[i] = Editor.Buildings[i].ToString(); LB_Items.SelectedIndex = 0; System.Media.SystemSounds.Asterisk.Play(); ReloadBuildingsTerrain(); } - private void B_DumpTerrainAcre_Click(object sender, EventArgs e) => MapDumpHelper.DumpTerrainAcre(Map.Terrain, AcreIndex, CB_Acre.Text); + private void B_DumpTerrainAcre_Click(object sender, EventArgs e) + { + var (relX, relY) = Editor.Mutator.Manager.ConfigTerrain.GetCoordinatesRelative(View.X / 2, View.Y / 2); + MapDumpHelper.DumpTerrainAcre(Editor.Terrain, $"{View.X:000}-{View.Y:000}", relX, relY); + } - private void B_DumpTerrainAll_Click(object sender, EventArgs e) => MapDumpHelper.DumpTerrainAll(Map.Terrain); + private void B_DumpTerrainAll_Click(object sender, EventArgs e) => MapDumpHelper.DumpTerrainAll(Editor.Terrain); private void B_ImportTerrainAcre_Click(object sender, EventArgs e) { - if (!MapDumpHelper.ImportTerrainAcre(Map.Terrain, AcreIndex, CB_Acre.Text)) + var (relX, relY) = Editor.Mutator.Manager.ConfigTerrain.GetCoordinatesRelative(View.X / 2, View.Y / 2); + if (!MapDumpHelper.ImportTerrainAcre(Editor.Terrain, $"{View.X:000}-{View.Y:000}", relX, relY)) return; - ChangeViewToAcre(AcreIndex); + ChangeViewToAcre(ExteriorAcreIndex); System.Media.SystemSounds.Asterisk.Play(); } private void B_ImportTerrainAll_Click(object sender, EventArgs e) { - if (!MapDumpHelper.ImportTerrainAll(Map.Terrain)) + if (!MapDumpHelper.ImportTerrainAll(Editor.Terrain)) return; - ChangeViewToAcre(AcreIndex); + ChangeViewToAcre(ExteriorAcreIndex); System.Media.SystemSounds.Asterisk.Play(); } @@ -744,81 +904,51 @@ private void PB_Map_MouseDown(object sender, MouseEventArgs e) { if (e.Button != MouseButtons.Left) return; - ClickMapAt(e, true); + ClickMapAt(e); } - private void ClickMapAt(MouseEventArgs e, bool skipLagCheck) + private void ClickMapAt(MouseEventArgs e) { - var layer = Map.Items.Layer1; - int mX = e.X; - int mY = e.Y; - bool centerReticle = CHK_SnapToAcre.Checked; - View.GetViewAnchorCoordinates(mX, mY, out var x, out var y, centerReticle); - x &= 0xFFFE; - y &= 0xFFFE; + var (absX, absY) = Editor.GetMapCoordinates(e.X, e.Y, CHK_SnapToAcre.Checked ? MapViewCoordinateRequest.SnapAcre : MapViewCoordinateRequest.Centered); - var acre = layer.GetAcre(x, y); - bool sameAcre = AcreIndex == acre; - if (!skipLagCheck) - { - if (CHK_SnapToAcre.Checked) - { - if (sameAcre) - return; - } - else - { - const int delta = 0; // disabled = 0 - var dx = Math.Abs(View.X - x); - var dy = Math.Abs(View.Y - y); - if (dx <= delta && dy <= delta && !sameAcre) - return; - } - } + // Truncate to root-node coordinates. The map is only 1px per tile, and nobody is wanting to click on extension-tiles. + absX &= 0xFFFE; + absY &= 0xFFFE; - if (!CHK_SnapToAcre.Checked) - { - if (View.SetViewTo(x, y)) - LoadItemGridAcre(); - return; - } - - if (!sameAcre) - CB_Acre.SelectedIndex = acre; + if (View.SetViewTo(absX, absY)) + LoadItemGridAcre(); } private void PB_Map_MouseMove(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { - ClickMapAt(e, false); + ClickMapAt(e); } else if (e.Button == MouseButtons.None) { - View.GetCursorCoordinates(e.X, e.Y, out var x, out var y); - SetCoordinateText(x, y); + var (absX, absY) = Editor.GetCursorCoordinates(e.X, e.Y); + SetCoordinateText(absX, absY); } } - private void SetCoordinateText(int x, int y) => L_Coordinates.Text = $"({x:000},{y:000}) = (0x{x:X2},0x{y:X2})"; + private void SetCoordinateText(int absX, int absY) => L_Coordinates.Text = $"({absX:000},{absY:000}) = (0x{absX:X2},0x{absY:X2})"; private void NUD_Layer_ValueChanged(object sender, EventArgs e) { - Map.MapLayer = (int)NUD_Layer.Value - 1; + View.ItemLayerIndex = (int)NUD_Layer.Value - 1; LoadItemGridAcre(); } private void Remove(ToolStripItem sender, Func removal) { - bool wholeMap = (ModifierKeys & Keys.Shift) != 0; - - string q = string.Format(MessageStrings.MsgFieldItemRemoveAsk, sender.Text); - var question = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, q); + var isModifyEntireMap = (ModifierKeys & Keys.Shift) != 0; + var message = string.Format(MessageStrings.MsgFieldItemRemoveAsk, sender.Text); + var question = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, message); if (question != DialogResult.Yes) return; - int count = View.ModifyFieldItems(removal, wholeMap); - + var count = Editor.Mutator.ModifyFieldItems(removal, isModifyEntireMap); if (count == 0) { WinFormsUtil.Alert(MessageStrings.MsgFieldItemRemoveNone); @@ -830,15 +960,13 @@ private void Remove(ToolStripItem sender, Func removal) private void Modify(ToolStripItem sender, Func action) { - bool wholeMap = (ModifierKeys & Keys.Shift) != 0; - - string q = string.Format(MessageStrings.MsgFieldItemModifyAsk, sender.Text); - var question = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, q); + var isModifyEntireMap = (ModifierKeys & Keys.Shift) != 0; + var message = string.Format(MessageStrings.MsgFieldItemModifyAsk, sender.Text); + var question = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, message); if (question != DialogResult.Yes) return; - int count = View.ModifyFieldItems(action, wholeMap); - + var count = Editor.Mutator.ModifyFieldItems(action, isModifyEntireMap); if (count == 0) { WinFormsUtil.Alert(MessageStrings.MsgFieldItemModifyNone); @@ -848,54 +976,46 @@ private void Modify(ToolStripItem sender, Func action) WinFormsUtil.Alert(string.Format(MessageStrings.MsgFieldItemModifyCount, count)); } - private void B_RemoveEditor_Click(object sender, EventArgs e) => Remove(B_RemoveEditor, (min, max, x, y) - => Map.CurrentLayer.RemoveAllLike(min, max, x, y, ItemEdit.SetItem(new Item()))); + private void B_RemoveEditor_Click(object sender, EventArgs e) + { + var item = ItemEdit.LoadFieldsToNewItem(); + var lambda = new Func((min, max, x, y) + => CurrentLayer.RemoveAllLike(min, max, x, y, item)); + Remove(B_RemoveEditor, lambda); + } - private void B_RemoveAllWeeds_Click(object sender, EventArgs e) => Remove(B_RemoveAllWeeds, Map.CurrentLayer.RemoveAllWeeds); + private void B_WaterFlowers_Click(object sender, EventArgs e) + { + var all = (ModifierKeys & Keys.Control) != 0; + var lambda = new Func((xmin, ymin, width, height) + => CurrentLayer.WaterAllFlowers(xmin, ymin, width, height, all)); + Modify(B_WaterFlowers, lambda); + } - private void B_RemoveAllTrees_Click(object sender, EventArgs e) => Remove(B_RemoveAllTrees, Map.CurrentLayer.RemoveAllTrees); - private void B_FillHoles_Click(object sender, EventArgs e) => Remove(B_FillHoles, Map.CurrentLayer.RemoveAllHoles); - - private void B_RemovePlants_Click(object sender, EventArgs e) => Remove(B_RemovePlants, Map.CurrentLayer.RemoveAllPlants); - - private void B_RemoveFences_Click(object sender, EventArgs e) => Remove(B_RemoveFences, Map.CurrentLayer.RemoveAllFences); - - private void B_RemoveObjects_Click(object sender, EventArgs e) => Remove(B_RemoveObjects, Map.CurrentLayer.RemoveAllObjects); - - private void B_RemoveAll_Click(object sender, EventArgs e) => Remove(B_RemoveAll, Map.CurrentLayer.RemoveAll); - - private void B_RemovePlacedItems_Click(object sender, EventArgs e) => Remove(B_RemovePlacedItems, Map.CurrentLayer.RemoveAllPlacedItems); - - private void B_RemoveShells_Click(object sender, EventArgs e) => Remove(B_RemoveShells, Map.CurrentLayer.RemoveAllShells); - - private void B_RemoveBranches_Click(object sender, EventArgs e) => Remove(B_RemoveBranches, Map.CurrentLayer.RemoveAllBranches); - - private void B_RemoveFlowers_Click(object sender, EventArgs e) => Remove(B_RemoveFlowers, Map.CurrentLayer.RemoveAllFlowers); - - private void B_RemoveBushes_Click(object sender, EventArgs e) => Remove(B_RemoveBushes, Map.CurrentLayer.RemoveAllBushes); - - private void B_WaterFlowers_Click(object sender, EventArgs e) => Modify(B_WaterFlowers, (xmin, ymin, width, height) - => Map.CurrentLayer.WaterAllFlowers(xmin, ymin, width, height, (ModifierKeys & Keys.Control) != 0)); + private void B_RemoveAllWeeds_Click(object sender, EventArgs e) => Remove(B_RemoveAllWeeds, CurrentLayer.RemoveAllWeeds); + private void B_RemoveAllTrees_Click(object sender, EventArgs e) => Remove(B_RemoveAllTrees, CurrentLayer.RemoveAllTrees); + private void B_FillHoles_Click(object sender, EventArgs e) => Remove(B_FillHoles, CurrentLayer.RemoveAllHoles); + private void B_RemovePlants_Click(object sender, EventArgs e) => Remove(B_RemovePlants, CurrentLayer.RemoveAllPlants); + private void B_RemoveFences_Click(object sender, EventArgs e) => Remove(B_RemoveFences, CurrentLayer.RemoveAllFences); + private void B_RemoveObjects_Click(object sender, EventArgs e) => Remove(B_RemoveObjects, CurrentLayer.RemoveAllObjects); + private void B_RemoveAll_Click(object sender, EventArgs e) => Remove(B_RemoveAll, CurrentLayer.RemoveAll); + private void B_RemovePlacedItems_Click(object sender, EventArgs e) => Remove(B_RemovePlacedItems, CurrentLayer.RemoveAllPlacedItems); + private void B_RemoveShells_Click(object sender, EventArgs e) => Remove(B_RemoveShells, CurrentLayer.RemoveAllShells); + private void B_RemoveBranches_Click(object sender, EventArgs e) => Remove(B_RemoveBranches, CurrentLayer.RemoveAllBranches); + private void B_RemoveFlowers_Click(object sender, EventArgs e) => Remove(B_RemoveFlowers, CurrentLayer.RemoveAllFlowers); + private void B_RemoveBushes_Click(object sender, EventArgs e) => Remove(B_RemoveBushes, CurrentLayer.RemoveAllBushes); private static void ShowContextMenuBelow(ToolStripDropDown c, Control n) => c.Show(n.PointToScreen(new Point(0, n.Height))); private void B_RemoveItemDropDown_Click(object sender, EventArgs e) => ShowContextMenuBelow(CM_Remove, B_RemoveItemDropDown); - private void B_DumpLoadField_Click(object sender, EventArgs e) => ShowContextMenuBelow(CM_DLField, B_DumpLoadField); - private void B_DumpLoadTerrain_Click(object sender, EventArgs e) => ShowContextMenuBelow(CM_DLTerrain, B_DumpLoadTerrain); - private void B_DumpLoadBuildings_Click(object sender, EventArgs e) => ShowContextMenuBelow(CM_DLBuilding, B_DumpLoadBuildings); - private void B_ModifyAllTerrain_Click(object sender, EventArgs e) => ShowContextMenuBelow(CM_Terrain, B_ModifyAllTerrain); - private void B_DumpLoadAcres_Click(object sender, EventArgs e) => ShowContextMenuBelow(CM_DLMapAcres, B_DumpLoadAcres); - private void TR_Transparency_Scroll(object sender, EventArgs e) => ReloadItems(); - private void TR_BuildingTransparency_Scroll(object sender, EventArgs e) => ReloadBuildingsTerrain(); - private void TR_Terrain_Scroll(object sender, EventArgs e) => ReloadBuildingsTerrain(); #region Buildings @@ -910,7 +1030,7 @@ private void NUD_PlazaX_ValueChanged(object sender, EventArgs e) { if (Loading) return; - Map.PlazaX = (uint)NUD_PlazaX.Value; + Map.Plaza.X = (uint)NUD_PlazaX.Value; ReloadBuildingsTerrain(); } @@ -918,7 +1038,7 @@ private void NUD_PlazaY_ValueChanged(object sender, EventArgs e) { if (Loading) return; - Map.PlazaY = (uint)NUD_PlazaY.Value; + Map.Plaza.Z = (uint)NUD_PlazaY.Value; ReloadBuildingsTerrain(); } @@ -926,18 +1046,18 @@ private void LB_Items_SelectedIndexChanged(object sender, EventArgs e) { if (LB_Items.SelectedIndex < 0) return; - LoadIndex(LB_Items.SelectedIndex); + LoadBuildingIndex(LB_Items.SelectedIndex); // View location snap has changed the view. Reload everything LoadItemGridAcre(); ReloadMapBackground(); } - private void LoadIndex(int index) + private void LoadBuildingIndex(int index) { Loading = true; SelectedBuildingIndex = index; - var b = Map.Buildings[index]; + var b = Editor.Buildings[index]; NUD_BuildingType.Value = (int)b.BuildingType; NUD_X.Value = b.X; NUD_Y.Value = b.Y; @@ -948,9 +1068,9 @@ private void LoadIndex(int index) NUD_UniqueID.Value = b.UniqueID; Loading = false; - // -32 for relative offset on map (buildings can be placed on the exterior ocean acres) + // Jump the view to see the building // -16 to put it in the center of the view - const int shift = 48; + const int shift = 16; var x = (b.X - shift) & 0xFFFE; var y = (b.Y - shift) & 0xFFFE; View.SetViewTo(x, y); @@ -961,7 +1081,7 @@ private void NUD_BuildingType_ValueChanged(object sender, EventArgs e) if (Loading || sender is not NumericUpDown n) return; - var b = Map.Buildings[SelectedBuildingIndex]; + var b = Editor.Buildings[SelectedBuildingIndex]; if (sender == NUD_BuildingType) b.BuildingType = (BuildingType)n.Value; else if (sender == NUD_X) @@ -979,7 +1099,7 @@ private void NUD_BuildingType_ValueChanged(object sender, EventArgs e) else if (sender == NUD_UniqueID) b.UniqueID = (ushort)n.Value; - LB_Items.Items[SelectedBuildingIndex] = Map.Buildings[SelectedBuildingIndex].ToString(); + LB_Items.Items[SelectedBuildingIndex] = Editor.Buildings[SelectedBuildingIndex].ToString(); ReloadBuildingsTerrain(); } @@ -989,7 +1109,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 = Editor.Terrain.BaseAcres.Span[CB_MapAcre.SelectedIndex * 2]; CB_MapAcreSelect.SelectedValue = (int)acre; // Jump view if available @@ -1005,7 +1125,8 @@ 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); + // u16[], but values are at most u8 each. + var span = Editor.Terrain.GetBaseAcreSpan(index); var oldValue = span[0]; if (value == oldValue) return; @@ -1016,7 +1137,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(Editor.Terrain.BaseAcres.Span)) return; ReloadBuildingsTerrain(); System.Media.SystemSounds.Asterisk.Play(); @@ -1024,7 +1145,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(Editor.Terrain.BaseAcres.Span)) return; ReloadBuildingsTerrain(); System.Media.SystemSounds.Asterisk.Play(); @@ -1036,7 +1157,7 @@ private void B_ZeroElevation_Click(object sender, EventArgs e) { if (DialogResult.Yes != WinFormsUtil.Prompt(MessageBoxButtons.YesNo, MessageStrings.MsgTerrainSetElevation0)) return; - foreach (var t in Map.Terrain.Tiles) + foreach (var t in Editor.Terrain.Tiles) t.Elevation = 0; ReloadBuildingsTerrain(); System.Media.SystemSounds.Asterisk.Play(); @@ -1049,7 +1170,7 @@ private void B_SetAllTerrain_Click(object sender, EventArgs e) var pgt = (TerrainTile)PG_TerrainTile.SelectedObject!; bool interiorOnly = DialogResult.Yes == WinFormsUtil.Prompt(MessageBoxButtons.YesNo, MessageStrings.MsgTerrainSetAllSkipExterior); - Map.Terrain.SetAll(pgt, interiorOnly); + Editor.Terrain.SetAll(pgt, interiorOnly); ReloadBuildingsTerrain(); System.Media.SystemSounds.Asterisk.Play(); @@ -1062,7 +1183,7 @@ private void B_SetAllRoadTiles_Click(object sender, EventArgs e) var pgt = (TerrainTile)PG_TerrainTile.SelectedObject!; bool interiorOnly = DialogResult.Yes == WinFormsUtil.Prompt(MessageBoxButtons.YesNo, MessageStrings.MsgTerrainSetAllSkipExterior); - Map.Terrain.SetAllRoad(pgt, interiorOnly); + Editor.Terrain.SetAllRoad(pgt, interiorOnly); ReloadBuildingsTerrain(); System.Media.SystemSounds.Asterisk.Play(); @@ -1070,7 +1191,7 @@ private void B_SetAllRoadTiles_Click(object sender, EventArgs e) private void B_ClearPlacedDesigns_Click(object sender, EventArgs e) { - MapManager.ClearDesignTiles(SAV); + SAV.ClearDesignTiles(); System.Media.SystemSounds.Asterisk.Play(); } @@ -1083,7 +1204,7 @@ private void B_ExportPlacedDesigns_Click(object sender, EventArgs e) return; string path = sfd.FileName; - var tiles = MapManager.ExportDesignTiles(SAV); + var tiles = SAV.MapDesignTileData.Span; File.WriteAllBytes(path, tiles); System.Media.SystemSounds.Asterisk.Play(); } @@ -1098,7 +1219,7 @@ private void B_ImportPlacedDesigns_Click(object sender, EventArgs e) string path = ofd.FileName; var tiles = File.ReadAllBytes(path); - MapManager.ImportDesignTiles(SAV, tiles); + tiles.CopyTo(SAV.MapDesignTileData.Span); System.Media.SystemSounds.Asterisk.Play(); } @@ -1106,9 +1227,9 @@ private void B_ImportPlacedDesigns_Click(object sender, EventArgs e) private void Menu_Bulk_Click(object sender, EventArgs e) { - var editor = new BatchEditor(SpawnLayer.Tiles, ItemEdit.SetItem(new Item())); + var editor = new BatchEditor(Spawn.Tiles, ItemEdit.SetItem(new Item())); editor.ShowDialog(); - SpawnLayer.ClearDanglingExtensions(0, 0, SpawnLayer.MaxWidth, SpawnLayer.MaxHeight); + Spawn.ClearDanglingExtensions(0, 0, Spawn.TileInfo.TotalWidth, Spawn.TileInfo.TotalHeight); LoadItemGridAcre(); } @@ -1129,5 +1250,5 @@ public interface IItemLayerEditor void ReloadItems(); ItemEditor ItemProvider { get; } - ItemLayer SpawnLayer { get; } + LayerItem Spawn { get; } } \ No newline at end of file diff --git a/NHSE.WinForms/Subforms/Map/FieldItemEditor.resx b/NHSE.WinForms/Subforms/Map/FieldItemEditor.resx index b5a05c3..08f9278 100644 --- a/NHSE.WinForms/Subforms/Map/FieldItemEditor.resx +++ b/NHSE.WinForms/Subforms/Map/FieldItemEditor.resx @@ -1,17 +1,17 @@  - diff --git a/NHSE.WinForms/Subforms/Map/MapDumpHelper.cs b/NHSE.WinForms/Subforms/Map/MapDumpHelper.cs index ba10465..c04cda9 100644 --- a/NHSE.WinForms/Subforms/Map/MapDumpHelper.cs +++ b/NHSE.WinForms/Subforms/Map/MapDumpHelper.cs @@ -8,18 +8,19 @@ namespace NHSE.WinForms; public static class MapDumpHelper { - public static bool ImportToLayerAcreSingle(FieldItemLayer layer, int acreIndex, string acre, int layerIndex) + public static bool ImportToLayerAcreSingle(LayerFieldItem layerField, string acre, int layerIndex, int relX, int relY) { + var name = GetBaseFileName(acre, "acre"); using var ofd = new OpenFileDialog(); ofd.Filter = "New Horizons Field Item Layer (*.nhl)|*.nhl|All files (*.*)|*.*"; - ofd.FileName = $"{acre}-{layerIndex}.nhl"; + ofd.FileName = $"{name}-{layerIndex}.nhl"; if (ofd.ShowDialog() != DialogResult.OK) return false; var path = ofd.FileName; var fi = new FileInfo(path); - int expect = layer.GridTileCount * Item.SIZE; + int expect = layerField.TileInfo.ViewCount * Item.SIZE; if (fi.Length != expect) { WinFormsUtil.Error(string.Format(MessageStrings.MsgDataSizeMismatchImport, fi.Length, expect)); @@ -27,11 +28,19 @@ public static bool ImportToLayerAcreSingle(FieldItemLayer layer, int acreIndex, } var data = File.ReadAllBytes(path); - layer.ImportAcre(acreIndex, data); + var importedCount = layerField.ImportAcre(data, relX, relY); return true; } - public static bool ImportToLayerAcreAll(FieldItemLayer layer) + private static string GetBaseFileName(string initialName, string fallback) + { + var result = StringUtil.CleanFileName(initialName); + if (string.IsNullOrEmpty(result)) + return fallback; + return result; + } + + public static bool ImportToLayerAcreAll(LayerFieldItem layerField) { using var ofd = new OpenFileDialog(); ofd.Filter = "New Horizons Field Item Layer (*.nhl)|*.nhl|All files (*.*)|*.*"; @@ -41,33 +50,34 @@ public static bool ImportToLayerAcreAll(FieldItemLayer layer) var path = ofd.FileName; var fi = new FileInfo(path); - - int expect = layer.MaxTileCount * Item.SIZE; - if (fi.Length != expect) + var expect = layerField.TileInfo.TotalCount * Item.SIZE; + if (fi.Length != expect && !FieldItemUpgrade.IsUpdateNeeded(fi.Length, expect)) { WinFormsUtil.Error(string.Format(MessageStrings.MsgDataSizeMismatchImport, fi.Length, expect)); return false; } var data = File.ReadAllBytes(path); - layer.ImportAll(data); + FieldItemUpgrade.DetectUpdate(ref data, expect); + layerField.ImportAll(data); return true; } - public static void DumpLayerAcreSingle(FieldItemLayer layer, int acreIndex, string acre, int layerIndex) + public static void DumpLayerAcreSingle(LayerFieldItem layerField, string acre, int layerIndex, int relX, int relY) { + var name = GetBaseFileName(acre, "acre"); using var sfd = new SaveFileDialog(); sfd.Filter = "New Horizons Field Item Layer (*.nhl)|*.nhl|All files (*.*)|*.*"; - sfd.FileName = $"{acre}-{layerIndex}.nhl"; + sfd.FileName = $"{name}-{layerIndex}.nhl"; if (sfd.ShowDialog() != DialogResult.OK) return; var path = sfd.FileName; - var data = layer.DumpAcre(acreIndex); + var data = layerField.DumpAcre(relX, relY); File.WriteAllBytes(path, data); } - public static void DumpLayerAcreAll(FieldItemLayer layer) + public static void DumpLayerAcreAll(LayerFieldItem layerField) { using var sfd = new SaveFileDialog(); sfd.Filter = "New Horizons Field Item Layer (*.nhl)|*.nhl|All files (*.*)|*.*"; @@ -76,22 +86,23 @@ public static void DumpLayerAcreAll(FieldItemLayer layer) return; var path = sfd.FileName; - var data = layer.DumpAll(); + var data = layerField.DumpAll(); File.WriteAllBytes(path, data); } - public static bool ImportTerrainAcre(TerrainLayer m, int acreIndex, string acre) + public static bool ImportTerrainAcre(LayerTerrain m, string acre, int relX, int relY) { + var name = GetBaseFileName(acre, "acre"); using var ofd = new OpenFileDialog(); ofd.Filter = "New Horizons Terrain (*.nht)|*.nht|All files (*.*)|*.*"; - ofd.FileName = $"{acre}.nht"; + ofd.FileName = $"{name}.nht"; if (ofd.ShowDialog() != DialogResult.OK) return false; 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)); @@ -99,11 +110,11 @@ public static bool ImportTerrainAcre(TerrainLayer m, int acreIndex, string acre) } var data = File.ReadAllBytes(path); - m.ImportAcre(acreIndex, data); + int importedCount = m.ImportAcre(data, relX, relY); return true; } - public static bool ImportTerrainAll(TerrainLayer m) + public static bool ImportTerrainAll(LayerTerrain m) { using var ofd = new OpenFileDialog(); ofd.Filter = "New Horizons Terrain (*.nht)|*.nht|All files (*.*)|*.*"; @@ -114,7 +125,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)); @@ -126,20 +137,21 @@ public static bool ImportTerrainAll(TerrainLayer m) return true; } - public static void DumpTerrainAcre(TerrainLayer m, int acreIndex, string acre) + public static void DumpTerrainAcre(LayerTerrain m, string acre, int relX, int relY) { + var name = GetBaseFileName(acre, "terrainAcre"); using var sfd = new SaveFileDialog(); sfd.Filter = "New Horizons Terrain (*.nht)|*.nht|All files (*.*)|*.*"; - sfd.FileName = $"{acre}.nht"; + sfd.FileName = $"{name}.nht"; if (sfd.ShowDialog() != DialogResult.OK) return; var path = sfd.FileName; - var data = m.DumpAcre(acreIndex); + var data = m.DumpAcre(relX, relY); File.WriteAllBytes(path, data); } - public static void DumpTerrainAll(TerrainLayer m) + public static void DumpTerrainAll(LayerTerrain m) { using var sfd = new SaveFileDialog(); sfd.Filter = "New Horizons Terrain (*.nht)|*.nht|All files (*.*)|*.*"; diff --git a/NHSE.WinForms/Subforms/Map/MiscDumpHelper.cs b/NHSE.WinForms/Subforms/Map/MiscDumpHelper.cs index 15ebdba..37ce4a3 100644 --- a/NHSE.WinForms/Subforms/Map/MiscDumpHelper.cs +++ b/NHSE.WinForms/Subforms/Map/MiscDumpHelper.cs @@ -85,7 +85,7 @@ public static bool LoadMuseum(Museum museum) } var data = File.ReadAllBytes(file); - data.AsSpan().CopyTo(museum.Data); + data.CopyTo(museum.Data); return true; } diff --git a/NHSE.WinForms/Subforms/Map/PlayerHouseEditor.cs b/NHSE.WinForms/Subforms/Map/PlayerHouseEditor.cs index c330af6..840a1ff 100644 --- a/NHSE.WinForms/Subforms/Map/PlayerHouseEditor.cs +++ b/NHSE.WinForms/Subforms/Map/PlayerHouseEditor.cs @@ -12,7 +12,7 @@ public partial class PlayerHouseEditor : Form private readonly MainSave SAV; private readonly IPlayerHouse[] Houses; private readonly IReadOnlyList Players; - private RoomItemManager Manager; + private RoomManager Manager; private const int scale = 24; private int Index = -1; @@ -25,7 +25,7 @@ public PlayerHouseEditor(IPlayerHouse[] houses, IReadOnlyList players, M SAV = sav; Houses = houses; Players = players; - Manager = new RoomItemManager(houses[0].GetRoom(0)); + Manager = new RoomManager(houses[0].GetRoom(0)); var data = GameInfo.Strings.ItemDataSource; ItemEdit.Initialize(data, true); @@ -111,7 +111,7 @@ private void B_EditFlags_Click(object sender, EventArgs e) private void PB_Room_MouseMove(object sender, MouseEventArgs e) { - var l = CurrentLayer; + var l = Current; var oldTile = l.GetTile(HoverX, HoverY); var tile = GetTile(l, e, out var x, out var y); if (ReferenceEquals(tile, oldTile)) @@ -124,7 +124,7 @@ private void PB_Room_MouseMove(object sender, MouseEventArgs e) private void SetCoordinateText(int x, int y, string name) => L_Coordinates.Text = $"({x:000},{y:000}) = {name}"; - private Item GetTile(ItemLayer layer, MouseEventArgs e, out int x, out int y) + private Item GetTile(LayerItem layer, MouseEventArgs e, out int x, out int y) { SetHoveredItem(e); return layer.GetTile(x = HoverX, y = HoverY); @@ -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) @@ -157,19 +157,23 @@ private void ReloadManager(IPlayerHouse house) if (unsupported.Count != 0) WinFormsUtil.Alert(MessageStrings.MsgFieldItemUnsupportedLayer2Tile); var room = house.GetRoom(RoomIndex); - Manager = new RoomItemManager(room); + Manager = new RoomManager(room); } - private void DrawLayer() => DrawRoom(CurrentLayer); + private void DrawLayer() => DrawRoom(Current); - private void DrawRoom(ItemLayer layer) + private void DrawRoom(LayerItem layer) { - var w = layer.MaxWidth; - var h = layer.MaxHeight; + var w = layer.TileInfo.TotalWidth; + var h = layer.TileInfo.TotalHeight; Span scale1 = stackalloc int[w * h]; - int[] scaleX = new int[scale * scale * scale1.Length]; + var scaleX = new int[scale * scale * scale1.Length]; var bmp = new Bitmap(scale * w, scale * h); - PB_Room.Image = ItemLayerSprite.GetBitmapItemLayerViewGrid(layer, 0, 0, scale, scale1, scaleX, bmp, gridlineColor: 0x7F000000); + + // 10x10 items (2x2 tiles per item) + var cfg = LayerPositionConfig.Create(1, 1, 20, 1, 0, 0); + ItemLayerSprite.LoadViewport(bmp, layer, cfg, 0, 0, scale1, scaleX, scale, gridlineColor: 0x7F000000); + PB_Room.Image = bmp; } private void NUD_Room_ValueChanged(object sender, EventArgs e) @@ -186,7 +190,7 @@ private void NUD_Layer_ValueChanged(object sender, EventArgs e) private void PlayerHouseEditor_Click(object sender, MouseEventArgs e) { - var tile = GetTile(CurrentLayer, e, out var x, out var y); + var tile = GetTile(Current, e, out var x, out var y); OmniTile(tile, x, y); } @@ -211,7 +215,7 @@ private void Menu_View_Click(object sender, EventArgs e) var x = HoverX; var y = HoverY; - var tile = CurrentLayer.GetTile(x, y); + var tile = Current.GetTile(x, y); ViewTile(tile, x, y); } @@ -220,7 +224,7 @@ private void Menu_Set_Click(object sender, EventArgs e) var x = HoverX; var y = HoverY; - var tile = CurrentLayer.GetTile(x, y); + var tile = Current.GetTile(x, y); SetTile(tile, x, y); } @@ -229,19 +233,19 @@ private void Menu_Reset_Click(object sender, EventArgs e) var x = HoverX; var y = HoverY; - var tile = CurrentLayer.GetTile(x, y); + var tile = Current.GetTile(x, y); DeleteTile(tile, x, y); } - private ItemLayer CurrentLayer => Manager.Layers[(int)NUD_Layer.Value - 1]; + private LayerItem Current => Manager.Layers[(int)NUD_Layer.Value - 1]; 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 l = Current; + 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; @@ -257,7 +261,7 @@ private void ViewTile(Item tile) private void SetTile(Item tile, int x, int y) { - var l = CurrentLayer; + var l = Current; var pgt = new Item(); ItemEdit.SetItem(pgt); var permission = l.IsOccupied(pgt, x, y); @@ -289,9 +293,9 @@ private void DeleteTile(Item tile, int x, int y) { x -= tile.ExtensionX; y -= tile.ExtensionY; - tile = CurrentLayer.GetTile(x, y); + tile = Current.GetTile(x, y); } - CurrentLayer.DeleteExtensionTiles(tile, x, y); + Current.DeleteExtensionTiles(tile, x, y); } tile.Delete(); diff --git a/NHSE.WinForms/Subforms/SysBot/SimpleHexEditor.cs b/NHSE.WinForms/Subforms/SysBot/SimpleHexEditor.cs index 31cdb27..b7157ee 100644 --- a/NHSE.WinForms/Subforms/SysBot/SimpleHexEditor.cs +++ b/NHSE.WinForms/Subforms/SysBot/SimpleHexEditor.cs @@ -1,7 +1,7 @@ -using System.Linq; +using System; +using System.Linq; using System.Windows.Forms; using NHSE.Core; -using NHSE.Injection; namespace NHSE.WinForms; @@ -17,10 +17,10 @@ public SimpleHexEditor(byte[] originalBytes) Bytes = originalBytes; } - private void Update_Click(object sender, System.EventArgs e) + private void Update_Click(object sender, EventArgs e) { var bytestring = RTB_RAM.Text.Replace("\t", "").Replace(" ", "").Trim(); - Bytes = Decoder.StringToByteArray(bytestring); + Bytes = Convert.FromHexString(bytestring); DialogResult = DialogResult.OK; Close(); } diff --git a/NHSE.WinForms/Subforms/SysBot/SysBotController.cs b/NHSE.WinForms/Subforms/SysBot/SysBotController.cs index dd75399..266421f 100644 --- a/NHSE.WinForms/Subforms/SysBot/SysBotController.cs +++ b/NHSE.WinForms/Subforms/SysBot/SysBotController.cs @@ -5,16 +5,13 @@ namespace NHSE.WinForms; -public class SysBotController +public sealed class SysBotController(InjectionType type) { - public SysBotController(InjectionType type) => Type = type; - - private readonly InjectionType Type; public readonly SysBot Bot = new(); - private readonly Settings Settings = Settings.Default; + private readonly Settings _settings = Settings.Default; - public string IP => Settings.SysBotIP; - public string Port => Settings.SysBotPort.ToString(); + public string IP => _settings.SysBotIP; + public string Port => _settings.SysBotPort.ToString(); public bool Connect(string ip, string port) { @@ -31,35 +28,29 @@ public bool Connect(string ip, string port) return false; } - var settings = Settings; - settings.SysBotIP = ip; - settings.SysBotPort = p; - settings.Save(); + _settings.SysBotIP = ip; + _settings.SysBotPort = p; + _settings.Save(); return true; } - public uint GetDefaultOffset() + public uint GetDefaultOffset() => type switch { - var settings = Settings; - return Type switch - { - InjectionType.Generic => settings.SysBotGenericOffset, - InjectionType.Pouch => settings.SysBotPouchOffset, - _ => throw new ArgumentOutOfRangeException() - }; - } + InjectionType.Generic => _settings.SysBotGenericOffset, + InjectionType.Pouch => _settings.SysBotPouchOffset, + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null), + }; public void SetOffset(uint value) { - var settings = Settings; - switch (Type) + switch (type) { - case InjectionType.Generic: settings.SysBotGenericOffset = value; break; - case InjectionType.Pouch: settings.SysBotPouchOffset = value; break; + case InjectionType.Generic: _settings.SysBotGenericOffset = value; break; + case InjectionType.Pouch: _settings.SysBotPouchOffset = value; break; default: return; } - settings.Save(); + _settings.Save(); } public void HexEdit(uint offset, int length) @@ -87,12 +78,12 @@ public void HexEdit(uint offset, int length) public void PopPrompt() { - if (Settings.SysBotPrompted) + if (_settings.SysBotPrompted) return; WinFormsUtil.Alert(MessageStrings.MsgSysBotInfo, MessageStrings.MsgSysBotRequired); - Settings.SysBotPrompted = true; - Settings.Save(); + _settings.SysBotPrompted = true; + _settings.Save(); } public void WriteBytes(byte[] data, uint offset) diff --git a/NHSE.WinForms/Subforms/SysBot/USBBotController.cs b/NHSE.WinForms/Subforms/SysBot/USBBotController.cs index 5b9ea18..0dda797 100644 --- a/NHSE.WinForms/Subforms/SysBot/USBBotController.cs +++ b/NHSE.WinForms/Subforms/SysBot/USBBotController.cs @@ -3,7 +3,7 @@ namespace NHSE.WinForms; -public class USBBotController +public sealed class USBBotController { public readonly USBBot Bot = new(); @@ -20,10 +20,7 @@ public bool Connect() } } - public void Disconnect() - { - Bot.Disconnect(); - } + public void Disconnect() => Bot.Disconnect(); //todo: this //public uint GetDefaultOffset() diff --git a/NHSE.WinForms/Util/InterpolatingPictureBox.cs b/NHSE.WinForms/Util/InterpolatingPictureBox.cs index 013301e..a9e3db2 100644 --- a/NHSE.WinForms/Util/InterpolatingPictureBox.cs +++ b/NHSE.WinForms/Util/InterpolatingPictureBox.cs @@ -3,7 +3,7 @@ namespace NHSE.WinForms; -public class InterpolatingPictureBox : PictureBox +public sealed class InterpolatingPictureBox : PictureBox { private readonly InterpolationMode InterpolationMode = InterpolationMode.HighQualityBicubic;