NHSE/NHSE.Core/Structures/Map/Managers/MapEditor.cs
Kurt b88c518d5c
Update FieldItemEditor for 3.0.0 (#716)
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.
2026-01-25 16:55:38 -06:00

129 lines
4.4 KiB
C#

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; } = 1;
/// <summary>
/// Amount of pixel upscaling compared to a 1px = 1 tile map.
/// </summary>
public int ViewScale { get; set; } = 16;
/// <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>
public (int X, int Y) GetCursorCoordinates(in int mX, in int mY)
{
var x = mX / MapScale;
var y = mY / MapScale;
return (x, y);
}
/// <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;
/// <summary>
/// Converts building map coordinates to view pixel coordinates.
/// </summary>
/// <param name="relX">Building map X coordinate.</param>
/// <param name="relY">Building map Y coordinate.</param>
/// <returns>View coordinates.</returns>
public (int X, int Y) GetViewCoordinatesBuilding(uint relX, uint relY)
=> GetViewCoordinates((int)relX, (int)relY, Mutator.Manager.ConfigBuildings);
public (int X, int Y) GetViewCoordinatesTerrain(int relX, int relY)
=> GetViewCoordinates(relX, relY, Mutator.Manager.ConfigTerrain);
public (int X, int Y) GetViewCoordinatesFieldItem(int relX, int relY)
=> GetViewCoordinates(relX, relY, Mutator.Manager.ConfigItems);
public (int X, int Y) GetViewCoordinates(int posX, int posY, LayerPositionConfig shifter)
{
// Get absolute coordinates from the layer.
var (x, y) = shifter.GetCoordinatesAbsolute(posX, posY);
// Shift to view coordinates
x -= X;
y -= Y;
// Scale to pixel coordinates
x *= ViewScale;
y *= ViewScale;
return (x, y);
}
/// <summary>
/// From a map pixel coordinate (<see cref="MapScale"/>), get the clamped map tile coordinate.
/// </summary>
/// <param name="x">Upscaled Map pixel X coordinate.</param>
/// <param name="y">Upscaled Map pixel Y coordinate.</param>
/// <param name="type">Option to adjust the coordinates to a desired type.</param>
/// <returns></returns>
public (int X, int Y) GetMapCoordinates(int x, int y, MapViewCoordinateRequest type)
{
x /= MapScale;
y /= MapScale;
if (x < 0) x = 0;
if (y < 0) y = 0;
// Adjust the view coordinate
if (type == MapViewCoordinateRequest.Centered)
{
// Reticle size is GridWidth, center = /2
var shift = (LayerFieldItem.TilesPerAcreDim * MapScale) / 2;
x -= shift;
y -= shift;
}
else if (type == MapViewCoordinateRequest.SnapAcre)
{
// Snap to the nearest acre
x -= x % LayerFieldItem.TilesPerAcreDim;
y -= y % LayerFieldItem.TilesPerAcreDim;
}
var view = Mutator.View;
// Clamp to viewport dimensions, and center to nearest acre if desired.
// Clamp to boundaries so that we always have a full grid to view.
return view.EnforceEdgeBuffer(x, y);
}
}
public enum MapViewCoordinateRequest
{
/// <summary>
/// No adjustment to the coordinates.
/// </summary>
None = 0,
/// <summary>
/// Snap the coordinates to the nearest acre boundary.
/// </summary>
SnapAcre,
/// <summary>
/// Center the view around the requested (x,y).
/// </summary>
Centered,
}