mirror of
https://github.com/kwsch/NHSE.git
synced 2026-04-23 08:57:42 -05:00
stash
rewritten core side to include tons more metadata/structure need to rewrite renderer to output full map image rather than skipping sea
This commit is contained in:
parent
9a54d0fb9a
commit
50ce05af6f
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -11,8 +11,8 @@ namespace NHSE.Core;
|
|||
/// <param name="AcreHeight">Always 6.</param>
|
||||
public sealed record FieldItemDropper(int AcreWidth, int AcreHeight = 6)
|
||||
{
|
||||
private int MapHeight => AcreWidth * FieldItemLayer.TilesPerAcreDim;
|
||||
private int MapWidth => AcreHeight * FieldItemLayer.TilesPerAcreDim;
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ public static class FieldItemUpgrade
|
|||
private const int ColumnCountNew = 9; // +1 column on each side
|
||||
private const int RowCount = 6;
|
||||
|
||||
private const byte SizeDim = FieldItemLayer.TilesPerAcreDim;
|
||||
private const byte SizeDim = LayerFieldItem.TilesPerAcreDim;
|
||||
private const int TilesPerAcre = SizeDim * SizeDim;
|
||||
private const int FieldItemSizeOld = (ColumnCountOld * RowCount) * TilesPerAcre * Item.SIZE;
|
||||
private const int FieldItemSizeNew = (ColumnCountNew * RowCount) * TilesPerAcre * Item.SIZE;
|
||||
private const int FieldItemSizeSingleColumn = RowCount * TilesPerAcre * Item.SIZE;
|
||||
private const int FieldItemSizeOld = ColumnCountOld * FieldItemSizeSingleColumn;
|
||||
private const int FieldItemSizeNew = ColumnCountNew * FieldItemSizeSingleColumn;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an update is needed based on the current size and expected size.
|
||||
|
|
|
|||
|
|
@ -186,12 +186,17 @@ public void SetAcreBytes(ReadOnlySpan<byte> data)
|
|||
data.CopyTo(Data[Offsets.OutsideField..]);
|
||||
}
|
||||
|
||||
|
||||
#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 = TerrainLayer.TilesPerAcreDim * TerrainLayer.TilesPerAcreDim * (7 * 6);
|
||||
private int TotalFieldItemTileCount => FieldItemLayer.TilesPerAcreDim * FieldItemLayer.TilesPerAcreDim * FieldItemAcreCount;
|
||||
|
||||
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<TerrainTile> array) => TerrainTile.SetArray(array).CopyTo(Data[Offsets.LandMakingMap..]);
|
||||
|
|
@ -212,17 +217,19 @@ public void ClearDesignTiles()
|
|||
private int FieldItemLayerSize => TotalFieldItemTileCount * Item.SIZE;
|
||||
private int FieldItemFlagSize => TotalFieldItemTileCount / sizeof(byte); // bitflags
|
||||
|
||||
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 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<byte> FieldItemFlag0Data => Data.Slice(FieldItemFlag0, FieldItemFlagSize).ToArray();
|
||||
public Memory<byte> FieldItemFlag1Data => Data.Slice(FieldItemFlag1, FieldItemFlagSize).ToArray();
|
||||
|
||||
public Item[] GetFieldItemLayer0() => Item.GetArray(Data.Slice(FieldItemLayer0, FieldItemLayerSize));
|
||||
public void SetFieldItemLayer0(IReadOnlyList<Item> array) => Item.SetArray(array).CopyTo(Data[FieldItemLayer0..]);
|
||||
|
||||
public Item[] GetFieldItemLayer1() => Item.GetArray(Data.Slice(FieldItemLayer1, FieldItemLayerSize));
|
||||
public void SetFieldItemLayer1(IReadOnlyList<Item> array) => Item.SetArray(array).CopyTo(Data[FieldItemLayer1..]);
|
||||
|
||||
public Item[] GetFieldItemLayer2() => Item.GetArray(Data.Slice(FieldItemLayer2, FieldItemLayerSize));
|
||||
public void SetFieldItemLayer2(IReadOnlyList<Item> array) => Item.SetArray(array).CopyTo(Data[FieldItemLayer2..]);
|
||||
|
||||
public ushort OutsideFieldTemplateUniqueId
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data[(Offsets.OutsideField + AcreSizeAll)..]);
|
||||
|
|
|
|||
33
NHSE.Core/Structures/Map/Layers/ILayerBuilding.cs
Normal file
33
NHSE.Core/Structures/Map/Layers/ILayerBuilding.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for interacting with a map's building layer.
|
||||
/// </summary>
|
||||
public interface ILayerBuilding
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiated list of all buildings on the map.
|
||||
/// </summary>
|
||||
IReadOnlyList<Building> Buildings { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Converts relative building coordinates to absolute coordinates in the map grid.
|
||||
/// </summary>
|
||||
/// <param name="relX">Relative building X coordinate</param>
|
||||
/// <param name="relY">Relative building Y coordinate</param>
|
||||
/// <returns>Map absolute X/Y coordinates</returns>
|
||||
(int X, int Y) GetCoordinatesAbsolute(ushort relX, ushort relY);
|
||||
|
||||
/// <summary>
|
||||
/// Converts absolute map grid coordinates to relative building coordinates.
|
||||
/// </summary>
|
||||
/// <param name="absX">Map absolute X coordinate</param>
|
||||
/// <param name="absY">Map absolute Y coordinate</param>
|
||||
/// <returns>Relative building X/Y coordinates</returns>
|
||||
(int X, int Y) GetCoordinatesRelative(int absX, int absY);
|
||||
|
||||
Building this[int i] { get; }
|
||||
int Count { get; }
|
||||
}
|
||||
48
NHSE.Core/Structures/Map/Layers/ILayerFieldItemFlag.cs
Normal file
48
NHSE.Core/Structures/Map/Layers/ILayerFieldItemFlag.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for managing field item flags within a layer.
|
||||
/// </summary>
|
||||
public interface ILayerFieldItemFlag
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the active state of the field item flag at the specified relative coordinates.
|
||||
/// </summary>
|
||||
/// <param name="relX">Relative X coordinate within the array.</param>
|
||||
/// <param name="relY">Relative Y coordinate within the array.</param>
|
||||
/// <returns>The active state of the field item flag.</returns>
|
||||
bool GetIsActive(int relX, int relY);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the active state of the field item flag at the specified relative coordinates.
|
||||
/// </summary>
|
||||
/// <param name="relX">Relative X coordinate within the array.</param>
|
||||
/// <param name="relY">Relative Y coordinate within the array.</param>
|
||||
/// <param name="value">The active state to set.</param>
|
||||
void SetIsActive(int relX, int relY, bool value = true);
|
||||
|
||||
bool this[int relX, int relY]
|
||||
{
|
||||
get => GetIsActive(relX, relY);
|
||||
set => SetIsActive(relX, relY, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deactivates all field item flags.
|
||||
/// </summary>
|
||||
void DeactivateAll();
|
||||
|
||||
/// <summary>
|
||||
/// Saves the (in)active flags into the provided destination span.
|
||||
/// </summary>
|
||||
/// <param name="dest">Destination span to save the flags into.</param>
|
||||
void Save(Span<byte> dest);
|
||||
|
||||
/// <summary>
|
||||
/// Imports the (in)active flags from the provided source span.
|
||||
/// </summary>
|
||||
/// <param name="src">Source span containing the flags to import.</param>
|
||||
void Import(Span<byte> src);
|
||||
}
|
||||
27
NHSE.Core/Structures/Map/Layers/ILayerFieldItemSet.cs
Normal file
27
NHSE.Core/Structures/Map/Layers/ILayerFieldItemSet.cs
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists out all coordinates of tiles present in <see cref="Layer1"/> that don't have anything underneath in <see cref="Layer0"/> to support them.
|
||||
/// </summary>
|
||||
List<string> GetUnsupportedTiles(int totalWidth, int totalHeight);
|
||||
|
||||
List<string> GetUnsupportedTiles() => GetUnsupportedTiles(Layer0.TileInfo.TotalWidth, Layer0.TileInfo.TotalHeight);
|
||||
}
|
||||
39
NHSE.Core/Structures/Map/Layers/LayerBuilding.cs
Normal file
39
NHSE.Core/Structures/Map/Layers/LayerBuilding.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for interacting with a map's building layer.
|
||||
/// </summary>
|
||||
public sealed record LayerBuilding : ILayerBuilding
|
||||
{
|
||||
public required IReadOnlyList<Building> 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.
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
namespace NHSE.Core;
|
||||
|
||||
public sealed record FieldItemLayer(Item[] Tiles, byte AcreWidth, byte AcreHeight)
|
||||
: ItemLayer(Tiles, GetViewport(AcreWidth, AcreHeight))
|
||||
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);
|
||||
|
||||
|
|
@ -116,4 +116,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);
|
||||
}
|
||||
}
|
||||
60
NHSE.Core/Structures/Map/Layers/LayerFieldItemFlag.cs
Normal file
60
NHSE.Core/Structures/Map/Layers/LayerFieldItemFlag.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for managing field item flags within a layer.
|
||||
/// </summary>
|
||||
public sealed class LayerFieldItemFlag(Memory<byte> raw, int width, int height) : ILayerFieldItemFlag
|
||||
{
|
||||
public Span<byte> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Although the Field Item Tiles are arranged y-column (y-x) based, the 'IsActive' flags are arranged x-row (x-y) based.
|
||||
/// </summary>
|
||||
private int GetLayerFlagIndex(int x, int y) => (y * width) + x;
|
||||
|
||||
public void DeactivateAll() => Data.Clear();
|
||||
|
||||
public void Save(Span<byte> dest) => Data.CopyTo(dest);
|
||||
|
||||
public void Import(Span<byte> src) => src.CopyTo(Data);
|
||||
|
||||
/// <summary>
|
||||
/// Diagnostic check of the active flags against the tiles.
|
||||
/// </summary>
|
||||
/// <param name="tiles">Tiles to check against.</param>
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
NHSE.Core/Structures/Map/Layers/LayerFieldItemSet.cs
Normal file
59
NHSE.Core/Structures/Map/Layers/LayerFieldItemSet.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Manages the <see cref="Item"/> data for the player's outside overworld.
|
||||
/// </summary>
|
||||
public sealed class LayerFieldItemSet : ILayerFieldItemSet
|
||||
{
|
||||
/// <summary>
|
||||
/// Base layer of items
|
||||
/// </summary>
|
||||
public required LayerFieldItem Layer0 { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Layer of items that are supported by <see cref="Layer0"/>
|
||||
/// </summary>
|
||||
public required LayerFieldItem Layer1 { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Lists out all coordinates of tiles present in <see cref="Layer1"/> that don't have anything underneath in <see cref="Layer0"/> to support them.
|
||||
/// </summary>
|
||||
public List<string> GetUnsupportedTiles(int totalWidth, int totalHeight)
|
||||
{
|
||||
var result = new List<string>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,21 +4,24 @@
|
|||
|
||||
namespace NHSE.Core;
|
||||
|
||||
public abstract record ItemLayer : AcreSelectionGrid
|
||||
public abstract record LayerItem : AcreSelectionGrid
|
||||
{
|
||||
public readonly Item[] Tiles;
|
||||
|
||||
protected ItemLayer(Item[] tiles, [ConstantExpected] byte w, [ConstantExpected] byte h) : this(tiles, new(w, h, w, h))
|
||||
#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 ItemLayer(Item[] tiles, TileGridViewport tileTileInfo) : base(tileTileInfo)
|
||||
protected LayerItem(Item[] tiles, TileGridViewport tileTileInfo) : base(tileTileInfo)
|
||||
{
|
||||
Tiles = tiles;
|
||||
Debug.Assert(TileInfo.TotalWidth * TileInfo.TotalHeight == tiles.Length);
|
||||
}
|
||||
|
||||
public Item GetTile(in int x, in int y) => this[TileInfo.GetTileIndex(x, y)];
|
||||
public void SetTile(in int x, in int y, Item tile) => this[TileInfo.GetTileIndex(x, y)] = tile;
|
||||
|
||||
public Item this[int index]
|
||||
{
|
||||
|
|
@ -12,7 +12,7 @@ namespace NHSE.Core;
|
|||
/// <param name="ShiftHeight">Vertical acre shift from the map's origin.</param>
|
||||
/// <param name="TilesPerAcre">Number of tiles per acre in one dimension (16 or 32).</param>
|
||||
/// <param name="TileBitShift">Bit shift value to convert between tiles and acres (4 for 16 tiles, 5 for 32 tiles).</param>
|
||||
public readonly record struct MapLayerConfigAcre(
|
||||
public readonly record struct LayerPositionConfig(
|
||||
byte CountWidth, byte CountHeight,
|
||||
byte ShiftWidth, byte ShiftHeight,
|
||||
[ConstantExpected] byte TilesPerAcre, byte TileBitShift)
|
||||
|
|
@ -42,20 +42,20 @@ namespace NHSE.Core;
|
|||
private const byte Shift16 = 4; // div16 is same as sh 4
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="MapLayerConfigAcre"/> instance, centering the layer within the acre.
|
||||
/// Creates a new <see cref="LayerPositionConfig"/> instance, centering the layer within the acre.
|
||||
/// </summary>
|
||||
/// <param name="width">Width of the layer in acres.</param>
|
||||
/// <param name="height">Height of the layer in acres.</param>
|
||||
/// <param name="tilesPerAcre">Number of tiles per acre (16 or 32).</param>
|
||||
/// <returns>A new <see cref="MapLayerConfigAcre"/> instance.</returns>
|
||||
public static MapLayerConfigAcre Create(byte width, byte height, [ConstantExpected(Min = Grid16, Max = Grid32)] byte tilesPerAcre)
|
||||
/// <returns>A new <see cref="LayerPositionConfig"/> instance.</returns>
|
||||
public static LayerPositionConfig Create(byte width, byte height, [ConstantExpected(Min = Grid16, Max = Grid32)] byte tilesPerAcre)
|
||||
{
|
||||
var shiftW = (byte)((MapAcreWidth - width) / 2); // centered
|
||||
var shiftH = (byte)((MapAcreHeight - height) / 2); // centered
|
||||
|
||||
var bitShift = tilesPerAcre == Grid16 ? Shift16 : Shift32;
|
||||
#pragma warning disable CA1857
|
||||
return new MapLayerConfigAcre(width, height, shiftW, shiftH, tilesPerAcre, bitShift);
|
||||
return new LayerPositionConfig(width, height, shiftW, shiftH, tilesPerAcre, bitShift);
|
||||
#pragma warning restore CA1857
|
||||
}
|
||||
|
||||
|
|
@ -3,27 +3,27 @@
|
|||
|
||||
namespace NHSE.Core;
|
||||
|
||||
public sealed record RoomItemLayer : ItemLayer
|
||||
public sealed record LayerRoomItem : LayerItem
|
||||
{
|
||||
public const int SIZE = Width * Height * Item.SIZE;
|
||||
private const byte Width = 20;
|
||||
private const byte Height = 20;
|
||||
|
||||
public RoomItemLayer(ReadOnlySpan<byte> data) : this(Item.GetArray(data)) { }
|
||||
public RoomItemLayer(Item[] tiles) : base(tiles, Width, Height) { }
|
||||
public LayerRoomItem(ReadOnlySpan<byte> data) : this(Item.GetArray(data)) { }
|
||||
public LayerRoomItem(Item[] tiles) : base(tiles, Width, Height) { }
|
||||
|
||||
public static RoomItemLayer[] GetArray(ReadOnlySpan<byte> data)
|
||||
public static LayerRoomItem[] GetArray(ReadOnlySpan<byte> 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<RoomItemLayer> data)
|
||||
public static byte[] SetArray(IReadOnlyList<LayerRoomItem> data)
|
||||
{
|
||||
var result = new byte[data.Count * SIZE];
|
||||
for (int i = 0; i < data.Count; i++)
|
||||
|
|
@ -7,7 +7,7 @@ namespace NHSE.Core;
|
|||
/// <summary>
|
||||
/// Grid of <see cref="TerrainTile"/>
|
||||
/// </summary>
|
||||
public sealed record TerrainLayer : AcreSelectionGrid
|
||||
public sealed record LayerTerrain : AcreSelectionGrid
|
||||
{
|
||||
public TerrainTile[] Tiles { get; init; }
|
||||
public Memory<byte> BaseAcres { get; init; }
|
||||
|
|
@ -19,7 +19,7 @@ public sealed record TerrainLayer : AcreSelectionGrid
|
|||
|
||||
private static TileGridViewport Viewport => new(TilesPerAcreDim, TilesPerAcreDim, CountAcreWidth, CountAcreHeight);
|
||||
|
||||
public TerrainLayer(TerrainTile[] tiles, Memory<byte> acres) : base(Viewport)
|
||||
public LayerTerrain(TerrainTile[] tiles, Memory<byte> acres) : base(Viewport)
|
||||
{
|
||||
BaseAcres = acres;
|
||||
Tiles = tiles;
|
||||
|
|
@ -140,22 +140,6 @@ public void SetAllRoad(TerrainTile tile, bool interiorOnly = true)
|
|||
}
|
||||
}
|
||||
|
||||
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 = TileInfo.ViewWidth;
|
||||
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 >= TileInfo.ViewWidth * acreScale)
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Manages the <see cref="Item"/> data for the player's outside overworld.
|
||||
/// </summary>
|
||||
public sealed class FieldItemManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Base layer of items
|
||||
/// </summary>
|
||||
public readonly FieldItemLayer Layer1;
|
||||
|
||||
/// <summary>
|
||||
/// Layer of items that are supported by <see cref="Layer1"/>
|
||||
/// </summary>
|
||||
public readonly FieldItemLayer Layer2;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the save file that will be updated when <see cref="Save"/> is called.
|
||||
/// </summary>
|
||||
public readonly MainSave SAV;
|
||||
|
||||
public readonly int FieldAcreWidth;
|
||||
public readonly int FieldAcreHeight;
|
||||
public readonly int FieldItemWidth;
|
||||
public readonly int FieldItemHeight;
|
||||
|
||||
public FieldItemManager(MainSave sav)
|
||||
{
|
||||
var (aWidth, aHeight) = (sav.FieldItemAcreWidth, sav.FieldItemAcreHeight);
|
||||
Layer1 = new FieldItemLayer(sav.GetFieldItemLayer1(), aWidth, aHeight);
|
||||
Layer2 = new FieldItemLayer(sav.GetFieldItemLayer2(), aWidth, aHeight);
|
||||
SAV = sav;
|
||||
|
||||
FieldAcreWidth = aWidth;
|
||||
FieldAcreHeight = aHeight;
|
||||
FieldItemWidth = Layer1.TileInfo.TotalWidth;
|
||||
FieldItemHeight = Layer1.TileInfo.TotalHeight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores all values for the <see cref="FieldItemManager"/> back to the <see cref="SAV"/>.
|
||||
/// </summary>
|
||||
public void Save()
|
||||
{
|
||||
SAV.SetFieldItemLayer1(Layer1.Tiles);
|
||||
SAV.SetFieldItemLayer2(Layer2.Tiles);
|
||||
|
||||
SetTileActiveFlags(Layer1, SAV.FieldItemFlag1);
|
||||
SetTileActiveFlags(Layer2, SAV.FieldItemFlag2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists out all coordinates of tiles present in <see cref="Layer2"/> that don't have anything underneath in <see cref="Layer1"/> to support them.
|
||||
/// </summary>
|
||||
public List<string> GetUnsupportedTiles(int totalWidth, int totalHeight)
|
||||
{
|
||||
var result = new List<string>();
|
||||
for (int x = 0; x < totalWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < totalHeight; 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 < FieldItemWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < 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 * FieldItemWidth) + x);
|
||||
private void SetIsActive(int ofs, int x, int y, bool value) => FlagUtil.SetFlag(SAV.Data, ofs, (y * FieldItemWidth) + x, value);
|
||||
|
||||
public bool IsOccupied(int x, int y) => !Layer1.GetTile(x, y).IsNone || !Layer2.GetTile(x, y).IsNone;
|
||||
}
|
||||
47
NHSE.Core/Structures/Map/Managers/MapEditor.cs
Normal file
47
NHSE.Core/Structures/Map/Managers/MapEditor.cs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
namespace NHSE.Core;
|
||||
|
||||
public sealed class MapEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Master interactor for mutating the map.
|
||||
/// </summary>
|
||||
public required MapMutator Mutator { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Amount of pixel upscaling compared to a 1px = 1 tile map.
|
||||
/// </summary>
|
||||
public int MapScale { get; set; } = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Amount of pixel upscaling compared to a 1px = 1 tile map.
|
||||
/// </summary>
|
||||
public int AcreScale { get; set; } = 8;
|
||||
|
||||
/// <summary>
|
||||
/// Converts an upscaled coordinate to a tile coordinate.
|
||||
/// </summary>
|
||||
/// <param name="mX">X coordinate (mouse on upscaled image).</param>
|
||||
/// <param name="mY">Y coordinate (mouse on upscaled image).</param>
|
||||
/// <param name="x">Absolute tile X coordinate.</param>
|
||||
/// <param name="y">Absolute tile Y coordinate.</param>
|
||||
public void GetCursorCoordinates(in int mX, in int mY, out int x, out int y)
|
||||
{
|
||||
x = mX / MapScale;
|
||||
y = mY / MapScale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the MapEditor class initialized from the specified save file.
|
||||
/// </summary>
|
||||
/// <param name="sav">The save file containing the data used to initialize the MapEditor. Cannot be null.</param>
|
||||
/// <returns>A MapEditor instance populated with data from the provided save file.</returns>
|
||||
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;
|
||||
}
|
||||
16
NHSE.Core/Structures/Map/Managers/MapManager.cs
Normal file
16
NHSE.Core/Structures/Map/Managers/MapManager.cs
Normal file
|
|
@ -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; }
|
||||
}
|
||||
61
NHSE.Core/Structures/Map/Managers/MapMutator.cs
Normal file
61
NHSE.Core/Structures/Map/Managers/MapMutator.cs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
public sealed class 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;
|
||||
|
||||
/// <inheritdoc cref="ModifyFieldItems(Func{int,int,int,int,int},in bool,LayerFieldItem)"/>
|
||||
public int ModifyFieldItems(Func<int, int, int, int, int> action, in bool wholeMap)
|
||||
=> ModifyFieldItems(action, wholeMap, CurrentLayer);
|
||||
|
||||
/// <inheritdoc cref="ReplaceFieldItems(Item,Item,bool,LayerFieldItem)"/>
|
||||
public int ReplaceFieldItems(Item oldItem, Item newItem, in bool wholeMap)
|
||||
=> ReplaceFieldItems(oldItem, newItem, wholeMap, CurrentLayer);
|
||||
|
||||
/// <summary>
|
||||
/// Modifies field items in the specified <paramref name="layerField"/> using the provided <paramref name="action"/> function.
|
||||
/// </summary>
|
||||
/// <param name="action">Range selector (xmin, ymin, width, height) to use.</param>
|
||||
/// <param name="wholeMap">If true, the modification is applied across the entire map; otherwise, only within the current view.</param>
|
||||
/// <param name="layerField">The layer field item to perform the modification on.</param>
|
||||
/// <returns>The number of items modified.</returns>
|
||||
public int ModifyFieldItems(Func<int, int, int, int, int> action, in bool wholeMap, LayerFieldItem layerField)
|
||||
{
|
||||
var (xMin, yMin) = wholeMap ? (0, 0) : (View.X, View.Y);
|
||||
var info = layerField.TileInfo;
|
||||
var (width, height) = wholeMap ? info.DimTotal : info.DimAcre;
|
||||
return action(xMin, yMin, width, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all instances of <paramref name="oldItem"/> with <paramref name="newItem"/> in the specified <paramref name="layerField"/>.
|
||||
/// </summary>
|
||||
/// <param name="oldItem">Item to be replaced.</param>
|
||||
/// <param name="newItem">Item to replace with.</param>
|
||||
/// <param name="wholeMap">If true, the replacement is done across the entire map; otherwise, only within the current view.</param>
|
||||
/// <param name="layerField">The layer field item to perform the replacement on.</param>
|
||||
/// <returns>The number of items replaced.</returns>
|
||||
private int ReplaceFieldItems(Item oldItem, Item newItem, bool wholeMap, LayerFieldItem layerField)
|
||||
{
|
||||
var (xMin, yMin) = wholeMap ? (0, 0) : (View.X, View.Y);
|
||||
var info = layerField.TileInfo;
|
||||
var (width, height) = wholeMap ? info.DimTotal : info.DimAcre;
|
||||
return layerField.ReplaceAll(oldItem, newItem, xMin, yMin, width, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="MapMutator"/> from the provided <see cref="MainSave"/> file.
|
||||
/// </summary>
|
||||
/// <param name="sav">The save file containing the data used to initialize the MapMutator. Cannot be null.</param>
|
||||
/// <returns>A MapMutator instance populated with data from the provided save file.</returns>
|
||||
public static MapMutator FromSaveFile(MainSave sav)
|
||||
=> new() { Manager = MapTileManagerUtil.FromSaveFile(sav) };
|
||||
}
|
||||
18
NHSE.Core/Structures/Map/Managers/MapStatePlaza.cs
Normal file
18
NHSE.Core/Structures/Map/Managers/MapStatePlaza.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Value storage for plaza position in the map.
|
||||
/// </summary>
|
||||
public sealed class MapStatePlaza
|
||||
{
|
||||
/// <summary>
|
||||
/// Plaza Position X coordinate.
|
||||
/// </summary>
|
||||
|
||||
public required uint X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Plaza Position Z coordinate.
|
||||
/// </summary>
|
||||
public required uint Z { get; set; }
|
||||
}
|
||||
41
NHSE.Core/Structures/Map/Managers/MapTileManagerUtil.cs
Normal file
41
NHSE.Core/Structures/Map/Managers/MapTileManagerUtil.cs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
namespace NHSE.Core;
|
||||
|
||||
public static class MapTileManagerUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="MapTileManager"/> from the provided <see cref="MainSave"/>.
|
||||
/// </summary>
|
||||
public static MapTileManager FromSaveFile(MainSave sav) => new()
|
||||
{
|
||||
ConfigTerrain = LayerPositionConfig.Create(7, 6, 16),
|
||||
ConfigBuildings = LayerPositionConfig.Create(5, 4, 16),
|
||||
ConfigItems = LayerPositionConfig.Create(sav.FieldItemAcreWidth, sav.FieldItemAcreHeight, 32),
|
||||
|
||||
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 },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Sets the values from the provided <see cref="MapTileManager"/> into the provided <see cref="MainSave"/>.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
132
NHSE.Core/Structures/Map/Managers/MapViewState.cs
Normal file
132
NHSE.Core/Structures/Map/Managers/MapViewState.cs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Amount the X/Y coordinates change when using arrow movement.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Top-left-origin X coordinate of the view.
|
||||
/// </summary>
|
||||
public int X
|
||||
{
|
||||
get;
|
||||
private set
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)value, (uint)MaxX);
|
||||
field = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Top-left-origin Y coordinate of the view.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Moves the view up by <see cref="ArrowViewInterval"/> tiles.
|
||||
/// </summary>
|
||||
/// <returns><see langword="true"/> if the view changed; otherwise, <see langword="false"/>.</returns>
|
||||
public bool ArrowUp()
|
||||
{
|
||||
if (!CanUp)
|
||||
return false;
|
||||
Y = Math.Max(0, Y - (int)ArrowViewInterval);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the view left by <see cref="ArrowViewInterval"/> tiles.
|
||||
/// </summary>
|
||||
/// <returns><see langword="true"/> if the view changed; otherwise, <see langword="false"/>.</returns>
|
||||
public bool ArrowLeft()
|
||||
{
|
||||
if (!CanLeft)
|
||||
return false;
|
||||
X = Math.Max(0, X - (int)ArrowViewInterval);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the view right by <see cref="ArrowViewInterval"/> tiles.
|
||||
/// </summary>
|
||||
/// <returns><see langword="true"/> if the view changed; otherwise, <see langword="false"/>.</returns>
|
||||
public bool ArrowRight()
|
||||
{
|
||||
if (!CanRight)
|
||||
return false;
|
||||
X = Math.Min(MaxX - EdgeBuffer, X + (int)ArrowViewInterval);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the view down by <see cref="ArrowViewInterval"/> tiles.
|
||||
/// </summary>
|
||||
/// <returns><see langword="true"/> if the view changed; otherwise, <see langword="false"/>.</returns>
|
||||
public bool ArrowDown()
|
||||
{
|
||||
if (!CanDown)
|
||||
return false;
|
||||
Y = Math.Min(MaxY - EdgeBuffer, Y + (int)ArrowViewInterval);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the requested coordinates (sanity checked).
|
||||
/// </summary>
|
||||
/// <returns><see langword="true"/> if the view changed; otherwise, <see langword="false"/>.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the view to the top-left of the specified acre.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Acres are ordered Y-down-first.
|
||||
/// </remarks>
|
||||
/// <param name="acre">Acre index to set the view to.</param>
|
||||
public void SetViewToAcre(int acre)
|
||||
{
|
||||
var acreX = acre % (MaxX / EdgeBuffer);
|
||||
var acreY = acre / (MaxX / EdgeBuffer);
|
||||
SetViewTo(acreX * EdgeBuffer, acreY * EdgeBuffer);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
namespace NHSE.Core;
|
||||
|
||||
public enum RoomLayerSurface
|
||||
public enum RoomLayerSurface : byte
|
||||
{
|
||||
Floor = 0,
|
||||
FloorSupported = 1,
|
||||
|
|
|
|||
|
|
@ -4,14 +4,18 @@
|
|||
|
||||
namespace NHSE.Core;
|
||||
|
||||
public sealed class RoomItemManager
|
||||
public sealed class RoomManager
|
||||
{
|
||||
public readonly RoomItemLayer[] Layers;
|
||||
public readonly LayerRoomItem[] Layers;
|
||||
|
||||
public readonly IPlayerRoom Room;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="RoomLayerSurface"/>
|
||||
/// </summary>
|
||||
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<string> GetUnsupportedTiles()
|
||||
|
|
@ -35,9 +39,11 @@ public List<string> GetUnsupportedTiles()
|
|||
var lBase = Layers[(int)RoomLayerSurface.Floor];
|
||||
var lSupport = Layers[(int)RoomLayerSurface.FloorSupported];
|
||||
var result = new List<string>();
|
||||
for (int x = 0; x < lBase.TileInfo.TotalWidth; x++)
|
||||
|
||||
var (width, height) = lBase.TileInfo.DimTotal;
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int y = 0; y < lBase.TileInfo.TotalHeight; y++)
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
var tile = lSupport.GetTile(x, y);
|
||||
if (tile.IsNone)
|
||||
|
|
@ -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<byte, Color> 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)
|
||||
};
|
||||
}
|
||||
|
|
@ -3,20 +3,20 @@
|
|||
/// <summary>
|
||||
/// Flagging various issues when trying to place an item.
|
||||
/// </summary>
|
||||
public enum PlacedItemPermission
|
||||
public enum PlacedItemPermission : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Item does not have any of its tiles overlapping with any other items.
|
||||
/// </summary>
|
||||
NoCollision,
|
||||
NoCollision = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Item tiles are overlapping with another item.
|
||||
/// </summary>
|
||||
Collision,
|
||||
Collision = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Item tiles would overflow out-of-bounds.
|
||||
/// </summary>
|
||||
OutOfBounds,
|
||||
OutOfBounds = 2,
|
||||
}
|
||||
38
NHSE.Core/Structures/Map/TileCollisionUtil.cs
Normal file
38
NHSE.Core/Structures/Map/TileCollisionUtil.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
public static class TileCollisionUtil
|
||||
{
|
||||
public static readonly Dictionary<byte, Color> 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)
|
||||
};
|
||||
}
|
||||
|
|
@ -32,6 +32,9 @@ namespace NHSE.Core;
|
|||
/// </summary>
|
||||
public int TotalCount => TotalWidth * TotalHeight;
|
||||
|
||||
public (int X, int Y) DimAcre => (ViewWidth, ViewHeight);
|
||||
public (int X, int Y) DimTotal => (TotalWidth, TotalHeight);
|
||||
|
||||
public int GetTileIndex(int acreX, int acreY, int gridX, int gridY)
|
||||
{
|
||||
var x = (acreX * ViewWidth) + gridX;
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
public sealed class MapManager(MainSave sav) : MapTerrainStructure(sav)
|
||||
{
|
||||
public readonly FieldItemManager Items = new(sav);
|
||||
|
||||
public int MapLayer { get; set; } // 0 or 1
|
||||
|
||||
public FieldItemLayer CurrentLayer => MapLayer == 0 ? Items.Layer1 : Items.Layer2;
|
||||
}
|
||||
|
||||
public abstract class MapTerrainStructure(MainSave sav)
|
||||
{
|
||||
public readonly TerrainLayer Terrain = new(sav.GetTerrainTiles(), sav.GetAcreBytes());
|
||||
public readonly IReadOnlyList<Building> Buildings = sav.Buildings;
|
||||
|
||||
public uint PlazaX { get; set; } = sav.EventPlazaLeftUpX;
|
||||
public uint PlazaY { get; set; } = sav.EventPlazaLeftUpZ;
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
public abstract class MapView(MapManager m, int scale = 16)
|
||||
{
|
||||
private const int ViewInterval = 2;
|
||||
public readonly MapManager Map = m;
|
||||
|
||||
public int MapScale { get; } = 1;
|
||||
public int AcreScale { get; } = scale;
|
||||
public int TerrainScale => AcreScale * 2;
|
||||
|
||||
// Top Left Anchor Coordinates
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
|
||||
public bool CanUp => Y != 0;
|
||||
public bool CanDown => Y < Map.CurrentLayer.TileInfo.TotalHeight - Map.CurrentLayer.TileInfo.ViewHeight;
|
||||
public bool CanLeft => X != 0;
|
||||
public bool CanRight => X < Map.CurrentLayer.TileInfo.TotalWidth - Map.CurrentLayer.TileInfo.ViewWidth;
|
||||
|
||||
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.TileInfo.TotalWidth - 2)
|
||||
return false;
|
||||
X += ViewInterval;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ArrowDown()
|
||||
{
|
||||
if (Y >= Map.CurrentLayer.TileInfo.TotalHeight - 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.TileInfo.TotalWidth - info.TileInfo.ViewWidth));
|
||||
var newY = Math.Max(0, Math.Min(y, info.TileInfo.TotalHeight - info.TileInfo.ViewHeight));
|
||||
bool diff = X != newX || Y != newY;
|
||||
X = newX;
|
||||
Y = newY;
|
||||
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<int, int, int, int, int> action, in bool wholeMap)
|
||||
{
|
||||
var layer = Map.CurrentLayer;
|
||||
return wholeMap
|
||||
? action(0, 0, layer.TileInfo.TotalWidth, layer.TileInfo.TotalHeight)
|
||||
: action(X, Y, layer.TileInfo.ViewWidth, layer.TileInfo.ViewHeight);
|
||||
}
|
||||
|
||||
public int ReplaceFieldItems(Item oldItem, Item newItem, in bool wholeMap)
|
||||
{
|
||||
var layer = Map.CurrentLayer;
|
||||
return wholeMap
|
||||
? layer.ReplaceAll(oldItem, newItem, 0, 0, layer.TileInfo.TotalWidth, layer.TileInfo.TotalHeight)
|
||||
: layer.ReplaceAll(oldItem, newItem, X, Y, layer.TileInfo.ViewWidth, layer.TileInfo.ViewHeight);
|
||||
}
|
||||
|
||||
public void GetCursorCoordinates(in int mX, in int mY, out int x, out int y)
|
||||
{
|
||||
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.TileInfo.GetViewAnchorCoordinates(ref x, ref y, centerReticle);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,6 @@ public interface IPlayerRoom
|
|||
byte[] Write();
|
||||
|
||||
string Extension { get; }
|
||||
RoomItemLayer[] GetItemLayers();
|
||||
void SetItemLayers(IReadOnlyList<RoomItemLayer> value);
|
||||
LayerRoomItem[] GetItemLayers();
|
||||
void SetItemLayers(IReadOnlyList<LayerRoomItem> value);
|
||||
}
|
||||
|
|
@ -20,8 +20,8 @@ public class PlayerRoom1(Memory<byte> raw) : IPlayerRoom
|
|||
*/
|
||||
|
||||
public const int LayerCount = 8;
|
||||
public RoomItemLayer[] GetItemLayers() => RoomItemLayer.GetArray(Data[..(LayerCount * RoomItemLayer.SIZE)].ToArray());
|
||||
public void SetItemLayers(IReadOnlyList<RoomItemLayer> value) => RoomItemLayer.SetArray(value).CopyTo(Data);
|
||||
public LayerRoomItem[] GetItemLayers() => LayerRoomItem.GetArray(Data[..(LayerCount * LayerRoomItem.SIZE)].ToArray());
|
||||
public void SetItemLayers(IReadOnlyList<LayerRoomItem> 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);
|
||||
|
|
|
|||
|
|
@ -21,4 +21,19 @@ public static void SetFlag(Span<byte> arr, int offset, int bitIndex, bool value)
|
|||
arr[offset] &= (byte)~(1 << bitIndex);
|
||||
arr[offset] |= (byte)((value ? 1 : 0) << bitIndex);
|
||||
}
|
||||
|
||||
public static bool GetFlag(ReadOnlySpan<byte> arr, int bitIndex)
|
||||
{
|
||||
var b = arr[bitIndex >> 3];
|
||||
var mask = 1 << (bitIndex & 7);
|
||||
return (b & mask) != 0;
|
||||
}
|
||||
|
||||
public static void SetFlag(Span<byte> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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..]);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ namespace NHSE.Sprites;
|
|||
|
||||
public static class ItemLayerSprite
|
||||
{
|
||||
public static Bitmap GetBitmapItemLayer(ItemLayer layer)
|
||||
public static Bitmap GetBitmapItemLayer(LayerItem layer)
|
||||
{
|
||||
var items = layer.Tiles;
|
||||
var height = layer.TileInfo.TotalHeight;
|
||||
|
|
@ -32,7 +32,7 @@ private static void LoadBitmapLayer(ReadOnlySpan<Item> items, Span<int> bmpData,
|
|||
}
|
||||
}
|
||||
|
||||
private static void LoadPixelsFromLayer(ItemLayer layer, int x0, int y0, int width, Span<int> bmpData)
|
||||
private static void LoadPixelsFromLayer(LayerItem layer, int x0, int y0, int width, Span<int> bmpData)
|
||||
{
|
||||
var stride = layer.TileInfo.ViewWidth;
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ private static void LoadPixelsFromLayer(ItemLayer layer, int x0, int y0, int wid
|
|||
}
|
||||
|
||||
// non-allocation image generator
|
||||
public static Bitmap GetBitmapItemLayerViewGrid(ItemLayer layer, int x0, int y0, int scale, Span<int> acre1, int[] acreScale, Bitmap dest, int transparency = -1, int gridlineColor = 0)
|
||||
public static Bitmap GetBitmapItemLayerViewGrid(LayerItem layer, int x0, int y0, int scale, Span<int> acre1, int[] acreScale, Bitmap dest, int transparency = -1, int gridlineColor = 0)
|
||||
{
|
||||
int w = layer.TileInfo.ViewWidth;
|
||||
int h = layer.TileInfo.ViewHeight;
|
||||
|
|
@ -73,7 +73,7 @@ public static Bitmap GetBitmapItemLayerViewGrid(ItemLayer layer, int x0, int y0,
|
|||
return dest;
|
||||
}
|
||||
|
||||
private static void DrawDirectionals(Span<int> data, ItemLayer layer, int w, int x0, int y0, int scale)
|
||||
private static void DrawDirectionals(Span<int> data, LayerItem layer, int w, int x0, int y0, int scale)
|
||||
{
|
||||
for (int x = x0; x < x0 + layer.TileInfo.ViewWidth; x++)
|
||||
{
|
||||
|
|
@ -232,7 +232,7 @@ public static void DrawGrid(Span<int> data, int w, int h, int scale, int gridlin
|
|||
}
|
||||
}
|
||||
|
||||
public static Bitmap GetBitmapItemLayer(ItemLayer layer, int x, int y, int[] data, Bitmap dest, int transparency = -1)
|
||||
public static Bitmap GetBitmapItemLayer(LayerItem layer, int x, int y, int[] data, Bitmap dest, int transparency = -1)
|
||||
{
|
||||
LoadBitmapLayer(layer.Tiles, data, layer.TileInfo.TotalWidth, layer.TileInfo.TotalHeight);
|
||||
if (transparency >>> 24 != 0xFF)
|
||||
|
|
|
|||
|
|
@ -7,8 +7,12 @@ namespace NHSE.Sprites;
|
|||
/// <summary>
|
||||
/// Produces bitmaps for viewing map acres and full maps.
|
||||
/// </summary>
|
||||
public sealed class MapViewer : MapView, IDisposable
|
||||
public sealed class MapRenderer : IDisposable
|
||||
{
|
||||
private readonly MapEditor Map;
|
||||
private int AcreScale => Map.MapScale;
|
||||
private int MapScale => Map.MapScale * 2;
|
||||
|
||||
private const byte ScaleAsMap = 2;
|
||||
private const byte FieldItemWidthOld = 7;
|
||||
private const byte FieldItemWidthNew = 9;
|
||||
|
|
@ -23,13 +27,16 @@ public sealed class MapViewer : MapView, IDisposable
|
|||
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)
|
||||
public MapRenderer(MapEditor m)
|
||||
{
|
||||
var l1 = m.Items.Layer1;
|
||||
Map = m;
|
||||
|
||||
var l1 = m.Mutator.Manager.FieldItems.Layer0;
|
||||
var info = l1.TileInfo;
|
||||
PixelsItemAcre1 = new int[info.ViewWidth * info.ViewHeight];
|
||||
PixelsItemAcreX = new int[PixelsItemAcre1.Length * AcreScale * AcreScale];
|
||||
|
|
@ -55,8 +62,8 @@ public void 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 GetLayerAcre(int t) => GetLayerAcre(Map.Mutator.View.X, Map.Mutator.View.Y, t);
|
||||
public Bitmap GetMapWithReticle(int t) => GetMapWithReticle(Map.Mutator.View.X, Map.Mutator.View.Y, t, Map.Mutator.CurrentLayer);
|
||||
|
||||
public Bitmap GetBackgroundTerrain(int index = -1)
|
||||
{
|
||||
|
|
@ -82,17 +89,17 @@ public Bitmap GetInflatedImage(Bitmap regular)
|
|||
|
||||
private Bitmap GetLayerAcre(int topX, int topY, int transparency)
|
||||
{
|
||||
var layer = Map.CurrentLayer;
|
||||
var layer = Map.Mutator.CurrentLayer;
|
||||
return ItemLayerSprite.GetBitmapItemLayerViewGrid(layer, topX, topY, AcreScale, PixelsItemAcre1, PixelsItemAcreX, ScaleAcre, transparency);
|
||||
}
|
||||
|
||||
public Bitmap GetBackgroundAcre(Font f, byte transparencyBuilding, byte transparencyTerrain, int index = -1)
|
||||
{
|
||||
return TerrainSprite.GetAcre(this, f, PixelsBackgroundAcre1, PixelsBackgroundAcreX, BackgroundAcre, index, transparencyBuilding, transparencyTerrain);
|
||||
return TerrainSprite.CreateAcreView(this, f, PixelsBackgroundAcre1, PixelsBackgroundAcreX, BackgroundAcre, index, transparencyBuilding, transparencyTerrain);
|
||||
}
|
||||
|
||||
private Bitmap GetMapWithReticle(int topX, int topY, int t, FieldItemLayer layer)
|
||||
private Bitmap GetMapWithReticle(int topX, int topY, int t, LayerFieldItem layerField)
|
||||
{
|
||||
return ItemLayerSprite.GetBitmapItemLayer(layer, topX, topY, PixelsItemMap, MapReticle, t);
|
||||
return ItemLayerSprite.GetBitmapItemLayer(layerField, topX, topY, PixelsItemMap, MapReticle, t);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,23 +17,24 @@ public static class TerrainSprite
|
|||
private const int PlazaWidth = 6 * 2;
|
||||
private const int PlazaHeight = 5 * 2;
|
||||
|
||||
public static void CreateMap(TerrainLayer mgr, Span<int> pixels)
|
||||
public static void CreateMap(LayerTerrain mgr, Span<int> pixels)
|
||||
{
|
||||
// Populate the image, with each pixel being a single tile.
|
||||
int width = mgr.TileInfo.TotalWidth;
|
||||
int height = mgr.TileInfo.TotalHeight;
|
||||
int i = 0;
|
||||
for (int y = 0; y < mgr.TileInfo.TotalHeight; y++)
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < mgr.TileInfo.TotalWidth; x++, i++)
|
||||
{
|
||||
for (int x = 0; x < width; x++, i++)
|
||||
pixels[i] = mgr.GetTileColor(x, y, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Bitmap CreateMap(TerrainLayer mgr, Span<int> scale1, int[] scaleX, Bitmap map, int scale, int acreIndex = -1)
|
||||
public static Bitmap CreateMap(LayerTerrain mgr, Span<int> scale1, Span<int> scaleX, Bitmap map, int scale, int acreIndex = -1)
|
||||
{
|
||||
CreateMap(mgr, scale1);
|
||||
ImageUtil.ScalePixelImage(scale1, scaleX, map.Width, map.Height, scale);
|
||||
ImageUtil.SetBitmapData(map, scaleX);
|
||||
map.SetBitmapData(scaleX);
|
||||
|
||||
if (acreIndex < 0)
|
||||
return map;
|
||||
|
|
@ -56,7 +57,7 @@ private static Bitmap DrawReticle(Bitmap map, TileGridViewport mgr, int x, int y
|
|||
return map;
|
||||
}
|
||||
|
||||
public static Bitmap GetMapWithBuildings(MapTerrainStructure m, Font? f, Span<int> scale1, int[] scaleX, Bitmap map, int scale = 4, int index = -1)
|
||||
public static Bitmap GetMapWithBuildings(MapTerrainStructure m, Font? f, Span<int> scale1, Span<int> scaleX, Bitmap map, int scale = 4, int index = -1)
|
||||
{
|
||||
CreateMap(m.Terrain, scale1, scaleX, map, scale);
|
||||
using var gfx = Graphics.FromImage(map);
|
||||
|
|
@ -66,7 +67,7 @@ public static Bitmap GetMapWithBuildings(MapTerrainStructure m, Font? f, Span<in
|
|||
return map;
|
||||
}
|
||||
|
||||
private static void DrawPlaza(this Graphics gfx, TerrainLayer g, ushort px, ushort py, int scale)
|
||||
private static void DrawPlaza(this Graphics gfx, LayerTerrain g, ushort px, ushort py, int scale)
|
||||
{
|
||||
g.GetBuildingCoordinate(px, py, scale, out var x, out var y);
|
||||
|
||||
|
|
@ -76,7 +77,7 @@ private static void DrawPlaza(this Graphics gfx, TerrainLayer g, ushort px, usho
|
|||
gfx.FillRectangle(Plaza, x, y, width, height);
|
||||
}
|
||||
|
||||
private static void DrawBuildings(this Graphics gfx, TerrainLayer g, IReadOnlyList<Building> buildings, Font? f, int scale, int index = -1)
|
||||
private static void DrawBuildings(this Graphics gfx, LayerTerrain g, IReadOnlyList<Building> buildings, Font? f, int scale, int index = -1)
|
||||
{
|
||||
for (int i = 0; i < buildings.Count; i++)
|
||||
{
|
||||
|
|
@ -101,13 +102,13 @@ private static void DrawBuilding(Graphics gfx, Font? f, int scale, Brush pen, in
|
|||
gfx.DrawString(name, f, text, new PointF(x, y - (scale * 2)), BuildingTextFormat);
|
||||
}
|
||||
|
||||
private static void SetAcreTerrainPixels(int x, int y, TerrainLayer t, Span<int> data, Span<int> scaleX, int scale)
|
||||
private static void SetAcreTerrainPixels(int x, int y, LayerTerrain t, Span<int> data, Span<int> scaleX, int scale)
|
||||
{
|
||||
GetAcre1(x, y, t, data);
|
||||
ImageUtil.ScalePixelImage(data, scaleX, 16 * scale, 16 * scale, scale / 16);
|
||||
}
|
||||
|
||||
private static void GetAcre1(int tileTopX, int tileTopY, TerrainLayer t, Span<int> data)
|
||||
private static void GetAcre1(int tileTopX, int tileTopY, LayerTerrain t, Span<int> data)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
|
|
@ -129,26 +130,28 @@ private static void GetAcre1(int tileTopX, int tileTopY, TerrainLayer t, Span<in
|
|||
}
|
||||
}
|
||||
|
||||
public static Bitmap GetAcre(MapView m, Font f, Span<int> scale1, int[] scaleX, Bitmap acre, int index, byte tbuild, byte tterrain)
|
||||
public static Bitmap CreateAcreView(MapEditor m, Font f, Span<int> scale1, Span<int> scaleX, Bitmap acre, int index, byte tbuild, byte tterrain)
|
||||
{
|
||||
// Convert from absolute to relative.
|
||||
int mx = m.X / 2;
|
||||
int my = m.Y / 2;
|
||||
SetAcreTerrainPixels(mx, my, m.Map.Terrain, scale1, scaleX, m.TerrainScale);
|
||||
SetAcreTerrainPixels(mx, my, m.Terrain, scale1, scaleX, m.Terrain.Scale);
|
||||
|
||||
const int grid1 = unchecked((int)0xFF888888u);
|
||||
const int grid2 = unchecked((int)0xFF666666u);
|
||||
ImageUtil.SetBitmapData(acre, scaleX);
|
||||
acre.SetBitmapData(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 plaza = m.Mutator.Manager.Plaza;
|
||||
gfx.DrawAcrePlaza(m.Terrain, mx, my, plaza.X, plaza.Z, m.Terrain.Scale, tbuild);
|
||||
|
||||
var buildings = m.Map.Buildings;
|
||||
var t = m.Map.Terrain;
|
||||
var buildings = m.Buildings.Buildings;
|
||||
var t = m.Terrain;
|
||||
for (var i = 0; i < buildings.Count; i++)
|
||||
{
|
||||
var b = buildings[i];
|
||||
t.GetBuildingRelativeCoordinates(mx, my, m.TerrainScale, b.X, b.Y, out var x, out var y);
|
||||
t.GetBuildingRelativeCoordinates(mx, my, m.Terrain.Scale, b.X, b.Y, out var x, out var y);
|
||||
|
||||
var pen = index == i ? Selected : Others;
|
||||
if (tbuild != byte.MaxValue)
|
||||
|
|
@ -157,30 +160,31 @@ public static Bitmap GetAcre(MapView m, Font f, Span<int> scale1, int[] scaleX,
|
|||
pen = new SolidBrush(Color.FromArgb(tbuild, orig));
|
||||
}
|
||||
|
||||
DrawBuilding(gfx, null, m.TerrainScale, pen, x, y, b, Text);
|
||||
DrawBuilding(gfx, null, m.Terrain.Scale, pen, x, y, b, Text);
|
||||
}
|
||||
|
||||
ImageUtil.GetBitmapData(acre, scaleX);
|
||||
acre.GetBitmapData(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);
|
||||
ItemLayerSprite.DrawGrid(scaleX, acre.Width, acre.Height, m.Terrain.Scale, grid2);
|
||||
acre.SetBitmapData(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))
|
||||
t.GetBuildingRelativeCoordinates(mx, my, m.Terrain.Scale, b.X, b.Y, out var x, out var y);
|
||||
if (!t.IsWithinGrid(m.Terrain.Scale, x, y))
|
||||
continue;
|
||||
var name = b.BuildingType.ToString();
|
||||
gfx.DrawString(name, f, Text, new PointF(x, y - (m.TerrainScale * 2)), BuildingTextFormat);
|
||||
var labelPosition = new PointF(x, y - (m.Terrain.Scale * 2));
|
||||
gfx.DrawString(name, f, Text, labelPosition, BuildingTextFormat);
|
||||
}
|
||||
|
||||
if (tterrain != 0)
|
||||
DrawTerrainTileNames(mx, my, gfx, t, f, m.TerrainScale, tterrain);
|
||||
DrawTerrainTileNames(mx, my, gfx, t, f, m.Terrain.Scale, tterrain);
|
||||
|
||||
return acre;
|
||||
}
|
||||
|
||||
private static void DrawTerrainTileNames(int topX, int topY, Graphics gfx, TerrainLayer t, Font f, int scale, byte transparency)
|
||||
private static void DrawTerrainTileNames(int topX, int topY, Graphics gfx, LayerTerrain t, Font f, int scale, byte transparency)
|
||||
{
|
||||
var pen = transparency != byte.MaxValue ? new SolidBrush(Color.FromArgb(transparency, Color.Black)) : Tile;
|
||||
|
||||
|
|
@ -200,9 +204,9 @@ private static void DrawTerrainTileNames(int topX, int topY, Graphics gfx, Terra
|
|||
}
|
||||
}
|
||||
|
||||
private static void DrawAcrePlaza(this Graphics gfx, TerrainLayer g, int topX, int topY, ushort px, ushort py, int scale, byte transparency)
|
||||
private static void DrawAcrePlaza(this Graphics gfx, LayerTerrain g, int topX, int topY, uint plazaX, uint plazaY, int scale, byte transparency)
|
||||
{
|
||||
g.GetBuildingRelativeCoordinates(topX, topY, scale, px, py, out var x, out var y);
|
||||
g.GetBuildingRelativeCoordinates(topX, topY, scale, plazaX, plazaY, out var x, out var y);
|
||||
|
||||
var width = scale * PlazaWidth;
|
||||
var height = scale * PlazaHeight;
|
||||
|
|
|
|||
|
|
@ -24,39 +24,69 @@ public static class ImageUtil
|
|||
public Bitmap GetPalette() => GetBitmap(bg.GetPaletteBitmap(), DesignPatternPRO.PaletteColorCount, 1, PixelFormat.Format24bppRgb);
|
||||
}
|
||||
|
||||
extension(Bitmap bmp)
|
||||
{
|
||||
public Span<byte> 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<byte> 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<int> data, PixelFormat format = PixelFormat.Format32bppArgb, byte bpp = 4)
|
||||
{
|
||||
var span = bmp.GetBitmapData(out var bmpData, format, bpp);
|
||||
var src = MemoryMarshal.Cast<byte, int>(span);
|
||||
src.CopyTo(data);
|
||||
bmp.UnlockBits(bmpData);
|
||||
}
|
||||
|
||||
public void SetBitmapData(ReadOnlySpan<byte> 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<int> data, PixelFormat format = PixelFormat.Format32bppArgb, byte bpp = 4)
|
||||
{
|
||||
var span = bmp.GetBitmapData(out var bmpData, format, bpp);
|
||||
var dest = MemoryMarshal.Cast<byte, int>(span);
|
||||
data.CopyTo(dest);
|
||||
bmp.UnlockBits(bmpData);
|
||||
}
|
||||
}
|
||||
|
||||
public static Bitmap GetBitmap(ReadOnlySpan<int> data, int width, int height, PixelFormat format = PixelFormat.Format32bppArgb)
|
||||
{
|
||||
var span = MemoryMarshal.Cast<int, byte>(data);
|
||||
return GetBitmap(span, width, height, format);
|
||||
}
|
||||
|
||||
public static Bitmap GetBitmap(ReadOnlySpan<byte> 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<byte>(), 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<byte> 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<byte> GetSpan(IntPtr ptr, int length)
|
||||
=> MemoryMarshal.CreateSpan(ref Unsafe.AddByteOffset(ref Unsafe.NullRef<byte>(), ptr), length);
|
||||
|
||||
// https://stackoverflow.com/a/24199315
|
||||
public static Bitmap ResizeImage(Image image, int width, int height)
|
||||
|
|
@ -127,4 +157,10 @@ public static void ClampAllTransparencyTo(Span<int> data, int trans)
|
|||
for (int i = 0; i < data.Length; i++)
|
||||
data[i] &= trans;
|
||||
}
|
||||
|
||||
public static void ClampAllTransparencyTo(Span<byte> data, byte trans)
|
||||
{
|
||||
for (int i = 0; i < data.Length; i += 4)
|
||||
data[i + 3] &= trans;
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<Item> items, int x, int y, SpawnArrangement arrange, int sizeX, int sizeY, bool noOverwrite)
|
||||
private static int SpawnItems(LayerItem layer, IReadOnlyList<Item> 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;
|
||||
|
|
|
|||
|
|
@ -14,9 +14,11 @@ namespace NHSE.WinForms;
|
|||
public sealed partial class FieldItemEditor : Form, IItemLayerEditor
|
||||
{
|
||||
private readonly MainSave SAV;
|
||||
private readonly MapEditor Editor;
|
||||
|
||||
private MapViewState View => Editor.Mutator.View;
|
||||
private MapTileManager Map => Editor.Mutator.Manager;
|
||||
|
||||
private readonly MapManager Map;
|
||||
private readonly MapViewer View;
|
||||
private readonly bool IsExtendedMap30;
|
||||
|
||||
private bool Loading;
|
||||
|
|
@ -29,7 +31,13 @@ public sealed partial class FieldItemEditor : Form, IItemLayerEditor
|
|||
private bool Dragging;
|
||||
|
||||
public ItemEditor ItemProvider => ItemEdit;
|
||||
public ItemLayer SpawnLayer => Map.CurrentLayer;
|
||||
|
||||
/// <summary>
|
||||
/// Layer to spawn items into.
|
||||
/// </summary>
|
||||
public LayerItem Spawn => CurrentLayer;
|
||||
|
||||
public LayerFieldItem CurrentLayer => Editor.Mutator.CurrentLayer;
|
||||
|
||||
private TerrainBrushEditor? tbeForm;
|
||||
|
||||
|
|
@ -38,11 +46,14 @@ public FieldItemEditor(MainSave sav)
|
|||
InitializeComponent();
|
||||
this.TranslateInterface(GameInfo.CurrentLanguage);
|
||||
|
||||
var scale = (PB_Acre.Width - 2) / FieldItemLayer.TilesPerAcreDim; // 1px border
|
||||
// Read the expected scale from the control.
|
||||
var scale = (PB_Acre.Width - 2) / LayerFieldItem.TilesPerAcreDim; // 1px border
|
||||
SAV = sav;
|
||||
IsExtendedMap30 = sav.FieldItemAcreWidth != 7;
|
||||
Map = new MapManager(sav);
|
||||
View = new MapViewer(Map, scale);
|
||||
Editor = MapEditor.FromSaveFile(sav);
|
||||
Editor.MapScale = scale;
|
||||
Editor.AcreScale = scale;
|
||||
Renderer = new MapRenderer(Editor);
|
||||
|
||||
Loading = true;
|
||||
|
||||
|
|
@ -80,7 +91,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());
|
||||
}
|
||||
|
||||
|
|
@ -193,7 +204,7 @@ private void ResetDrag()
|
|||
|
||||
private void OmniTile(MouseEventArgs e)
|
||||
{
|
||||
var tile = GetTile(Map.CurrentLayer, e, out var x, out var y);
|
||||
var tile = GetTile(CurrentLayer, e, out var x, out var y);
|
||||
OmniTile(tile, x, y);
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +213,7 @@ 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);
|
||||
TerrainTile tile = Editor.Terrain.GetTile(x / 2, y / 2);
|
||||
if (tbeForm?.IsBrushSelected != true)
|
||||
{
|
||||
OmniTileTerrain(tile);
|
||||
|
|
@ -223,7 +234,7 @@ 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((x / 2) + i, (y / 2) + j));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -275,10 +286,10 @@ private void OmniTileTerrain(TerrainTile tile)
|
|||
}
|
||||
}
|
||||
|
||||
private Item GetTile(FieldItemLayer layer, MouseEventArgs e, out int x, out int y)
|
||||
private Item GetTile(LayerFieldItem layerField, MouseEventArgs e, out int x, out int y)
|
||||
{
|
||||
SetHoveredItem(e);
|
||||
return layer.GetTile(x = View.X + HoverX, y = View.Y + HoverY);
|
||||
return layerField.GetTile(x = View.X + HoverX, y = View.Y + HoverY);
|
||||
}
|
||||
|
||||
private void SetHoveredItem(MouseEventArgs e)
|
||||
|
|
@ -292,15 +303,15 @@ private void SetHoveredItem(MouseEventArgs e)
|
|||
|
||||
private void GetAcreCoordinates(MouseEventArgs e, out int x, out int y)
|
||||
{
|
||||
x = e.X / View.AcreScale;
|
||||
y = e.Y / View.AcreScale;
|
||||
x = e.X / Editor.AcreScale;
|
||||
y = e.Y / Editor.AcreScale;
|
||||
}
|
||||
|
||||
private void PB_Acre_MouseDown(object sender, MouseEventArgs e) => ResetDrag();
|
||||
|
||||
private void PB_Acre_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
var l = Map.CurrentLayer;
|
||||
var l = CurrentLayer;
|
||||
if (e.Button == MouseButtons.Left && CHK_MoveOnDrag.Checked)
|
||||
{
|
||||
MoveDrag(e);
|
||||
|
|
@ -317,8 +328,9 @@ private void PB_Acre_MouseMove(object sender, MouseEventArgs e)
|
|||
return;
|
||||
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);
|
||||
SetCoordinateText(x, y);
|
||||
|
|
@ -367,7 +379,7 @@ private void ViewTile(Item tile, int x, int y)
|
|||
{
|
||||
if (CHK_RedirectExtensionLoad.Checked && tile.IsExtension)
|
||||
{
|
||||
var l = Map.CurrentLayer;
|
||||
var l = CurrentLayer;
|
||||
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);
|
||||
|
|
@ -394,7 +406,7 @@ private void ViewTile(TerrainTile tile)
|
|||
|
||||
private void SetTile(Item tile, int x, int y)
|
||||
{
|
||||
var l = Map.CurrentLayer;
|
||||
var l = CurrentLayer;
|
||||
var pgt = new Item();
|
||||
ItemEdit.SetItem(pgt);
|
||||
|
||||
|
|
@ -429,7 +441,7 @@ private void SetTile(Item tile, int x, int y)
|
|||
|
||||
private void ReplaceTile(Item tile, int x, int y)
|
||||
{
|
||||
var l = Map.CurrentLayer;
|
||||
var l = CurrentLayer;
|
||||
var pgt = new Item();
|
||||
ItemEdit.SetItem(pgt);
|
||||
|
||||
|
|
@ -451,7 +463,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);
|
||||
|
|
@ -507,13 +519,14 @@ private void DeleteTile(Item tile, int x, int y)
|
|||
{
|
||||
if (CHK_AutoExtension.Checked)
|
||||
{
|
||||
var layer = CurrentLayer;
|
||||
if (!tile.IsRoot)
|
||||
{
|
||||
x -= tile.ExtensionX;
|
||||
y -= tile.ExtensionY;
|
||||
tile = Map.CurrentLayer.GetTile(x, y);
|
||||
tile = layer.GetTile(x, y);
|
||||
}
|
||||
Map.CurrentLayer.DeleteExtensionTiles(tile, x, y);
|
||||
layer.DeleteExtensionTiles(tile, x, y);
|
||||
}
|
||||
|
||||
tile.Delete();
|
||||
|
|
@ -530,8 +543,8 @@ private void DeleteTile(TerrainTile tile)
|
|||
|
||||
private void B_Save_Click(object sender, EventArgs e)
|
||||
{
|
||||
var view = View.Map.Items.Layer1.TileInfo;
|
||||
var unsupported = Map.Items.GetUnsupportedTiles(view.TotalWidth, view.TotalHeight);
|
||||
var set = Map.FieldItems;
|
||||
var unsupported = set.GetUnsupportedTiles();
|
||||
if (unsupported.Count != 0)
|
||||
{
|
||||
var err = MessageStrings.MsgFieldItemUnsupportedLayer2Tile;
|
||||
|
|
@ -541,16 +554,9 @@ private void B_Save_Click(object sender, EventArgs e)
|
|||
return;
|
||||
}
|
||||
|
||||
Map.Items.Save();
|
||||
SAV.SetTerrainTiles(Map.Terrain.Tiles);
|
||||
|
||||
SAV.SetAcreBytes(Map.Terrain.BaseAcres.Span);
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
@ -561,12 +567,12 @@ private void Menu_View_Click(object sender, EventArgs e)
|
|||
|
||||
if (RB_Item.Checked)
|
||||
{
|
||||
var tile = Map.CurrentLayer.GetTile(x, y);
|
||||
var tile = CurrentLayer.GetTile(x, y);
|
||||
ViewTile(tile, x, y);
|
||||
}
|
||||
else if (RB_Terrain.Checked)
|
||||
{
|
||||
var tile = Map.Terrain.GetTile(x / 2, y / 2);
|
||||
TerrainTile tile = Editor.Terrain.GetTile(x / 2, y / 2);
|
||||
ViewTile(tile);
|
||||
}
|
||||
}
|
||||
|
|
@ -578,12 +584,12 @@ private void Menu_Set_Click(object sender, EventArgs e)
|
|||
|
||||
if (RB_Item.Checked)
|
||||
{
|
||||
var tile = Map.CurrentLayer.GetTile(x, y);
|
||||
var tile = CurrentLayer.GetTile(x, y);
|
||||
SetTile(tile, x, y);
|
||||
}
|
||||
else if (RB_Terrain.Checked)
|
||||
{
|
||||
var tile = Map.Terrain.GetTile(x / 2, y / 2);
|
||||
var tile = Editor.Terrain.GetTile(x / 2, y / 2);
|
||||
SetTile(tile);
|
||||
}
|
||||
}
|
||||
|
|
@ -595,12 +601,12 @@ private void Menu_Reset_Click(object sender, EventArgs e)
|
|||
|
||||
if (RB_Item.Checked)
|
||||
{
|
||||
var tile = Map.CurrentLayer.GetTile(x, y);
|
||||
var tile = CurrentLayer.GetTile(x, y);
|
||||
DeleteTile(tile, x, y);
|
||||
}
|
||||
else if (RB_Terrain.Checked)
|
||||
{
|
||||
var tile = Map.Terrain.GetTile(x / 2, y / 2);
|
||||
var tile = Editor.Terrain.GetTile(x / 2, y / 2);
|
||||
DeleteTile(tile);
|
||||
}
|
||||
}
|
||||
|
|
@ -617,10 +623,11 @@ private void CM_Click_Opening(object sender, System.ComponentModel.CancelEventAr
|
|||
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 flagLayer = NUD_Layer.Value == 0 ? Map.LayerItemFlag0 : Map.LayerItemFlag1;
|
||||
var isActive = flagLayer.GetIsActive(x, y);
|
||||
Menu_Activate.Text = isActive ? "Inactivate" : "Activate";
|
||||
CM_Click.Items.Add(Menu_Activate);
|
||||
hasActivate = true;
|
||||
}
|
||||
|
|
@ -629,14 +636,15 @@ 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 flagLayer = NUD_Layer.Value == 0 ? Map.LayerItemFlag0 : Map.LayerItemFlag1;
|
||||
var isActive = flagLayer.GetIsActive(x, y);
|
||||
flagLayer.SetIsActive(x, y, !isActive);
|
||||
}
|
||||
|
||||
private void B_Up_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (ModifierKeys == Keys.Shift)
|
||||
CB_Acre.SelectedIndex = Math.Max(0, CB_Acre.SelectedIndex - View.Map.Items.Layer1.TileInfo.Columns);
|
||||
CB_Acre.SelectedIndex = Math.Max(0, CB_Acre.SelectedIndex - Editor.Items.Layer0.TileInfo.Columns);
|
||||
else if (View.ArrowUp())
|
||||
LoadItemGridAcre();
|
||||
}
|
||||
|
|
@ -660,18 +668,18 @@ 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 + View.Map.Items.Layer1.TileInfo.Columns, 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_DumpAcre_Click(object sender, EventArgs e) => MapDumpHelper.DumpLayerAcreSingle(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_DumpAllAcres_Click(object sender, EventArgs e) => MapDumpHelper.DumpLayerAcreAll(CurrentLayer);
|
||||
|
||||
private void B_ImportAcre_Click(object sender, EventArgs e)
|
||||
{
|
||||
var layer = Map.CurrentLayer;
|
||||
var layer = CurrentLayer;
|
||||
if (!MapDumpHelper.ImportToLayerAcreSingle(layer, AcreIndex, CB_Acre.Text, (int)NUD_Layer.Value))
|
||||
return;
|
||||
ChangeViewToAcre(AcreIndex);
|
||||
|
|
@ -680,33 +688,33 @@ private void B_ImportAcre_Click(object sender, EventArgs e)
|
|||
|
||||
private void B_ImportAllAcres_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!MapDumpHelper.ImportToLayerAcreAll(Map.CurrentLayer))
|
||||
if (!MapDumpHelper.ImportToLayerAcreAll(CurrentLayer))
|
||||
return;
|
||||
ChangeViewToAcre(AcreIndex);
|
||||
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) => MapDumpHelper.DumpTerrainAcre(Editor.Terrain, AcreIndex, CB_Acre.Text);
|
||||
|
||||
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))
|
||||
if (!MapDumpHelper.ImportTerrainAcre(Editor.Terrain, AcreIndex, CB_Acre.Text))
|
||||
return;
|
||||
ChangeViewToAcre(AcreIndex);
|
||||
System.Media.SystemSounds.Asterisk.Play();
|
||||
|
|
@ -714,7 +722,7 @@ private void B_ImportTerrainAcre_Click(object sender, EventArgs e)
|
|||
|
||||
private void B_ImportTerrainAll_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!MapDumpHelper.ImportTerrainAll(Map.Terrain))
|
||||
if (!MapDumpHelper.ImportTerrainAll(Editor.Terrain))
|
||||
return;
|
||||
ChangeViewToAcre(AcreIndex);
|
||||
System.Media.SystemSounds.Asterisk.Play();
|
||||
|
|
@ -769,7 +777,7 @@ private void PB_Map_MouseDown(object sender, MouseEventArgs e)
|
|||
|
||||
private void ClickMapAt(MouseEventArgs e, bool skipLagCheck)
|
||||
{
|
||||
var layer = Map.Items.Layer1;
|
||||
var layer = Editor.Items.Layer0;
|
||||
int mX = e.X;
|
||||
int mY = e.Y;
|
||||
bool centerReticle = CHK_SnapToAcre.Checked;
|
||||
|
|
@ -824,7 +832,7 @@ private void PB_Map_MouseMove(object sender, MouseEventArgs e)
|
|||
|
||||
private void NUD_Layer_ValueChanged(object sender, EventArgs e)
|
||||
{
|
||||
Map.MapLayer = (int)NUD_Layer.Value - 1;
|
||||
View.ItemLayerIndex = (int)NUD_Layer.Value - 1;
|
||||
LoadItemGridAcre();
|
||||
}
|
||||
|
||||
|
|
@ -837,7 +845,7 @@ private void Remove(ToolStripItem sender, Func<int, int, int, int, int> removal)
|
|||
if (question != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
int count = View.ModifyFieldItems(removal, wholeMap);
|
||||
int count = Editor.Mutator.ModifyFieldItems(removal, wholeMap);
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
|
|
@ -857,7 +865,7 @@ private void Modify(ToolStripItem sender, Func<int, int, int, int, int> action)
|
|||
if (question != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
int count = View.ModifyFieldItems(action, wholeMap);
|
||||
int count = Editor.Mutator.ModifyFieldItems(action, wholeMap);
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
|
|
@ -868,54 +876,46 @@ private void Modify(ToolStripItem sender, Func<int, int, int, int, int> 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<int, int, int, int, int>((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<int, int, int, int, int>((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
|
||||
|
|
@ -930,7 +930,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();
|
||||
}
|
||||
|
||||
|
|
@ -938,7 +938,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();
|
||||
}
|
||||
|
||||
|
|
@ -957,7 +957,7 @@ private void LoadIndex(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;
|
||||
|
|
@ -981,7 +981,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)
|
||||
|
|
@ -999,7 +999,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();
|
||||
}
|
||||
|
||||
|
|
@ -1009,7 +1009,7 @@ private void NUD_BuildingType_ValueChanged(object sender, EventArgs e)
|
|||
|
||||
private void CB_MapAcre_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
var acre = Map.Terrain.BaseAcres.Span[CB_MapAcre.SelectedIndex * 2];
|
||||
var acre = Editor.Terrain.BaseAcres.Span[CB_MapAcre.SelectedIndex * 2];
|
||||
CB_MapAcreSelect.SelectedValue = (int)acre;
|
||||
|
||||
// Jump view if available
|
||||
|
|
@ -1025,7 +1025,7 @@ private void CB_MapAcreSelect_SelectedValueChanged(object sender, EventArgs e)
|
|||
var index = CB_MapAcre.SelectedIndex;
|
||||
var value = WinFormsUtil.GetIndex(CB_MapAcreSelect);
|
||||
|
||||
var span = Map.Terrain.BaseAcres.Span.Slice(index * 2, 2);
|
||||
var span = Editor.Terrain.BaseAcres.Span.Slice(index * 2, 2);
|
||||
var oldValue = span[0];
|
||||
if (value == oldValue)
|
||||
return;
|
||||
|
|
@ -1036,7 +1036,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.Span))
|
||||
if (!MapDumpHelper.DumpMapAcresAll(Editor.Terrain.BaseAcres.Span))
|
||||
return;
|
||||
ReloadBuildingsTerrain();
|
||||
System.Media.SystemSounds.Asterisk.Play();
|
||||
|
|
@ -1044,7 +1044,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.Span))
|
||||
if (!MapDumpHelper.ImportMapAcresAll(Editor.Terrain.BaseAcres.Span))
|
||||
return;
|
||||
ReloadBuildingsTerrain();
|
||||
System.Media.SystemSounds.Asterisk.Play();
|
||||
|
|
@ -1056,7 +1056,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();
|
||||
|
|
@ -1069,7 +1069,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();
|
||||
|
|
@ -1082,7 +1082,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();
|
||||
|
|
@ -1126,9 +1126,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.TileInfo.TotalWidth, SpawnLayer.TileInfo.TotalHeight);
|
||||
Spawn.ClearDanglingExtensions(0, 0, Spawn.TileInfo.TotalWidth, Spawn.TileInfo.TotalHeight);
|
||||
LoadItemGridAcre();
|
||||
}
|
||||
|
||||
|
|
@ -1149,5 +1149,5 @@ public interface IItemLayerEditor
|
|||
void ReloadItems();
|
||||
|
||||
ItemEditor ItemProvider { get; }
|
||||
ItemLayer SpawnLayer { get; }
|
||||
LayerItem Spawn { get; }
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ 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, int acreIndex, string acre, int layerIndex)
|
||||
{
|
||||
using var ofd = new OpenFileDialog();
|
||||
ofd.Filter = "New Horizons Field Item Layer (*.nhl)|*.nhl|All files (*.*)|*.*";
|
||||
|
|
@ -19,7 +19,7 @@ public static bool ImportToLayerAcreSingle(FieldItemLayer layer, int acreIndex,
|
|||
var path = ofd.FileName;
|
||||
var fi = new FileInfo(path);
|
||||
|
||||
int expect = layer.TileInfo.ViewCount * Item.SIZE;
|
||||
int expect = layerField.TileInfo.ViewCount * Item.SIZE;
|
||||
if (fi.Length != expect)
|
||||
{
|
||||
WinFormsUtil.Error(string.Format(MessageStrings.MsgDataSizeMismatchImport, fi.Length, expect));
|
||||
|
|
@ -27,11 +27,11 @@ public static bool ImportToLayerAcreSingle(FieldItemLayer layer, int acreIndex,
|
|||
}
|
||||
|
||||
var data = File.ReadAllBytes(path);
|
||||
layer.ImportAcre(acreIndex, data);
|
||||
layerField.ImportAcre(acreIndex, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ImportToLayerAcreAll(FieldItemLayer layer)
|
||||
public static bool ImportToLayerAcreAll(LayerFieldItem layerField)
|
||||
{
|
||||
using var ofd = new OpenFileDialog();
|
||||
ofd.Filter = "New Horizons Field Item Layer (*.nhl)|*.nhl|All files (*.*)|*.*";
|
||||
|
|
@ -41,7 +41,7 @@ public static bool ImportToLayerAcreAll(FieldItemLayer layer)
|
|||
|
||||
var path = ofd.FileName;
|
||||
var fi = new FileInfo(path);
|
||||
var expect = layer.TileInfo.TotalCount * Item.SIZE;
|
||||
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));
|
||||
|
|
@ -50,11 +50,11 @@ public static bool ImportToLayerAcreAll(FieldItemLayer layer)
|
|||
|
||||
var data = File.ReadAllBytes(path);
|
||||
FieldItemUpgrade.DetectUpdate(ref data, expect);
|
||||
layer.ImportAll(data);
|
||||
layerField.ImportAll(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void DumpLayerAcreSingle(FieldItemLayer layer, int acreIndex, string acre, int layerIndex)
|
||||
public static void DumpLayerAcreSingle(LayerFieldItem layerField, int acreIndex, string acre, int layerIndex)
|
||||
{
|
||||
using var sfd = new SaveFileDialog();
|
||||
sfd.Filter = "New Horizons Field Item Layer (*.nhl)|*.nhl|All files (*.*)|*.*";
|
||||
|
|
@ -63,11 +63,11 @@ public static void DumpLayerAcreSingle(FieldItemLayer layer, int acreIndex, stri
|
|||
return;
|
||||
|
||||
var path = sfd.FileName;
|
||||
var data = layer.DumpAcre(acreIndex);
|
||||
var data = layerField.DumpAcre(acreIndex);
|
||||
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,11 +76,11 @@ 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, int acreIndex, string acre)
|
||||
{
|
||||
using var ofd = new OpenFileDialog();
|
||||
ofd.Filter = "New Horizons Terrain (*.nht)|*.nht|All files (*.*)|*.*";
|
||||
|
|
@ -103,7 +103,7 @@ public static bool ImportTerrainAcre(TerrainLayer m, int acreIndex, string acre)
|
|||
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 (*.*)|*.*";
|
||||
|
|
@ -126,7 +126,7 @@ public static bool ImportTerrainAll(TerrainLayer m)
|
|||
return true;
|
||||
}
|
||||
|
||||
public static void DumpTerrainAcre(TerrainLayer m, int acreIndex, string acre)
|
||||
public static void DumpTerrainAcre(LayerTerrain m, int acreIndex, string acre)
|
||||
{
|
||||
using var sfd = new SaveFileDialog();
|
||||
sfd.Filter = "New Horizons Terrain (*.nht)|*.nht|All files (*.*)|*.*";
|
||||
|
|
@ -139,7 +139,7 @@ public static void DumpTerrainAcre(TerrainLayer m, int acreIndex, string acre)
|
|||
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 (*.*)|*.*";
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ public partial class PlayerHouseEditor : Form
|
|||
private readonly MainSave SAV;
|
||||
private readonly IPlayerHouse[] Houses;
|
||||
private readonly IReadOnlyList<Player> 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<Player> 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);
|
||||
|
|
@ -157,12 +157,12 @@ 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.TileInfo.TotalWidth;
|
||||
var h = layer.TileInfo.TotalHeight;
|
||||
|
|
@ -186,7 +186,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 +211,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 +220,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,17 +229,17 @@ 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 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);
|
||||
|
|
@ -257,7 +257,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 +289,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();
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user