mirror of
https://github.com/kwsch/NHSE.git
synced 2026-03-22 09:44:49 -05:00
Updates the Field Item Editor to render layers based on the entire map, and the per-patch positioning of each layer. Import/export will gracefully handle upgrade/downgrade, and viewport import/export will gracefully update tiles rather than a per-acre basis. Performance has also been slightly improved; no allocation is done anymore when updating the image.
154 lines
4.6 KiB
C#
154 lines
4.6 KiB
C#
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>
|
|
/// Drags the view by the specified delta amounts.
|
|
/// </summary>
|
|
/// <returns><see langword="true"/> if the view changed; otherwise, <see langword="false"/>.</returns>
|
|
public bool DragView(int dX, int dY) => SetViewTo(X + dX, Y + dY);
|
|
|
|
/// <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);
|
|
}
|
|
|
|
public (int X, int Y) EnforceEdgeBuffer(int x, int y)
|
|
{
|
|
x = Math.Clamp(x, 0, MaxX - EdgeBuffer);
|
|
y = Math.Clamp(y, 0, MaxY - EdgeBuffer);
|
|
return (x, y);
|
|
}
|
|
|
|
public bool IsWithinView(int x, int y, int tileStride)
|
|
{
|
|
if (x < X || x >= X + tileStride)
|
|
return false;
|
|
if (y < Y || y >= Y + tileStride)
|
|
return false;
|
|
return true;
|
|
}
|
|
} |