mirror of
https://github.com/kwsch/NHSE.git
synced 2026-09-12 05:05:37 -05:00
Push current
Still some issues rendering the inflated viewport
This commit is contained in:
@@ -6,8 +6,8 @@
|
||||
/// <remarks>
|
||||
/// Extension tiles underneath the actual item root are included; extension tiles to the right are not.
|
||||
/// </remarks>
|
||||
/// <param name="X">X Coordinate within the Field Item Layer</param>
|
||||
/// <param name="Y">Y Coordinate within the Field Item Layer</param>
|
||||
/// <param name="RelativeX">X Coordinate within the Field Item Layer</param>
|
||||
/// <param name="RelativeY">Y Coordinate within the Field Item Layer</param>
|
||||
/// <param name="Offset">Offset relative to the start of the Field Item Layer</param>
|
||||
/// <param name="Data">Data for this column</param>
|
||||
public sealed record FieldItemColumn(int X, int Y, int Offset, byte[] Data);
|
||||
public sealed record FieldItemColumn(int RelativeX, int RelativeY, int Offset, byte[] Data);
|
||||
@@ -110,7 +110,7 @@ private static byte[] GetColumnExtension(ReadOnlySpan<Item> items)
|
||||
return Item.SetArray(col);
|
||||
}
|
||||
|
||||
private int GetTileOffset(int x, int y) => Item.SIZE * (y + (x * MapHeight));
|
||||
private int GetTileOffset(int relX, int relY) => Item.SIZE * (relY + (relX * MapHeight));
|
||||
|
||||
private static Item GetDroppedItem(Item item)
|
||||
{
|
||||
|
||||
@@ -100,4 +100,10 @@ public int GetIndexTileRelative(int relX, int relY)
|
||||
// In other words, Item[1] is X=0,Y=1
|
||||
return (relX * (CountHeight << TileBitShift)) + relY;
|
||||
}
|
||||
|
||||
public bool IsCoordinateValidAbsolute(int absX, int absY)
|
||||
{
|
||||
var (relX, relY) = GetCoordinatesRelative(absX, absY);
|
||||
return IsCoordinateValidRelative(relX, relY);
|
||||
}
|
||||
}
|
||||
@@ -22,12 +22,11 @@ public sealed class MapEditor
|
||||
/// </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)
|
||||
public (int X, int Y) GetCursorCoordinates(in int mX, in int mY)
|
||||
{
|
||||
x = mX / MapScale;
|
||||
y = mY / MapScale;
|
||||
var x = mX / MapScale;
|
||||
var y = mY / MapScale;
|
||||
return (x, y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -29,9 +29,24 @@ public int ReplaceFieldItems(Item oldItem, Item newItem, in bool wholeMap)
|
||||
/// <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;
|
||||
int xMin, yMin, width, height;
|
||||
if (wholeMap)
|
||||
{
|
||||
(xMin, yMin) = (0, 0);
|
||||
var info = layerField.TileInfo;
|
||||
(width, height) = info.DimTotal;
|
||||
}
|
||||
else
|
||||
{
|
||||
(xMin, yMin) = (View.X, View.Y);
|
||||
// Convert absolute to relative coordinates
|
||||
if (!Manager.ConfigItems.IsCoordinateValidAbsolute(xMin, yMin))
|
||||
return 0;
|
||||
(xMin, yMin) = Manager.ConfigItems.GetCoordinatesRelative(xMin, yMin);
|
||||
|
||||
var info = layerField.TileInfo;
|
||||
(width, height) = info.DimAcre;
|
||||
}
|
||||
return action(xMin, yMin, width, height);
|
||||
}
|
||||
|
||||
@@ -45,9 +60,25 @@ public int ModifyFieldItems(Func<int, int, int, int, int> action, in bool wholeM
|
||||
/// <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;
|
||||
int xMin, yMin, width, height;
|
||||
if (wholeMap)
|
||||
{
|
||||
(xMin, yMin) = (0, 0);
|
||||
var info = layerField.TileInfo;
|
||||
(width, height) = info.DimTotal;
|
||||
}
|
||||
else
|
||||
{
|
||||
(xMin, yMin) = (View.X, View.Y);
|
||||
// Convert absolute to relative coordinates
|
||||
if (!Manager.ConfigItems.IsCoordinateValidAbsolute(xMin, yMin))
|
||||
return 0;
|
||||
(xMin, yMin) = Manager.ConfigItems.GetCoordinatesRelative(xMin, yMin);
|
||||
|
||||
var info = layerField.TileInfo;
|
||||
(width, height) = info.DimAcre;
|
||||
}
|
||||
|
||||
return layerField.ReplaceAll(oldItem, newItem, xMin, yMin, width, height);
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,12 @@ public bool SetViewTo(in int absX, in int absY)
|
||||
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>
|
||||
|
||||
@@ -76,9 +76,9 @@ public void CopyRoadFrom(TerrainTile tile)
|
||||
LandMakingAngleRoad = tile.LandMakingAngleRoad;
|
||||
}
|
||||
|
||||
public bool Rotate() => UnitModelRoad != 0 ? RotateRoad() : RotateTerrain();
|
||||
public bool TryRotate() => UnitModelRoad != 0 ? TryRotateRoad() : TryRotateTerrain();
|
||||
|
||||
private bool RotateTerrain()
|
||||
private bool TryRotateTerrain()
|
||||
{
|
||||
if (UnitModel == TerrainUnitModel.Base)
|
||||
return false;
|
||||
@@ -88,7 +88,7 @@ private bool RotateTerrain()
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool RotateRoad()
|
||||
private bool TryRotateRoad()
|
||||
{
|
||||
var rot = LandMakingAngleRoad;
|
||||
rot = (ushort) ((rot + 1) & 3);
|
||||
|
||||
@@ -11,43 +11,38 @@ public static class ItemLayerSprite
|
||||
{
|
||||
private static readonly Pen Reticle = new(Color.Red);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a bitmap representation of the provided item layer at a 1px-per-tile scale.
|
||||
/// </summary>
|
||||
/// <param name="layer">Item layer to render.</param>
|
||||
public static Bitmap GetBitmapItemLayer(LayerItem layer)
|
||||
{
|
||||
var items = layer.Tiles;
|
||||
var imgHeight = layer.TileInfo.TotalHeight;
|
||||
var imgWidth = items.Length / imgHeight;
|
||||
|
||||
var bmpData = new int[imgWidth * imgHeight];
|
||||
LoadBitmapLayer(items, bmpData, imgWidth, imgHeight);
|
||||
|
||||
return ImageUtil.GetBitmap(bmpData, imgWidth, imgHeight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates a bitmap data buffer with color values derived from a collection of items, arranging the colors in column-major order based on the specified width and height.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Each item's color is determined using FieldItemColor.GetItemColor and stored in ARGB format.
|
||||
/// Each item's color is determined using <see cref="FieldItemColor.GetItemColor"/> and stored in ARGB format.
|
||||
/// Colors are written to bmpData in column-major order, where each column is filled from top to bottom.
|
||||
/// </remarks>
|
||||
/// <param name="items">List of items from which color values are extracted. The span must contain at least width × height elements.</param>
|
||||
/// <param name="bmpData">Pixel data for the bitmap. The span must have a length of at least width × height.</param>
|
||||
/// <param name="cfg">Configuration for layer positioning.</param>
|
||||
/// <param name="imgWidth">The number of columns in the bitmap. Must be greater than zero.</param>
|
||||
/// <param name="imgHeight">The number of rows in the bitmap. Must be greater than zero.</param>
|
||||
private static void LoadBitmapLayer(ReadOnlySpan<Item> items, Span<int> bmpData, int imgWidth, int imgHeight)
|
||||
private static void LoadBitmapLayer(ReadOnlySpan<Item> items, Span<int> bmpData, in LayerPositionConfig cfg, int imgWidth, int imgHeight)
|
||||
{
|
||||
var (shiftX, shiftY) = cfg.GetCoordinatesAbsolute(0, 0);
|
||||
|
||||
// Iterate through the relative positions within the layer.
|
||||
// Then, map to absolute positions in the bitmap with the configured shift.
|
||||
for (int x = 0; x < imgWidth; x++)
|
||||
{
|
||||
var ix = x * imgHeight;
|
||||
for (int y = 0; y < imgHeight; y++)
|
||||
{
|
||||
// Get the tile at this position.
|
||||
var index = ix + y;
|
||||
var tile = items[index];
|
||||
bmpData[(y * imgWidth) + x] = FieldItemColor.GetItemColor(tile).ToArgb();
|
||||
|
||||
// Get the actual shifted position in the bitmap.
|
||||
var offset = ((y + shiftY) * imgWidth) + (x + shiftX);
|
||||
|
||||
// Write the color to the bitmap data.
|
||||
bmpData[offset] = FieldItemColor.GetItemColor(tile).ToArgb();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,62 +50,69 @@ private static void LoadBitmapLayer(ReadOnlySpan<Item> items, Span<int> bmpData,
|
||||
/// <summary>
|
||||
/// Loads an item layer into a bitmap, scaling it up to the desired size, drawing special symbols, and overlaying a grid.
|
||||
/// </summary>
|
||||
/// <param name="img">Inflated acre bitmap to write to.</param>
|
||||
/// <param name="imgScaled">Inflated acre bitmap to write to.</param>
|
||||
/// <param name="layer">Item layer to draw from.</param>
|
||||
/// <param name="relX">Top-left X coordinate to start drawing from, relative to the origin of the layer (not map coordinates).</param>
|
||||
/// <param name="relY">Top-left Y coordinate to start drawing from.</param>
|
||||
/// <param name="cfg">Configuration for layer positioning.</param>
|
||||
/// <param name="absX">Top-left X coordinate to start drawing from, relative to the origin of the map.</param>
|
||||
/// <param name="absY">Top-left Y coordinate to start drawing from.</param>
|
||||
/// <param name="imgSingle">Pixel data for 1px per tile image.</param>
|
||||
/// <param name="imgUpscaled">>Pixel data for final inflated image.</param>
|
||||
/// <param name="imgScale">Scaling factor from 1px => final image dimensions.</param>
|
||||
/// <param name="transparency">Optional transparency override color.</param>
|
||||
/// <param name="gridlineColor">Color to use for gridlines.</param>
|
||||
public static void LoadItemLayerViewGrid(Bitmap img, LayerItem layer, int relX, int relY,
|
||||
public static void LoadViewport(Bitmap imgScaled, LayerItem layer, in LayerPositionConfig cfg, int absX, int absY,
|
||||
Span<int> imgSingle, Span<int> imgUpscaled, int imgScale, int transparency = -1, int gridlineColor = 0)
|
||||
{
|
||||
// Update the 1px view-grid image pixel data.
|
||||
LoadViewport(imgSingle, layer, relX, relY);
|
||||
LoadViewport(layer, cfg, imgSingle, absX, absY);
|
||||
|
||||
// Get the final inflated size of the image.
|
||||
int imgWidth = layer.TileInfo.ViewWidth;
|
||||
int h = layer.TileInfo.ViewHeight;
|
||||
imgWidth *= imgScale;
|
||||
h *= imgScale;
|
||||
var imgWidth = imgScaled.Width;
|
||||
var imgHeight = imgScaled.Height;
|
||||
// Inflate to the final size storage.
|
||||
ImageUtil.ScalePixelImage(imgSingle, imgUpscaled, imgWidth, h, imgScale);
|
||||
ImageUtil.ScalePixelImage(imgSingle, imgUpscaled, imgWidth, imgHeight, imgScale);
|
||||
|
||||
// Optional transparency clamping to make image fainter.
|
||||
if (transparency >>> 24 != 0xFF)
|
||||
ImageUtil.ClampAllTransparencyTo(imgUpscaled, transparency);
|
||||
|
||||
// Draw symbols over special items now?
|
||||
DrawDirectionals(imgUpscaled, layer, relX, relY, imgWidth, imgScale);
|
||||
DrawDirectionals(layer, cfg, imgUpscaled, absX, absY, imgWidth, imgScale);
|
||||
|
||||
// Apply gridlines to visually separate each cell.
|
||||
DrawGrid(imgUpscaled, imgWidth, h, gridlineColor, imgScale);
|
||||
DrawGrid(imgUpscaled, imgWidth, imgHeight, gridlineColor, imgScale);
|
||||
|
||||
// Update the bitmap, final data.
|
||||
img.SetBitmapData(imgUpscaled);
|
||||
imgScaled.SetBitmapData(imgUpscaled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads pixel data from the specified layer into the provided span, using the given starting coordinates and the layer's view dimensions.
|
||||
/// </summary>
|
||||
/// <param name="data">Pixel data of the final image.</param>
|
||||
/// <param name="layer">The layer from which to load pixel data.</param>
|
||||
/// <param name="relX">The x-coordinate of the upper-left corner in the layer from which to start loading pixels.</param>
|
||||
/// <param name="relY">The y-coordinate of the upper-left corner in the layer from which to start loading pixels.</param>
|
||||
private static void LoadViewport(Span<int> data, LayerItem layer, int relX, int relY)
|
||||
/// <param name="cfg">Configuration for layer positioning.</param>
|
||||
/// <param name="data">Pixel data of the final image.</param>
|
||||
/// <param name="absX">The x-coordinate of the upper-left corner in the map from which to start loading pixels.</param>
|
||||
/// <param name="absY">The y-coordinate of the upper-left corner in the map from which to start loading pixels.</param>
|
||||
private static void LoadViewport(LayerItem layer, in LayerPositionConfig cfg, Span<int> data, int absX, int absY)
|
||||
{
|
||||
var width = layer.TileInfo.ViewWidth;
|
||||
var height = layer.TileInfo.ViewHeight;
|
||||
|
||||
var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY);
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
var baseIndex = (y * width);
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
var tile = layer.GetTile(relX + x, relY + y);
|
||||
var tileX = relX + x;
|
||||
var tileY = relY + y;
|
||||
if (!cfg.IsCoordinateValidRelative(tileX, tileY))
|
||||
continue;
|
||||
var tile = layer.GetTile(tileX, tileY);
|
||||
var color = FieldItemColor.GetItemColor(tile).ToArgb();
|
||||
|
||||
var index = baseIndex + x;
|
||||
data[index] = color;
|
||||
}
|
||||
@@ -120,34 +122,41 @@ private static void LoadViewport(Span<int> data, LayerItem layer, int relX, int
|
||||
/// <summary>
|
||||
/// Draws extension tile info for the tiles in the specified layer onto the provided pixel data.
|
||||
/// </summary>
|
||||
/// <param name="data">Pixel data of the entire image.</param>
|
||||
/// <param name="layer">The layer containing the tiles to process.</param>
|
||||
/// <param name="relX">Top-left X coordinate to start drawing from, relative to the origin of the layer (not map coordinates).</param>
|
||||
/// <param name="relY">Top-left Y coordinate to start drawing from.</param>
|
||||
/// <param name="cfg">Configuration for layer positioning.</param>
|
||||
/// <param name="data">Pixel data of the entire image.</param>
|
||||
/// <param name="absX">Top-left X coordinate to start drawing from, relative to the origin of the map.</param>
|
||||
/// <param name="absY">Top-left Y coordinate to start drawing from.</param>
|
||||
/// <param name="imgWidth">Width of the entire image.</param>
|
||||
/// <param name="imgScale">Scaling factor from 1px => final image dimensions.</param>
|
||||
private static void DrawDirectionals(Span<int> data, LayerItem layer, int relX, int relY, int imgWidth, int imgScale)
|
||||
private static void DrawDirectionals(LayerItem layer, in LayerPositionConfig cfg,
|
||||
Span<int> data,
|
||||
int absX, int absY, int imgWidth, int imgScale)
|
||||
{
|
||||
var width = layer.TileInfo.ViewWidth;
|
||||
var height = layer.TileInfo.ViewHeight;
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY);
|
||||
|
||||
for (int viewX = 0; viewX < width; viewX++)
|
||||
{
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int viewY = 0; viewY < height; viewY++)
|
||||
{
|
||||
var pX = relX + x;
|
||||
var pY = relY + y;
|
||||
var pX = relX + viewX;
|
||||
var pY = relY + viewY;
|
||||
if (!cfg.IsCoordinateValidRelative(pX, pY))
|
||||
continue;
|
||||
var tile = layer.GetTile(pX, pY);
|
||||
if (tile.IsNone)
|
||||
continue;
|
||||
|
||||
// Apply cosmetic details based on the tile's details.
|
||||
if (tile.IsBuried)
|
||||
DrawX(data, relX * imgScale, relY * imgScale, imgScale, imgWidth);
|
||||
DrawX(data, viewX * imgScale, viewY * imgScale, imgScale, imgWidth);
|
||||
else if (tile.IsDropped)
|
||||
DrawPlus(data, relX * imgScale, relY * imgScale, imgScale, imgWidth);
|
||||
DrawPlus(data, viewX * imgScale, viewY * imgScale, imgScale, imgWidth);
|
||||
else if (tile.IsExtension)
|
||||
DrawDirectional(data, tile, relX * imgScale, relY * imgScale, imgScale, imgWidth);
|
||||
DrawDirectional(data, tile, viewX * imgScale, viewY * imgScale, imgScale, imgWidth);
|
||||
|
||||
// Based on the display item, apply details.
|
||||
var id = tile.DisplayItemId;
|
||||
@@ -159,7 +168,7 @@ private static void DrawDirectionals(Span<int> data, LayerItem layer, int relX,
|
||||
var geneValue = (genes >> (geneIndex * 2)) & 3;
|
||||
if (geneValue == 0)
|
||||
continue;
|
||||
DrawGene(data, relX * imgScale, relY * imgScale, imgScale, imgWidth, geneValue, geneIndex);
|
||||
DrawGene(data, absX * imgScale, absY * imgScale, imgScale, imgWidth, geneValue, geneIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,8 +181,13 @@ private static int GetGeneIndex(ref Item tile, LayerItem layer, int relX, int re
|
||||
{
|
||||
if (tile.IsRoot)
|
||||
return 0;
|
||||
var geneIndex = (tile.ExtensionY << 1) | tile.ExtensionX;
|
||||
tile = layer.GetTile(relX - tile.ExtensionX, relY - tile.ExtensionY);
|
||||
|
||||
// Sanity check: can only extend by 1 in either direction, and must extend in same direction as relative position.
|
||||
// Ignore bad extension values; we know the gene index from position alone.
|
||||
var eX = relX & 1;
|
||||
var eY = relY & 1;
|
||||
var geneIndex = (eY << 1) | eX;
|
||||
tile = layer.GetTile(relX - eX, relY - eY);
|
||||
return geneIndex;
|
||||
}
|
||||
|
||||
@@ -181,35 +195,35 @@ private static int GetGeneIndex(ref Item tile, LayerItem layer, int relX, int re
|
||||
/// Draws a flower gene on the item cell.
|
||||
/// </summary>
|
||||
/// <param name="data">Pixel data of the entire image.</param>
|
||||
/// <param name="x0">Top-left X coordinate to start drawing from.</param>
|
||||
/// <param name="y0">Top-left Y coordinate to start drawing from.</param>
|
||||
/// <param name="viewX">Top-left X coordinate to start drawing from.</param>
|
||||
/// <param name="viewY">Top-left Y coordinate to start drawing from.</param>
|
||||
/// <param name="imgScale">Scale of the entire cell.</param>
|
||||
/// <param name="imgWidth">Width of the entire image.</param>
|
||||
/// <param name="geneValue">Value of the gene (0-3).</param>
|
||||
/// <param name="geneIndex">0-3 value indicating which gene (quadrant) to draw.</param>
|
||||
private static void DrawGene(Span<int> data, int x0, int y0, int imgScale, int imgWidth, uint geneValue, int geneIndex)
|
||||
private static void DrawGene(Span<int> data, int viewX, int viewY, int imgScale, int imgWidth, uint geneValue, int geneIndex)
|
||||
{
|
||||
var c = ShiftToGeneCoordinate(ref x0, ref y0, imgScale, geneIndex);
|
||||
FillSquare(data, x0, y0, imgScale / 2, imgWidth, c, geneValue == 3 ? 1 : 2);
|
||||
var c = ShiftToGeneCoordinate(ref viewX, ref viewY, imgScale, geneIndex);
|
||||
FillSquare(data, viewX, viewY, imgScale / 2, imgWidth, c, geneValue == 3 ? 1 : 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills a square region within a one-dimensional span with the specified color value, using a given increment to control the fill step.
|
||||
/// Fills a square region within a one-dimensional span with the specified color value, using a given increment to control the fill step.
|
||||
/// </summary>
|
||||
/// <remarks>This method assumes that the specified region fits within the bounds of the provided span.
|
||||
/// No bounds checking is performed.
|
||||
/// The increment parameter can be used to fill a subset with the square's elements, which may be useful for performance or pattern effects.
|
||||
/// </remarks>
|
||||
/// <param name="data">The span representing the target buffer to fill. Each element corresponds to a pixel or cell in a row-major order grid.</param>
|
||||
/// <param name="x0">Top-left X coordinate to start drawing from.</param>
|
||||
/// <param name="y0">Top-left Y coordinate to start drawing from.</param>
|
||||
/// <param name="viewX">Top-left X coordinate to start drawing from.</param>
|
||||
/// <param name="viewY">Top-left Y coordinate to start drawing from.</param>
|
||||
/// <param name="imgScale">Scale of the entire cell.</param>
|
||||
/// <param name="imgWidth">Width of the entire image.</param>
|
||||
/// <param name="color">The color value to assign to each filled element in the square region.</param>
|
||||
/// <param name="increment">The step size to use when filling elements. Must be positive. A larger increment skips more elements within the square.</param>
|
||||
private static void FillSquare(Span<int> data, int x0, int y0, int imgScale, int imgWidth, int color, int increment)
|
||||
private static void FillSquare(Span<int> data, int viewX, int viewY, int imgScale, int imgWidth, int color, int increment)
|
||||
{
|
||||
var baseIndex = (y0 * imgWidth) + x0;
|
||||
var baseIndex = (viewY * imgWidth) + viewX;
|
||||
for (int i = 0; i < imgScale * imgScale; i += increment)
|
||||
{
|
||||
var x = i % imgScale;
|
||||
@@ -226,24 +240,24 @@ private static void FillSquare(Span<int> data, int x0, int y0, int imgScale, int
|
||||
/// The method modifies the input coordinates in place according to the specified gene region.
|
||||
/// The returned color value corresponds to the region: Red for bottom right, Yellow for bottom left, AntiqueWhite for top right, and Black for top left or any other index.
|
||||
/// </remarks>
|
||||
/// <param name="x0">Top-left X coordinate to start drawing from.</param>
|
||||
/// <param name="y0">Top-left Y coordinate to start drawing from.</param>
|
||||
/// <param name="absX">Top-left X coordinate to start drawing from.</param>
|
||||
/// <param name="absY">Top-left Y coordinate to start drawing from.</param>
|
||||
/// <param name="imgScale">Scale of the entire cell.</param>
|
||||
/// <param name="geneIndex">The index of the gene region to shift to. Valid values are 0 (bottom right), 1 (bottom left), 2 (top right), and any other value for top left.</param>
|
||||
/// <returns>An integer representing the ARGB color value associated with the specified gene region.</returns>
|
||||
private static int ShiftToGeneCoordinate(ref int x0, ref int y0, int imgScale, int geneIndex)
|
||||
private static int ShiftToGeneCoordinate(ref int absX, ref int absY, int imgScale, int geneIndex)
|
||||
{
|
||||
switch (geneIndex)
|
||||
{
|
||||
case 0: // bottom right
|
||||
x0 += imgScale / 2;
|
||||
y0 += imgScale / 2;
|
||||
absX += imgScale / 2;
|
||||
absY += imgScale / 2;
|
||||
return Color.Red.ToArgb();
|
||||
case 1: // bottom left
|
||||
y0 += imgScale / 2;
|
||||
absY += imgScale / 2;
|
||||
return Color.Yellow.ToArgb();
|
||||
case 2: // top right
|
||||
x0 += imgScale / 2;
|
||||
absX += imgScale / 2;
|
||||
return Color.AntiqueWhite.ToArgb();
|
||||
default: // top left
|
||||
return Color.Black.ToArgb();
|
||||
@@ -254,13 +268,13 @@ private static int ShiftToGeneCoordinate(ref int x0, ref int y0, int imgScale, i
|
||||
/// Updates the pixel data to draw a `+`, indicating the item as "dropped".
|
||||
/// </summary>
|
||||
/// <param name="data">Pixel data of the entire image.</param>
|
||||
/// <param name="x0">Top-left X coordinate to start drawing from.</param>
|
||||
/// <param name="y0">Top-left Y coordinate to start drawing from.</param>
|
||||
/// <param name="viewX">Top-left X coordinate to start drawing from.</param>
|
||||
/// <param name="viewY">Top-left Y coordinate to start drawing from.</param>
|
||||
/// <param name="imgScale">Scaling factor from 1px => final image dimensions.</param>
|
||||
/// <param name="imgWidth">Width of the entire image.</param>
|
||||
private static void DrawPlus(Span<int> data, int x0, int y0, int imgScale, int imgWidth)
|
||||
private static void DrawPlus(Span<int> data, int viewX, int viewY, int imgScale, int imgWidth)
|
||||
{
|
||||
var x0y0 = (imgWidth * y0) + x0;
|
||||
var x0y0 = (imgWidth * viewY) + viewX;
|
||||
var s2 = imgScale / 2;
|
||||
var ws2 = imgWidth * s2;
|
||||
|
||||
@@ -280,17 +294,17 @@ private static void DrawPlus(Span<int> data, int x0, int y0, int imgScale, int i
|
||||
/// Updates the pixel data to draw an `X`, indicating the item as "buried".
|
||||
/// </summary>
|
||||
/// <param name="data">Pixel data of the entire image.</param>
|
||||
/// <param name="x0">Top-left X coordinate to start drawing from.</param>
|
||||
/// <param name="y0">Top-left Y coordinate to start drawing from.</param>
|
||||
/// <param name="viewX">Top-left X coordinate to start drawing from.</param>
|
||||
/// <param name="viewY">Top-left Y coordinate to start drawing from.</param>
|
||||
/// <param name="imgScale">Scaling factor from 1px => final image dimensions.</param>
|
||||
/// <param name="imgWidth">Width of the entire image.</param>
|
||||
private static void DrawX(Span<int> data, int x0, int y0, int imgScale, int imgWidth)
|
||||
private static void DrawX(Span<int> data, int viewX, int viewY, int imgScale, int imgWidth)
|
||||
{
|
||||
var opposite = imgScale - 1;
|
||||
var wo = imgWidth * opposite;
|
||||
|
||||
// Starting offsets for each of the slashes
|
||||
var bBackward = (imgWidth * y0) + x0; // Backwards \
|
||||
var bBackward = (imgWidth * viewY) + viewX; // Backwards \
|
||||
var bForward = bBackward + wo; // Forwards /
|
||||
|
||||
for (int x = 0; x < imgScale; x++)
|
||||
@@ -308,11 +322,11 @@ private static void DrawX(Span<int> data, int x0, int y0, int imgScale, int imgW
|
||||
/// </summary>
|
||||
/// <param name="data">Pixel data of the entire image.</param>
|
||||
/// <param name="tile">Extension tile data.</param>
|
||||
/// <param name="x0">Top-left X coordinate to start drawing from.</param>
|
||||
/// <param name="y0">Top-left Y coordinate to start drawing from.</param>
|
||||
/// <param name="viewX">Top-left X coordinate to start drawing from.</param>
|
||||
/// <param name="viewY">Top-left Y coordinate to start drawing from.</param>
|
||||
/// <param name="imgScale">Scaling factor from 1px => final image dimensions.</param>
|
||||
/// <param name="imgWidth">Width of the entire image.</param>
|
||||
private static void DrawDirectional(Span<int> data, Item tile, int x0, int y0, int imgScale, int imgWidth)
|
||||
private static void DrawDirectional(Span<int> data, Item tile, int viewX, int viewY, int imgScale, int imgWidth)
|
||||
{
|
||||
var eX = tile.ExtensionX;
|
||||
var eY = tile.ExtensionY;
|
||||
@@ -323,10 +337,12 @@ private static void DrawDirectional(Span<int> data, Item tile, int x0, int y0, i
|
||||
var startX = eX >= eY ? 0 : start;
|
||||
var startY = eX <= eY ? 0 : start;
|
||||
|
||||
var baseIndex = (imgWidth * y0) + x0;
|
||||
var baseIndex = (imgWidth * viewY) + viewX;
|
||||
for (int x = startX, y = startY; x < imgScale && y < imgScale; x += eX, y += eY)
|
||||
{
|
||||
var index = baseIndex + (imgWidth * y) + x;
|
||||
if (index >= data.Length) // Since we can't guarantee valid extension values, just skip bad ones.
|
||||
continue;
|
||||
data[index] ^= 0x00_808080;
|
||||
}
|
||||
}
|
||||
@@ -364,26 +380,25 @@ public static void DrawGrid(Span<int> data, int imgWidth, int imgHeight, int gri
|
||||
}
|
||||
}
|
||||
|
||||
public static Bitmap GetBitmapItemLayer(Bitmap dest, LayerItem layer, int topX, int topY, Span<int> data, int transparency = -1)
|
||||
/// <summary>
|
||||
/// Loads an item layer into a viewport bitmap, drawing a view reticle over it.
|
||||
/// </summary>
|
||||
/// <param name="cfg">Configuration for layer positioning.</param>
|
||||
/// <param name="layer">Item layer to draw from.</param>
|
||||
/// <param name="data">Pixel data of the final image.</param>
|
||||
/// <param name="transparency">Optional transparency override color.</param>
|
||||
public static void LoadItemLayerDrawReticle(LayerPositionConfig cfg, LayerItem layer, Span<int> data, int transparency = -1)
|
||||
{
|
||||
LoadBitmapLayer(layer.Tiles, data, layer.TileInfo.TotalWidth, layer.TileInfo.TotalHeight);
|
||||
LoadBitmapLayer(layer.Tiles, data, cfg, layer.TileInfo.TotalWidth, layer.TileInfo.TotalHeight);
|
||||
if (transparency >>> 24 != 0xFF)
|
||||
ImageUtil.ClampAllTransparencyTo(data, transparency);
|
||||
dest.SetBitmapData(data);
|
||||
return DrawViewReticle(dest, layer.TileInfo, topX, topY);
|
||||
}
|
||||
|
||||
private static Bitmap DrawViewReticle(Bitmap map, TileGridViewport g, int topX, int topY, int scale = 1)
|
||||
public static void DrawViewReticle(Bitmap map, TileGridViewport g, int absX, int absY, int scale = 1)
|
||||
{
|
||||
using var gfx = Graphics.FromImage(map);
|
||||
gfx.DrawViewReticle(g, topX, topY, scale);
|
||||
return map;
|
||||
}
|
||||
|
||||
private static void DrawViewReticle(this Graphics gfx, TileGridViewport g, int topX, int topY, int scale)
|
||||
{
|
||||
int w = g.ViewWidth * scale;
|
||||
int h = g.ViewHeight * scale;
|
||||
gfx.DrawRectangle(Reticle, topX * scale, topY * scale, w, h);
|
||||
var reticleWidth = g.ViewWidth * scale;
|
||||
var reticleHeight = g.ViewHeight * scale;
|
||||
gfx.DrawRectangle(Reticle, absX * scale, absY * scale, reticleWidth, reticleHeight);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using NHSE.Core;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using NHSE.Core;
|
||||
|
||||
namespace NHSE.Sprites;
|
||||
|
||||
@@ -9,98 +9,132 @@ namespace NHSE.Sprites;
|
||||
/// </summary>
|
||||
public sealed class MapRenderer : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Data source for map rendering.
|
||||
/// </summary>
|
||||
private readonly MapEditor Map;
|
||||
|
||||
/// <summary> Scale factor for full map rendering. </summary>
|
||||
private int MapScale => Map.MapScale;
|
||||
|
||||
/// <summary> Scale factor for acre viewport rendering. </summary>
|
||||
private int ViewScale => Map.ViewScale;
|
||||
|
||||
private const byte FieldItemWidthOld = 7;
|
||||
private const byte FieldItemWidthNew = 9;
|
||||
|
||||
// Cached acre view objects to remove allocation/GC
|
||||
private readonly int[] PixelsItemAcre1;
|
||||
private readonly int[] PixelsItemAcreX;
|
||||
private readonly Bitmap ScaleAcre;
|
||||
private readonly int[] PixelsItemMap;
|
||||
private readonly Bitmap MapReticle;
|
||||
|
||||
private readonly int[] PixelsBackgroundAcre1;
|
||||
private readonly int[] PixelsBackgroundAcreX;
|
||||
private readonly Bitmap BackgroundAcre;
|
||||
/// <summary> 1px scale viewport item layer pixel data. </summary>
|
||||
private readonly int[] ViewportItems1;
|
||||
/// <summary> Upscaled viewport item layer pixel data. </summary>
|
||||
private readonly int[] ViewportItemsX;
|
||||
/// <summary> Upscaled viewport item layer image. </summary>
|
||||
private readonly Bitmap ViewportItemsImage;
|
||||
|
||||
private readonly int[] PixelsBackgroundMap1;
|
||||
private readonly int[] PixelsBackgroundMapX;
|
||||
private readonly Bitmap BackgroundMap;
|
||||
/// <summary> Upscaled map item layer pixel data with reticle. </summary>
|
||||
private readonly int[] MapItemsReticleX;
|
||||
/// <summary> Upscaled map item layer image with reticle. </summary>
|
||||
private readonly Bitmap MapItemsReticleImage;
|
||||
|
||||
/// <summary> 1px scale viewport terrain layer pixel data. </summary>
|
||||
private readonly int[] ViewportTerrain1;
|
||||
/// <summary> Upscaled viewport terrain layer pixel data. </summary>
|
||||
private readonly int[] ViewportTerrainX;
|
||||
/// <summary> Upscaled viewport terrain layer image. </summary>
|
||||
private readonly Bitmap ViewportTerrainImage;
|
||||
|
||||
/// <summary> 1px scale map terrain layer pixel data. </summary>
|
||||
private readonly int[] MapTerrain1;
|
||||
/// <summary> Upscaled map terrain layer pixel data. </summary>
|
||||
private readonly int[] MapTerrainX;
|
||||
/// <summary> Upscaled map terrain layer image. </summary>
|
||||
private readonly Bitmap MapTerrainImage;
|
||||
|
||||
public MapRenderer(MapEditor m)
|
||||
{
|
||||
Map = m;
|
||||
|
||||
// Initialize cached objects based on map size
|
||||
// Get tile info from layer 0 (item layer is the tiniest cell we can render)
|
||||
var l1 = m.Mutator.Manager.FieldItems.Layer0;
|
||||
var info = l1.TileInfo;
|
||||
PixelsItemAcre1 = new int[info.ViewWidth * info.ViewHeight];
|
||||
PixelsItemAcreX = new int[PixelsItemAcre1.Length * ViewScale * ViewScale];
|
||||
ScaleAcre = new Bitmap(info.ViewWidth * ViewScale, info.ViewHeight * ViewScale);
|
||||
|
||||
PixelsItemMap = new int[info.TotalWidth * info.TotalHeight * MapScale * MapScale];
|
||||
MapReticle = new Bitmap(info.TotalWidth * MapScale, info.TotalHeight * MapScale);
|
||||
MapItemsReticleX = new int[info.TotalWidth * info.TotalHeight * MapScale * MapScale];
|
||||
MapItemsReticleImage = new Bitmap(info.TotalWidth * MapScale, info.TotalHeight * MapScale);
|
||||
|
||||
PixelsBackgroundAcre1 = new int[PixelsItemAcre1.Length];
|
||||
PixelsBackgroundAcreX = new int[PixelsItemAcreX.Length];
|
||||
BackgroundAcre = new Bitmap(ScaleAcre.Width, ScaleAcre.Height);
|
||||
MapTerrain1 = new int[MapItemsReticleX.Length / (MapScale * MapScale)];
|
||||
MapTerrainX = new int[MapItemsReticleX.Length];
|
||||
MapTerrainImage = new Bitmap(MapItemsReticleImage.Width, MapItemsReticleImage.Height);
|
||||
|
||||
PixelsBackgroundMap1 = new int[PixelsItemMap.Length / (MapScale * MapScale)];
|
||||
PixelsBackgroundMapX = new int[PixelsItemMap.Length];
|
||||
BackgroundMap = new Bitmap(MapReticle.Width, MapReticle.Height);
|
||||
ViewportItems1 = new int[info.ViewWidth * info.ViewHeight];
|
||||
ViewportItemsX = new int[ViewportItems1.Length * ViewScale * ViewScale];
|
||||
ViewportItemsImage = new Bitmap(info.ViewWidth * ViewScale, info.ViewHeight * ViewScale);
|
||||
|
||||
ViewportTerrain1 = new int[ViewportItems1.Length];
|
||||
ViewportTerrainX = new int[ViewportItemsX.Length];
|
||||
ViewportTerrainImage = new Bitmap(ViewportItemsImage.Width, ViewportItemsImage.Height);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ScaleAcre.Dispose();
|
||||
MapReticle.Dispose();
|
||||
BackgroundAcre.Dispose();
|
||||
BackgroundMap.Dispose();
|
||||
MapItemsReticleImage.Dispose();
|
||||
MapTerrainImage.Dispose();
|
||||
|
||||
ViewportItemsImage.Dispose();
|
||||
ViewportTerrainImage.Dispose();
|
||||
}
|
||||
|
||||
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);
|
||||
/// <summary>
|
||||
/// Updates the map items reticle bitmap for the current layer and view position.
|
||||
/// </summary>
|
||||
/// <param name="transparency">Transparency of items (not including reticle).</param>
|
||||
/// <param name="drawReticle">Option to draw the reticle.</param>
|
||||
/// <returns>Updated bitmap with reticle.</returns>
|
||||
public Bitmap UpdateMapItemsReticle(int transparency, bool drawReticle = true)
|
||||
=> UpdateMapItemsReticle(Map.Mutator.CurrentLayer, Map.Mutator.View.X, Map.Mutator.View.Y, transparency, drawReticle);
|
||||
|
||||
public Bitmap GetBackgroundTerrain(int index = -1)
|
||||
/// <summary>
|
||||
/// Updates the map terrain bitmap.
|
||||
/// </summary>
|
||||
/// <param name="selectedBuildingIndex">Index of building to highlight, or -1 for none.</param>
|
||||
/// <returns>Updated map terrain bitmap.</returns>
|
||||
public Bitmap UpdateMapTerrain(int selectedBuildingIndex = -1)
|
||||
=> TerrainSprite.GetMapWithBuildings(MapTerrainImage, Map, null, MapTerrain1, MapTerrainX, selectedBuildingIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the viewport items bitmap for the current layer and view position.
|
||||
/// </summary>
|
||||
/// <param name="transparency">Transparency of items.</param>
|
||||
public Bitmap UpdateViewportItems(int transparency)
|
||||
=> UpdateViewportItems(Map.Mutator.View.X, Map.Mutator.View.Y, transparency);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the viewport terrain bitmap.
|
||||
/// </summary>
|
||||
/// <param name="f">Font to use for building labels.</param>
|
||||
/// <param name="transparencyBuilding">Transparency for building shapes drawn on terrain.</param>
|
||||
/// <param name="transparencyTerrain">Transparency for terrain layer.</param>
|
||||
/// <param name="selectedBuildingIndex">Index of building to highlight, or -1 for none.</param>
|
||||
/// <returns></returns>
|
||||
public Bitmap UpdateViewportTerrain(Font f, byte transparencyBuilding, byte transparencyTerrain, int selectedBuildingIndex = -1)
|
||||
{
|
||||
return TerrainSprite.GetMapWithBuildings(Map, null, PixelsBackgroundMap1, PixelsBackgroundMapX, BackgroundMap, index);
|
||||
TerrainSprite.LoadViewport(ViewportTerrainImage, Map, f, ViewportTerrain1, ViewportTerrainX, selectedBuildingIndex, transparencyBuilding, transparencyTerrain);
|
||||
return ViewportTerrainImage;
|
||||
}
|
||||
|
||||
public Bitmap GetInflatedImage(Bitmap regular)
|
||||
private Bitmap UpdateMapItemsReticle(LayerFieldItem layer, int absX, int absY, int transparency, bool drawReticle = true)
|
||||
{
|
||||
// Insert 1 acre on each side
|
||||
int columnWidth = (regular.Width / FieldItemWidthOld);
|
||||
var newWidth = columnWidth * FieldItemWidthNew;
|
||||
var bmp = new Bitmap(newWidth, regular.Height);
|
||||
using var g = Graphics.FromImage(bmp);
|
||||
|
||||
// Fill with blue
|
||||
// g.Clear(Color.FromArgb(100, 149, 237));
|
||||
|
||||
// Draw regular centered to new bitmap
|
||||
g.DrawImage(regular, columnWidth, 0, regular.Width, regular.Height);
|
||||
|
||||
return bmp;
|
||||
var cfg = Map.Mutator.Manager.ConfigItems;
|
||||
ItemLayerSprite.LoadItemLayerDrawReticle(cfg, layer, MapItemsReticleX, transparency);
|
||||
MapItemsReticleImage.SetBitmapData(MapItemsReticleX);
|
||||
if (drawReticle)
|
||||
ItemLayerSprite.DrawViewReticle(MapItemsReticleImage, layer.TileInfo, absX, absY);
|
||||
return MapItemsReticleImage;
|
||||
}
|
||||
|
||||
private Bitmap GetLayerAcre(int topX, int topY, int transparency)
|
||||
private Bitmap UpdateViewportItems(int absX, int absY, int transparency)
|
||||
{
|
||||
var cfg = Map.Mutator.Manager.ConfigItems;
|
||||
var layer = Map.Mutator.CurrentLayer;
|
||||
ItemLayerSprite.LoadItemLayerViewGrid(ScaleAcre, layer, topX, topY, PixelsItemAcre1, PixelsItemAcreX, ViewScale, transparency);
|
||||
return ScaleAcre;
|
||||
}
|
||||
|
||||
public Bitmap GetBackgroundAcre(Font f, byte transparencyBuilding, byte transparencyTerrain, int index = -1)
|
||||
{
|
||||
TerrainSprite.LoadViewport(BackgroundAcre, Map, f, PixelsBackgroundAcre1, PixelsBackgroundAcreX, index, transparencyBuilding, transparencyTerrain);
|
||||
return BackgroundAcre;
|
||||
}
|
||||
|
||||
private Bitmap GetMapWithReticle(int topX, int topY, int t, LayerFieldItem layerField)
|
||||
{
|
||||
return ItemLayerSprite.GetBitmapItemLayer(MapReticle, layerField, topX, topY, PixelsItemMap, t);
|
||||
ItemLayerSprite.LoadViewport(ViewportItemsImage, layer, cfg, absX, absY, ViewportItems1, ViewportItemsX, ViewScale, transparency);
|
||||
return ViewportItemsImage;
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,9 @@ public static void GenerateMap(Bitmap map, MapMutator mut, Span<int> scale1, Spa
|
||||
DrawReticle(map, mgr.TileInfo, x, y, imgScale);
|
||||
}
|
||||
|
||||
public static Bitmap GetMapWithBuildings(MapEditor m, Font? f, Span<int> scale1, Span<int> scaleX, Bitmap map, int buildingIndex = -1)
|
||||
public static Bitmap GetMapWithBuildings(Bitmap map, MapEditor m, Font? f,
|
||||
Span<int> scale1, Span<int> scaleX,
|
||||
int buildingIndex = -1)
|
||||
{
|
||||
GenerateMap(map, m.Mutator, scale1, scaleX, m.MapScale);
|
||||
using var gfx = Graphics.FromImage(map);
|
||||
@@ -52,7 +54,9 @@ public static Bitmap GetMapWithBuildings(MapEditor m, Font? f, Span<int> scale1,
|
||||
return map;
|
||||
}
|
||||
|
||||
public static void LoadViewport(Bitmap img, MapEditor m, Font f, Span<int> scale1, Span<int> scaleX, int index, byte transBuild, byte transTerrain)
|
||||
public static void LoadViewport(Bitmap img, MapEditor m, Font f,
|
||||
Span<int> scale1, Span<int> scaleX,
|
||||
int selectedBuildingIndex, byte transparencyBuilding, byte transTerrain)
|
||||
{
|
||||
// Convert from absolute to relative.
|
||||
int mx = m.X / 2;
|
||||
@@ -62,8 +66,8 @@ public static void LoadViewport(Bitmap img, MapEditor m, Font f, Span<int> scale
|
||||
// Drawing building tiles currently uses the graphics API rather than writing pixels.
|
||||
img.SetBitmapData(scaleX);
|
||||
using var gfx = Graphics.FromImage(img);
|
||||
gfx.DrawViewPlaza(m, transBuild);
|
||||
gfx.DrawViewBuildings(m, index, transBuild);
|
||||
gfx.DrawViewPlaza(m, transparencyBuilding);
|
||||
gfx.DrawViewBuildings(m, selectedBuildingIndex, transparencyBuilding);
|
||||
|
||||
// Return to pixel writing mode
|
||||
img.GetBitmapData(scaleX);
|
||||
|
||||
@@ -1395,6 +1395,7 @@ private void InitializeComponent()
|
||||
CB_Acre.Name = "CB_Acre";
|
||||
CB_Acre.Size = new System.Drawing.Size(56, 25);
|
||||
CB_Acre.TabIndex = 100;
|
||||
CB_Acre.SelectedIndexChanged += ChangeAcre;
|
||||
//
|
||||
// FieldItemEditor
|
||||
//
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
@@ -20,16 +21,19 @@ public sealed partial class FieldItemEditor : Form, IItemLayerEditor
|
||||
private MapViewState View => Editor.Mutator.View;
|
||||
private MapTileManager Map => Editor.Mutator.Manager;
|
||||
|
||||
private readonly bool IsExtendedMap30;
|
||||
|
||||
private bool Loading;
|
||||
private int SelectedBuildingIndex;
|
||||
|
||||
/// <summary> Cached current hover X coordinate within the acre. </summary>
|
||||
private int HoverX;
|
||||
/// <summary> Cached current hover Y coordinate within the acre. </summary>
|
||||
private int HoverY;
|
||||
|
||||
private int DragX = -1;
|
||||
private int DragY = -1;
|
||||
private bool Dragging;
|
||||
private bool IsDragOperationActive;
|
||||
|
||||
private bool IsMenuHasActivate = true;
|
||||
|
||||
public ItemEditor ItemProvider => ItemEdit;
|
||||
|
||||
@@ -50,7 +54,6 @@ public FieldItemEditor(MainSave sav)
|
||||
// Read the expected scale from the control.
|
||||
var scale = (PB_Acre.Width - 2) / LayerFieldItem.TilesPerAcreDim; // 1px border
|
||||
SAV = sav;
|
||||
IsExtendedMap30 = sav.FieldItemAcreWidth != 7;
|
||||
Editor = MapEditor.FromSaveFile(sav);
|
||||
Editor.MapScale = scale;
|
||||
Editor.ViewScale = scale;
|
||||
@@ -62,9 +65,12 @@ public FieldItemEditor(MainSave sav)
|
||||
LoadBuildings(sav);
|
||||
ReloadMapBackground();
|
||||
LoadEditors();
|
||||
|
||||
// Set initial states
|
||||
LB_Items.SelectedIndex = 0;
|
||||
CB_Acre.SelectedIndex = 0;
|
||||
CB_MapAcre.SelectedIndex = 0;
|
||||
|
||||
Loading = false;
|
||||
LoadItemGridAcre();
|
||||
}
|
||||
@@ -130,16 +136,14 @@ private void LoadItemGridAcre()
|
||||
|
||||
private void ReloadMapBackground()
|
||||
{
|
||||
var img = Renderer.GetBackgroundTerrain(SelectedBuildingIndex);
|
||||
var img = Renderer.UpdateMapTerrain(SelectedBuildingIndex);
|
||||
SetMapBackgroundImage(img);
|
||||
}
|
||||
|
||||
private void ReloadMapItemGrid() => SetMapForegroundImage(Renderer.GetMapWithReticle(GetItemTransparency()));
|
||||
private void ReloadMapItemGrid() => SetMapForegroundImage(Renderer.UpdateMapItemsReticle(GetItemTransparency()));
|
||||
|
||||
private void SetMapBackgroundImage(Bitmap img)
|
||||
{
|
||||
if (IsExtendedMap30)
|
||||
img = Renderer.GetInflatedImage(img);
|
||||
PB_Map.BackgroundImage = img;
|
||||
PB_Map.Invalidate(); // background image reassigning to same img doesn't redraw; force it
|
||||
}
|
||||
@@ -153,12 +157,12 @@ private void ReloadAcreBackground()
|
||||
{
|
||||
var tbuild = (byte)TR_BuildingTransparency.Value;
|
||||
var tterrain = (byte)TR_Terrain.Value;
|
||||
var img = Renderer.GetBackgroundAcre(L_Coordinates.Font, tbuild, tterrain, SelectedBuildingIndex);
|
||||
var img = Renderer.UpdateViewportTerrain(L_Coordinates.Font, tbuild, tterrain, SelectedBuildingIndex);
|
||||
PB_Acre.BackgroundImage = img;
|
||||
PB_Acre.Invalidate(); // background image reassigning to same img doesn't redraw; force it
|
||||
}
|
||||
|
||||
private void ReloadAcreItemGrid() => PB_Acre.Image = Renderer.GetLayerAcre(GetItemTransparency());
|
||||
private void ReloadAcreItemGrid() => PB_Acre.Image = Renderer.UpdateViewportItems(GetItemTransparency());
|
||||
|
||||
public void ReloadItems()
|
||||
{
|
||||
@@ -182,7 +186,7 @@ private void UpdateArrowVisibility()
|
||||
|
||||
private void PB_Acre_MouseClick(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (Dragging)
|
||||
if (IsDragOperationActive)
|
||||
{
|
||||
ResetDrag();
|
||||
return;
|
||||
@@ -198,21 +202,25 @@ private void ResetDrag()
|
||||
{
|
||||
DragX = -1;
|
||||
DragY = -1;
|
||||
Dragging = false;
|
||||
IsDragOperationActive = false;
|
||||
}
|
||||
|
||||
private void OmniTile(MouseEventArgs e)
|
||||
{
|
||||
var tile = GetTile(CurrentLayer, e, out var x, out var y);
|
||||
OmniTile(tile, x, y);
|
||||
if (!GetTile(e, CurrentLayer, out var tile))
|
||||
return;
|
||||
OmniTile(tile.Tile, tile.RelativeX, tile.RelativeY);
|
||||
}
|
||||
|
||||
private void OmniTileTerrain(MouseEventArgs e)
|
||||
{
|
||||
SetHoveredItem(e);
|
||||
var x = View.X + HoverX;
|
||||
var y = View.Y + HoverY;
|
||||
TerrainTile tile = Editor.Terrain.GetTile(x / 2, y / 2);
|
||||
if (!GetTile(e, Editor.Mutator.Manager.LayerTerrain, out var meta))
|
||||
return;
|
||||
|
||||
var tile = meta.Tile;
|
||||
var relX = meta.RelativeX;
|
||||
var relY = meta.RelativeY;
|
||||
|
||||
if (tbeForm?.IsBrushSelected != true)
|
||||
{
|
||||
OmniTileTerrain(tile);
|
||||
@@ -233,32 +241,32 @@ private void OmniTileTerrain(MouseEventArgs e)
|
||||
for (int j = -radius; j < radius; j++)
|
||||
{
|
||||
if ((i * i) + (j * j) < threshold)
|
||||
selectedTiles.Add(Editor.Terrain.GetTile((x / 2) + i, (y / 2) + j));
|
||||
selectedTiles.Add(Editor.Terrain.GetTile(relX + i, relY + j));
|
||||
}
|
||||
}
|
||||
|
||||
SetTiles(selectedTiles);
|
||||
}
|
||||
|
||||
private void OmniTile(Item tile, int x, int y)
|
||||
private void OmniTile(Item tile, int relX, int relY)
|
||||
{
|
||||
switch (ModifierKeys)
|
||||
{
|
||||
default:
|
||||
ViewTile(tile, x, y);
|
||||
ViewTile(tile, relX, relY);
|
||||
return;
|
||||
|
||||
case Keys.Alt | Keys.Control:
|
||||
case Keys.Alt | Keys.Control | Keys.Shift:
|
||||
ReplaceTile(tile, x, y);
|
||||
ReplaceTile(tile, relX, relY);
|
||||
return;
|
||||
|
||||
case Keys.Shift:
|
||||
SetTile(tile, x, y);
|
||||
SetTile(tile, relX, relY);
|
||||
return;
|
||||
|
||||
case Keys.Alt:
|
||||
DeleteTile(tile, x, y);
|
||||
DeleteTile(tile, relX, relY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -285,27 +293,83 @@ private void OmniTileTerrain(TerrainTile tile)
|
||||
}
|
||||
}
|
||||
|
||||
private Item GetTile(LayerFieldItem layerField, MouseEventArgs e, out int x, out int y)
|
||||
private sealed record TileCheck<T>(T Tile, int AbsoluteX, int AbsoluteY, int RelativeX, int RelativeY);
|
||||
|
||||
private bool GetTile(MouseEventArgs e, LayerFieldItem layerField, [NotNullWhen(true)] out TileCheck<Item>? item)
|
||||
{
|
||||
SetHoveredItem(e);
|
||||
return layerField.GetTile(x = View.X + HoverX, y = View.Y + HoverY);
|
||||
UpdateHoveredCoordinates(e);
|
||||
var (absX, absY) = GetAbsoluteCoordinatesHover();
|
||||
return GetTile(layerField, absX, absY, out item);
|
||||
}
|
||||
|
||||
private void SetHoveredItem(MouseEventArgs e)
|
||||
private (int X, int Y) GetAbsoluteCoordinatesHover()
|
||||
{
|
||||
GetAcreCoordinates(e, out HoverX, out HoverY);
|
||||
var absX = View.X + HoverX;
|
||||
var absY = View.Y + HoverY;
|
||||
return (absX, absY);
|
||||
}
|
||||
|
||||
private (int X, int Y) GetAbsoluteCoordinatesHoverTerrain()
|
||||
{
|
||||
// Terrain tiles are 16x16, but the view caters to 32x32 for field items.
|
||||
var absX = (View.X + HoverX) / 2;
|
||||
var absY = (View.Y + HoverY) / 2;
|
||||
return (absX, absY);
|
||||
}
|
||||
|
||||
private (int X, int Y) GetViewCoordinates(MouseEventArgs e)
|
||||
{
|
||||
var x = e.X / Editor.ViewScale;
|
||||
var y = e.Y / Editor.ViewScale;
|
||||
return (x, y);
|
||||
}
|
||||
|
||||
private bool GetTile(LayerFieldItem layerField, int absX, int absY, [NotNullWhen(true)] out TileCheck<Item>? item)
|
||||
{
|
||||
var cfg = Editor.Mutator.Manager.ConfigItems;
|
||||
if (!cfg.IsCoordinateValidAbsolute(absX, absY))
|
||||
{
|
||||
item = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var rel = cfg.GetCoordinatesRelative(absX, absY);
|
||||
var tile = layerField.GetTile(rel.X, rel.Y);
|
||||
item = new TileCheck<Item>(tile, absX, absY, rel.X, rel.Y);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool GetTile(MouseEventArgs e, LayerTerrain layerField, [NotNullWhen(true)] out TileCheck<TerrainTile>? item)
|
||||
{
|
||||
UpdateHoveredCoordinates(e);
|
||||
var (absX, absY) = GetAbsoluteCoordinatesHoverTerrain();
|
||||
return GetTile(layerField, absX, absY, out item);
|
||||
}
|
||||
|
||||
private bool GetTile(LayerTerrain layerField, int absX, int absY, [NotNullWhen(true)] out TileCheck<TerrainTile>? item)
|
||||
{
|
||||
var cfg = Editor.Mutator.Manager.ConfigItems;
|
||||
if (!cfg.IsCoordinateValidAbsolute(absX, absY))
|
||||
{
|
||||
item = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var rel = cfg.GetCoordinatesRelative(absX, absY);
|
||||
var tile = layerField.GetTile(rel.X, rel.Y);
|
||||
item = new TileCheck<TerrainTile>(tile, absX, absY, rel.X, rel.Y);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UpdateHoveredCoordinates(MouseEventArgs e)
|
||||
{
|
||||
(HoverX, HoverY) = GetViewCoordinates(e);
|
||||
|
||||
// Mouse event may fire with a slightly too large x/y; clamp just in case.
|
||||
HoverX &= 0x1F;
|
||||
HoverY &= 0x1F;
|
||||
}
|
||||
|
||||
private void GetAcreCoordinates(MouseEventArgs e, out int x, out int y)
|
||||
{
|
||||
x = e.X / Editor.ViewScale;
|
||||
y = e.Y / Editor.ViewScale;
|
||||
}
|
||||
|
||||
private void PB_Acre_MouseDown(object sender, MouseEventArgs e) => ResetDrag();
|
||||
|
||||
private void PB_Acre_MouseMove(object sender, MouseEventArgs e)
|
||||
@@ -321,10 +385,24 @@ private void PB_Acre_MouseMove(object sender, MouseEventArgs e)
|
||||
OmniTileTerrain(e);
|
||||
}
|
||||
|
||||
var oldTile = l.GetTile(View.X + HoverX, View.Y + HoverY);
|
||||
var tile = GetTile(l, e, out var x, out var y);
|
||||
if (ReferenceEquals(tile, oldTile))
|
||||
// Update hover tooltip if it is a different tile
|
||||
// Can't compare coordinates if redirection of extension tiles hijacks to return the root tile.
|
||||
// Just check the (root) tile returns for each.
|
||||
// Two different extension tiles can redirect to the same root tile, can skip updating in that case.
|
||||
if (!GetTile(l, View.X + HoverX, View.Y + HoverY, out var old))
|
||||
return;
|
||||
|
||||
var oldTile = old.Tile;
|
||||
if (!GetTile(e, l, out var dest))
|
||||
return;
|
||||
|
||||
var tile = dest.Tile;
|
||||
var x = dest.RelativeX;
|
||||
var y = dest.RelativeY;
|
||||
if (ReferenceEquals(tile, oldTile))
|
||||
return; // same tile, no change
|
||||
|
||||
// Regenerate tooltip text
|
||||
var str = GameInfo.Strings;
|
||||
var name = str.GetItemName(tile);
|
||||
var flagLayer = NUD_Layer.Value == 0 ? Map.LayerItemFlag0 : Map.LayerItemFlag1;
|
||||
@@ -337,19 +415,19 @@ private void PB_Acre_MouseMove(object sender, MouseEventArgs e)
|
||||
|
||||
private void MoveDrag(MouseEventArgs e)
|
||||
{
|
||||
GetAcreCoordinates(e, out var nhX, out var nhY);
|
||||
var (viewX, viewY) = GetViewCoordinates(e);
|
||||
|
||||
if (DragX == -1)
|
||||
{
|
||||
DragX = nhX;
|
||||
DragY = nhY;
|
||||
DragX = viewX;
|
||||
DragY = viewY;
|
||||
return;
|
||||
}
|
||||
|
||||
var dX = DragX - nhX;
|
||||
var dY = DragY - nhY;
|
||||
var dX = DragX - viewX;
|
||||
var dY = DragY - viewY;
|
||||
|
||||
if (ModifierKeys == Keys.Control)
|
||||
if (ModifierKeys == Keys.Control) // move in larger steps
|
||||
{
|
||||
dX *= 2;
|
||||
dY *= 2;
|
||||
@@ -360,30 +438,33 @@ private void MoveDrag(MouseEventArgs e)
|
||||
if ((dY & 1) == 1)
|
||||
dY ^= 1;
|
||||
|
||||
// Ensure movement is significant enough
|
||||
var aX = Math.Abs(dX);
|
||||
var aY = Math.Abs(dY);
|
||||
if (aX < 2 && aY < 2)
|
||||
return;
|
||||
|
||||
DragX = nhX;
|
||||
DragY = nhY;
|
||||
if (!View.SetViewTo(View.X + dX, View.Y + dY))
|
||||
DragX = viewX;
|
||||
DragY = viewY;
|
||||
if (!View.DragView(dX, dY))
|
||||
return;
|
||||
|
||||
Dragging = true;
|
||||
IsDragOperationActive = true;
|
||||
LoadItemGridAcre();
|
||||
}
|
||||
|
||||
private void ViewTile(Item tile, int x, int y)
|
||||
private void ViewTile(Item tile, int relX, int relY)
|
||||
{
|
||||
if (CHK_RedirectExtensionLoad.Checked && tile.IsExtension)
|
||||
{
|
||||
var l = 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);
|
||||
if (redir.IsRoot && redir.ItemId == tile.ExtensionItemId)
|
||||
tile = redir;
|
||||
relX -= tile.ExtensionX;
|
||||
relY -= tile.ExtensionY;
|
||||
l.TileInfo.ClampInside(ref relX, ref relY);
|
||||
var redirectTile = l.GetTile(relX, relY);
|
||||
|
||||
if (redirectTile.IsRoot && redirectTile.ItemId == tile.ExtensionItemId)
|
||||
tile = redirectTile;
|
||||
}
|
||||
|
||||
ViewTile(tile);
|
||||
@@ -403,7 +484,7 @@ private void ViewTile(TerrainTile tile)
|
||||
TC_Editor.SelectedTab = Tab_Terrain;
|
||||
}
|
||||
|
||||
private void SetTile(Item tile, int x, int y)
|
||||
private void SetTile(Item tile, int relX, int relY)
|
||||
{
|
||||
var l = CurrentLayer;
|
||||
var pgt = new Item();
|
||||
@@ -412,12 +493,12 @@ private void SetTile(Item tile, int x, int y)
|
||||
if (pgt.IsFieldItem && CHK_FieldItemSnap.Checked)
|
||||
{
|
||||
// coordinates must be even (not odd-half)
|
||||
x &= 0xFFFE;
|
||||
y &= 0xFFFE;
|
||||
tile = l.GetTile(x, y);
|
||||
relX &= 0xFFFE;
|
||||
relY &= 0xFFFE;
|
||||
tile = l.GetTile(relX, relY);
|
||||
}
|
||||
|
||||
var permission = l.IsOccupied(pgt, x, y);
|
||||
var permission = l.IsOccupied(pgt, relX, relY);
|
||||
switch (permission)
|
||||
{
|
||||
case PlacedItemPermission.OutOfBounds:
|
||||
@@ -428,17 +509,17 @@ private void SetTile(Item tile, int x, int y)
|
||||
|
||||
// Clean up original placed data
|
||||
if (tile.IsRoot && CHK_AutoExtension.Checked)
|
||||
l.DeleteExtensionTiles(tile, x, y);
|
||||
l.DeleteExtensionTiles(tile, relX, relY);
|
||||
|
||||
// Set new placed data
|
||||
if (pgt.IsRoot && CHK_AutoExtension.Checked)
|
||||
l.SetExtensionTiles(pgt, x, y);
|
||||
l.SetExtensionTiles(pgt, relX, relY);
|
||||
tile.CopyFrom(pgt);
|
||||
|
||||
ReloadItems();
|
||||
}
|
||||
|
||||
private void ReplaceTile(Item tile, int x, int y)
|
||||
private void ReplaceTile(Item tile, int relX, int relY)
|
||||
{
|
||||
var l = CurrentLayer;
|
||||
var pgt = new Item();
|
||||
@@ -447,12 +528,12 @@ private void ReplaceTile(Item tile, int x, int y)
|
||||
if (pgt.IsFieldItem && CHK_FieldItemSnap.Checked)
|
||||
{
|
||||
// coordinates must be even (not odd-half)
|
||||
x &= 0xFFFE;
|
||||
y &= 0xFFFE;
|
||||
tile = l.GetTile(x, y);
|
||||
relX &= 0xFFFE;
|
||||
relY &= 0xFFFE;
|
||||
tile = l.GetTile(relX, relY);
|
||||
}
|
||||
|
||||
var permission = l.IsOccupied(pgt, x, y);
|
||||
var permission = l.IsOccupied(pgt, relX, relY);
|
||||
switch (permission)
|
||||
{
|
||||
case PlacedItemPermission.OutOfBounds:
|
||||
@@ -474,8 +555,8 @@ private void ReplaceTile(Item tile, int x, int y)
|
||||
|
||||
private void RotateTile(TerrainTile tile)
|
||||
{
|
||||
bool rotated = tile.Rotate();
|
||||
if (!rotated)
|
||||
bool wasRotated = tile.TryRotate();
|
||||
if (!wasRotated)
|
||||
{
|
||||
System.Media.SystemSounds.Asterisk.Play();
|
||||
return;
|
||||
@@ -486,6 +567,8 @@ private void RotateTile(TerrainTile tile)
|
||||
private void SetTile(TerrainTile tile)
|
||||
{
|
||||
var pgt = (TerrainTile)PG_TerrainTile.SelectedObject!;
|
||||
|
||||
// Apply randomization if enabled
|
||||
if (tbeForm?.RandomizeVariation == true)
|
||||
{
|
||||
switch (pgt.UnitModel)
|
||||
@@ -499,33 +582,30 @@ private void SetTile(TerrainTile tile)
|
||||
}
|
||||
|
||||
tile.CopyFrom(pgt);
|
||||
|
||||
ReloadBuildingsTerrain();
|
||||
}
|
||||
|
||||
private void SetTiles(IEnumerable<TerrainTile> tiles)
|
||||
{
|
||||
var pgt = (TerrainTile)PG_TerrainTile.SelectedObject!;
|
||||
foreach (TerrainTile tile in tiles)
|
||||
{
|
||||
tile.CopyFrom(pgt);
|
||||
}
|
||||
|
||||
foreach (var tile in tiles)
|
||||
tile.CopyFrom(pgt);
|
||||
ReloadBuildingsTerrain();
|
||||
}
|
||||
|
||||
private void DeleteTile(Item tile, int x, int y)
|
||||
private void DeleteTile(Item tile, int relX, int relY)
|
||||
{
|
||||
if (CHK_AutoExtension.Checked)
|
||||
{
|
||||
var layer = CurrentLayer;
|
||||
if (!tile.IsRoot)
|
||||
{
|
||||
x -= tile.ExtensionX;
|
||||
y -= tile.ExtensionY;
|
||||
tile = layer.GetTile(x, y);
|
||||
relX -= tile.ExtensionX;
|
||||
relY -= tile.ExtensionY;
|
||||
tile = layer.GetTile(relX, relY);
|
||||
}
|
||||
layer.DeleteExtensionTiles(tile, x, y);
|
||||
layer.DeleteExtensionTiles(tile, relX, relY);
|
||||
}
|
||||
|
||||
tile.Delete();
|
||||
@@ -561,83 +641,125 @@ private void B_Save_Click(object sender, EventArgs e)
|
||||
|
||||
private void Menu_View_Click(object sender, EventArgs e)
|
||||
{
|
||||
var x = View.X + HoverX;
|
||||
var y = View.Y + HoverY;
|
||||
var (absX, absY) = GetAbsoluteCoordinatesHover();
|
||||
var cfg = Editor.Mutator.Manager.ConfigItems;
|
||||
if (!cfg.IsCoordinateValidAbsolute(absX, absY))
|
||||
{
|
||||
System.Media.SystemSounds.Asterisk.Play();
|
||||
return;
|
||||
}
|
||||
|
||||
var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY);
|
||||
if (RB_Item.Checked)
|
||||
{
|
||||
var tile = CurrentLayer.GetTile(x, y);
|
||||
ViewTile(tile, x, y);
|
||||
var tile = CurrentLayer.GetTile(relX, relY);
|
||||
ViewTile(tile, relX, relY);
|
||||
}
|
||||
else if (RB_Terrain.Checked)
|
||||
{
|
||||
TerrainTile tile = Editor.Terrain.GetTile(x / 2, y / 2);
|
||||
var tile = Editor.Terrain.GetTile(relX, relY);
|
||||
ViewTile(tile);
|
||||
}
|
||||
}
|
||||
|
||||
private void Menu_Set_Click(object sender, EventArgs e)
|
||||
{
|
||||
var x = View.X + HoverX;
|
||||
var y = View.Y + HoverY;
|
||||
|
||||
if (RB_Item.Checked)
|
||||
{
|
||||
var tile = CurrentLayer.GetTile(x, y);
|
||||
SetTile(tile, x, y);
|
||||
var (absX, absY) = GetAbsoluteCoordinatesHover();
|
||||
var cfg = Editor.Mutator.Manager.ConfigItems;
|
||||
if (!cfg.IsCoordinateValidAbsolute(absX, absY))
|
||||
{
|
||||
System.Media.SystemSounds.Asterisk.Play();
|
||||
return;
|
||||
}
|
||||
var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY);
|
||||
|
||||
var tile = CurrentLayer.GetTile(relX, relY);
|
||||
SetTile(tile, relX, relY);
|
||||
}
|
||||
else if (RB_Terrain.Checked)
|
||||
{
|
||||
var tile = Editor.Terrain.GetTile(x / 2, y / 2);
|
||||
var (absX, absY) = GetAbsoluteCoordinatesHoverTerrain();
|
||||
var cfg = Editor.Mutator.Manager.ConfigTerrain;
|
||||
if (!cfg.IsCoordinateValidAbsolute(absX, absY))
|
||||
{
|
||||
System.Media.SystemSounds.Asterisk.Play();
|
||||
return;
|
||||
}
|
||||
|
||||
var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY);
|
||||
var tile = Editor.Terrain.GetTile(relX, relY);
|
||||
SetTile(tile);
|
||||
}
|
||||
}
|
||||
|
||||
private void Menu_Reset_Click(object sender, EventArgs e)
|
||||
{
|
||||
var x = View.X + HoverX;
|
||||
var y = View.Y + HoverY;
|
||||
|
||||
if (RB_Item.Checked)
|
||||
{
|
||||
var tile = CurrentLayer.GetTile(x, y);
|
||||
DeleteTile(tile, x, y);
|
||||
var (absX, absY) = GetAbsoluteCoordinatesHover();
|
||||
var cfg = Editor.Mutator.Manager.ConfigItems;
|
||||
if (!cfg.IsCoordinateValidAbsolute(absX, absY))
|
||||
{
|
||||
System.Media.SystemSounds.Asterisk.Play();
|
||||
return;
|
||||
}
|
||||
var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY);
|
||||
|
||||
var tile = CurrentLayer.GetTile(relX, relY);
|
||||
DeleteTile(tile, relX, relY);
|
||||
}
|
||||
else if (RB_Terrain.Checked)
|
||||
{
|
||||
var tile = Editor.Terrain.GetTile(x / 2, y / 2);
|
||||
var (absX, absY) = GetAbsoluteCoordinatesHoverTerrain();
|
||||
var cfg = Editor.Mutator.Manager.ConfigTerrain;
|
||||
if (!cfg.IsCoordinateValidAbsolute(absX, absY))
|
||||
{
|
||||
System.Media.SystemSounds.Asterisk.Play();
|
||||
return;
|
||||
}
|
||||
|
||||
var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY);
|
||||
var tile = Editor.Terrain.GetTile(relX, relY);
|
||||
DeleteTile(tile);
|
||||
}
|
||||
}
|
||||
|
||||
private bool hasActivate = true;
|
||||
|
||||
private void CM_Click_Opening(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
if (!RB_Item.Checked)
|
||||
if (!RB_Item.Checked) // not in Item edit mode, therefore no "Activate Flag" menu
|
||||
{
|
||||
if (hasActivate)
|
||||
if (IsMenuHasActivate)
|
||||
CM_Click.Items.Remove(Menu_Activate);
|
||||
hasActivate = false;
|
||||
IsMenuHasActivate = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var x = View.X + HoverX;
|
||||
var y = View.Y + HoverY;
|
||||
var (absX, absY) = GetAbsoluteCoordinatesHover();
|
||||
var cfg = Editor.Mutator.Manager.ConfigItems;
|
||||
if (!cfg.IsCoordinateValidAbsolute(absX, absY))
|
||||
return;
|
||||
|
||||
var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY);
|
||||
var flagLayer = NUD_Layer.Value == 0 ? Map.LayerItemFlag0 : Map.LayerItemFlag1;
|
||||
var isActive = flagLayer.GetIsActive(x, y);
|
||||
var isActive = flagLayer.GetIsActive(relX, relY);
|
||||
Menu_Activate.Text = isActive ? "Inactivate" : "Activate";
|
||||
CM_Click.Items.Add(Menu_Activate);
|
||||
hasActivate = true;
|
||||
IsMenuHasActivate = true;
|
||||
}
|
||||
|
||||
private void Menu_Activate_Click(object sender, EventArgs e)
|
||||
{
|
||||
var x = View.X + HoverX;
|
||||
var y = View.Y + HoverY;
|
||||
var (absX, absY) = GetAbsoluteCoordinatesHover();
|
||||
var cfg = Editor.Mutator.Manager.ConfigItems;
|
||||
if (!cfg.IsCoordinateValidAbsolute(absX, absY))
|
||||
return;
|
||||
|
||||
var (relX, relY) = cfg.GetCoordinatesRelative(absX, absY);
|
||||
var flagLayer = NUD_Layer.Value == 0 ? Map.LayerItemFlag0 : Map.LayerItemFlag1;
|
||||
var isActive = flagLayer.GetIsActive(x, y);
|
||||
flagLayer.SetIsActive(x, y, !isActive);
|
||||
var isActive = flagLayer.GetIsActive(relX, relY);
|
||||
flagLayer.SetIsActive(relX, relY, !isActive);
|
||||
}
|
||||
|
||||
private void B_Up_Click(object sender, EventArgs e)
|
||||
@@ -776,13 +898,13 @@ private void PB_Map_MouseDown(object sender, MouseEventArgs e)
|
||||
|
||||
private void ClickMapAt(MouseEventArgs e)
|
||||
{
|
||||
var (x, y) = Editor.GetMapCoordinates(e.X, e.Y, CHK_SnapToAcre.Checked ? MapViewCoordinateRequest.SnapAcre : MapViewCoordinateRequest.Centered);
|
||||
var (absX, absY) = Editor.GetMapCoordinates(e.X, e.Y, CHK_SnapToAcre.Checked ? MapViewCoordinateRequest.SnapAcre : MapViewCoordinateRequest.Centered);
|
||||
|
||||
// Truncate to root-node coordinates. The map is only 1px per tile, and nobody is wanting to click on extension-tiles.
|
||||
x &= 0xFFFE;
|
||||
y &= 0xFFFE;
|
||||
absX &= 0xFFFE;
|
||||
absY &= 0xFFFE;
|
||||
|
||||
if (View.SetViewTo(x, y))
|
||||
if (View.SetViewTo(absX, absY))
|
||||
LoadItemGridAcre();
|
||||
}
|
||||
|
||||
@@ -794,12 +916,12 @@ private void PB_Map_MouseMove(object sender, MouseEventArgs e)
|
||||
}
|
||||
else if (e.Button == MouseButtons.None)
|
||||
{
|
||||
Editor.GetCursorCoordinates(e.X, e.Y, out var x, out var y);
|
||||
SetCoordinateText(x, y);
|
||||
var (absX, absY) = Editor.GetCursorCoordinates(e.X, e.Y);
|
||||
SetCoordinateText(absX, absY);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetCoordinateText(int x, int y) => L_Coordinates.Text = $"({x:000},{y:000}) = (0x{x:X2},0x{y:X2})";
|
||||
private void SetCoordinateText(int absX, int absY) => L_Coordinates.Text = $"({absX:000},{absY:000}) = (0x{absX:X2},0x{absY:X2})";
|
||||
|
||||
private void NUD_Layer_ValueChanged(object sender, EventArgs e)
|
||||
{
|
||||
@@ -809,15 +931,13 @@ private void NUD_Layer_ValueChanged(object sender, EventArgs e)
|
||||
|
||||
private void Remove(ToolStripItem sender, Func<int, int, int, int, int> removal)
|
||||
{
|
||||
bool wholeMap = (ModifierKeys & Keys.Shift) != 0;
|
||||
|
||||
string q = string.Format(MessageStrings.MsgFieldItemRemoveAsk, sender.Text);
|
||||
var question = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, q);
|
||||
var isModifyEntireMap = (ModifierKeys & Keys.Shift) != 0;
|
||||
var message = string.Format(MessageStrings.MsgFieldItemRemoveAsk, sender.Text);
|
||||
var question = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, message);
|
||||
if (question != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
int count = Editor.Mutator.ModifyFieldItems(removal, wholeMap);
|
||||
|
||||
var count = Editor.Mutator.ModifyFieldItems(removal, isModifyEntireMap);
|
||||
if (count == 0)
|
||||
{
|
||||
WinFormsUtil.Alert(MessageStrings.MsgFieldItemRemoveNone);
|
||||
@@ -829,15 +949,13 @@ private void Remove(ToolStripItem sender, Func<int, int, int, int, int> removal)
|
||||
|
||||
private void Modify(ToolStripItem sender, Func<int, int, int, int, int> action)
|
||||
{
|
||||
bool wholeMap = (ModifierKeys & Keys.Shift) != 0;
|
||||
|
||||
string q = string.Format(MessageStrings.MsgFieldItemModifyAsk, sender.Text);
|
||||
var question = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, q);
|
||||
var isModifyEntireMap = (ModifierKeys & Keys.Shift) != 0;
|
||||
var message = string.Format(MessageStrings.MsgFieldItemModifyAsk, sender.Text);
|
||||
var question = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, message);
|
||||
if (question != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
int count = Editor.Mutator.ModifyFieldItems(action, wholeMap);
|
||||
|
||||
var count = Editor.Mutator.ModifyFieldItems(action, isModifyEntireMap);
|
||||
if (count == 0)
|
||||
{
|
||||
WinFormsUtil.Alert(MessageStrings.MsgFieldItemModifyNone);
|
||||
@@ -917,14 +1035,14 @@ private void LB_Items_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (LB_Items.SelectedIndex < 0)
|
||||
return;
|
||||
LoadIndex(LB_Items.SelectedIndex);
|
||||
LoadBuildingIndex(LB_Items.SelectedIndex);
|
||||
|
||||
// View location snap has changed the view. Reload everything
|
||||
LoadItemGridAcre();
|
||||
ReloadMapBackground();
|
||||
}
|
||||
|
||||
private void LoadIndex(int index)
|
||||
private void LoadBuildingIndex(int index)
|
||||
{
|
||||
Loading = true;
|
||||
SelectedBuildingIndex = index;
|
||||
@@ -939,9 +1057,9 @@ private void LoadIndex(int index)
|
||||
NUD_UniqueID.Value = b.UniqueID;
|
||||
Loading = false;
|
||||
|
||||
// -32 for relative offset on map (buildings can be placed on the exterior ocean acres)
|
||||
// Jump the view to see the building
|
||||
// -16 to put it in the center of the view
|
||||
const int shift = 48;
|
||||
const int shift = 16;
|
||||
var x = (b.X - shift) & 0xFFFE;
|
||||
var y = (b.Y - shift) & 0xFFFE;
|
||||
View.SetViewTo(x, y);
|
||||
@@ -996,6 +1114,7 @@ private void CB_MapAcreSelect_SelectedValueChanged(object sender, EventArgs e)
|
||||
var index = CB_MapAcre.SelectedIndex;
|
||||
var value = WinFormsUtil.GetIndex(CB_MapAcreSelect);
|
||||
|
||||
// u16[], but values are at most u8 each.
|
||||
var span = Editor.Terrain.BaseAcres.Span.Slice(index * 2, 2);
|
||||
var oldValue = span[0];
|
||||
if (value == oldValue)
|
||||
|
||||
@@ -169,7 +169,10 @@ private void DrawRoom(LayerItem layer)
|
||||
Span<int> scale1 = stackalloc int[w * h];
|
||||
var scaleX = new int[scale * scale * scale1.Length];
|
||||
var bmp = new Bitmap(scale * w, scale * h);
|
||||
ItemLayerSprite.LoadItemLayerViewGrid(bmp, layer, 0, 0, scale1, scaleX, scale, gridlineColor: 0x7F000000);
|
||||
|
||||
// 10x10 items (2x2 tiles per item)
|
||||
var cfg = LayerPositionConfig.Create(1, 1, 20, 1);
|
||||
ItemLayerSprite.LoadViewport(bmp, layer, cfg, 0, 0, scale1, scaleX, scale, gridlineColor: 0x7F000000);
|
||||
PB_Room.Image = bmp;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user