diff --git a/NHSE.Core/Drawing/TerrainTileColor.cs b/NHSE.Core/Drawing/TerrainTileColor.cs index f9c150f..c4b0fec 100644 --- a/NHSE.Core/Drawing/TerrainTileColor.cs +++ b/NHSE.Core/Drawing/TerrainTileColor.cs @@ -1,17 +1,20 @@ using System; using System.Drawing; +using static NHSE.Core.TerrainUnitModel; +using static NHSE.Core.LandAngles; namespace NHSE.Core { public static class TerrainTileColor { private static readonly Color River = Color.FromArgb(128, 215, 195); + private static readonly Color Grass = Color.ForestGreen; - public static Color GetTileColor(TerrainTile tile) + public static Color GetTileColor(TerrainTile tile, int relativeX, int relativeY) { if (tile.UnitModelRoad.IsRoad()) return GetRoadColor(tile.UnitModelRoad); - var baseColor = GetTileDefaultColor(tile.UnitModel); + var baseColor = GetTileDefaultColor(tile.UnitModel, tile.LandMakingAngle, relativeX, relativeY); if (tile.Elevation == 0) return baseColor; @@ -37,20 +40,195 @@ private static Color GetRoadColor(TerrainUnitModel mdl) return Color.BurlyWood; } - private static readonly Color CliffBase = ColorUtil.Blend(Color.ForestGreen, Color.Black, 0.6d); - - private static Color GetTileDefaultColor(TerrainUnitModel mdl) + /// Notes about rivers the number is how many sides / diagonals are water. + private static Color GetRiverColor(TerrainUnitModel mdl, LandAngles landAngle, int relativeX, int relativeY) { + return mdl switch + { + // River0A single "hole" of water land all sides. Rotation does nothing + River0A when (relativeX < 4 || relativeX >= 12 || relativeY < 4 || relativeY >= 12) => + Grass, + // River1A narrow channel end opening on bottom, land on other sides + River1A => landAngle switch + { + Default when relativeX < 4 || relativeX >= 12 || relativeY < 4 => Grass, + Rotate90ClockAnverse when relativeX < 4 || relativeY < 4 || relativeY >= 12 => Grass, + Rotate180ClockAnverse when relativeX < 4 || relativeX >= 12 || relativeY >= 12 => Grass, + Rotate270ClockAnverse when relativeY < 4 || relativeY >= 12 || relativeX >= 12 => Grass, + _ => River + }, + // River2A narrow water channel opening on top and bottom, land left and right + River2A => landAngle switch + { + Default when relativeX is < 4 or >= 12 => Grass, + Rotate90ClockAnverse when relativeY is >= 12 or < 4 => Grass, + Rotate180ClockAnverse when relativeX is < 4 or >= 12 => Grass, + Rotate270ClockAnverse when relativeY is < 4 or >= 12 => Grass, + _ => River + }, + // River2B narrow 45 channel angled land top left with nub bottom right + River2B => landAngle switch + { + Default when IsPointInMultiTriangle(relativeX, relativeY, new(4, 15), new(0, 0), new(15, 4), new(0, 15), new(15, 0)) || IsNubOnBottomRight(relativeX, relativeY) || relativeX < 4 || relativeY < 4 => Grass, + Rotate90ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(4, 0), new(0, 15), new(15, 12), new(0, 0), new(15, 15)) || IsNubOnTopRight(relativeX, relativeY) || relativeX < 4 || relativeY >= 12 => Grass, + Rotate180ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(0, 12), new(15, 15), new(12, 0), new(0, 15), new(15, 0)) || IsNubOnTopLeft(relativeX, relativeY) || relativeX >= 12 || relativeY >= 12 => Grass, + Rotate270ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(0, 4), new(15, 0), new(12, 15), new(0, 0), new(15, 15)) || IsNubOnBottomLeft(relativeX, relativeY) || relativeX >= 12 || relativeY < 4 => Grass, + _ => River + }, + // River2C narrow 90 channel corner land top left with nub bottom right + River2C => landAngle switch + { + Default when relativeX < 4 || relativeY < 4 || IsNubOnBottomRight(relativeX, relativeY) => Grass, + Rotate90ClockAnverse when relativeX < 4 || relativeY >= 12 || IsNubOnTopRight(relativeX, relativeY) => Grass, + Rotate180ClockAnverse when relativeX >= 12 || relativeY >= 12 || IsNubOnTopLeft(relativeX, relativeY) => Grass, + Rotate270ClockAnverse when relativeX >= 12 || relativeY < 4 || IsNubOnBottomLeft(relativeX, relativeY) => Grass, + _ => River + }, + // River3A narrow 3 way land left side, nub top right and bottom right + River3A => landAngle switch + { + Default when relativeX < 4 || IsNubOnTopRight(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) => Grass, + Rotate90ClockAnverse when relativeY >= 12 || IsNubOnTopLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass, + Rotate180ClockAnverse when relativeX >= 12 || IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnTopLeft(relativeX, relativeY) => Grass, + Rotate270ClockAnverse when relativeY < 4 || IsNubOnBottomRight(relativeX, relativeY) || IsNubOnBottomLeft(relativeX, relativeY) => Grass, + _ => River + }, + // River3B river 45 corner angled land top left, no nub + River3B => landAngle switch + { + Default when IsPointInMultiTriangle(relativeX, relativeY, new(4, 15), new(0, 0), new(15, 4), new(0, 15), new(15, 0)) => Grass, + Rotate90ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(4, 0), new(0, 15), new(15, 12), new(0, 0), new(15, 15)) => Grass, + Rotate180ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(0, 12), new(15, 15), new(12, 0), new(0, 15), new(15, 0)) => Grass, + Rotate270ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(0, 4), new(15, 0), new(12, 15), new(0, 0), new(15, 15)) => Grass, + _ => River + }, + // River3C river 90 corner corner land top left, no nub + River3C => landAngle switch + { + Default when relativeX < 4 || relativeY < 4 => Grass, + Rotate90ClockAnverse when relativeX < 4 || relativeY >= 12 => Grass, + Rotate180ClockAnverse when relativeX >= 12 || relativeY >= 12 => Grass, + Rotate270ClockAnverse when relativeX >= 12 || relativeY < 4 => Grass, + _ => River + }, + // River4A river side with nub top land left side with nub top right only + River4A => landAngle switch + { + Default when relativeX < 4 || IsNubOnTopRight(relativeX, relativeY) => Grass, + Rotate90ClockAnverse when relativeY >= 12 || IsNubOnTopLeft(relativeX, relativeY) => Grass, + Rotate180ClockAnverse when relativeX >= 12 || IsNubOnBottomLeft(relativeX, relativeY) => Grass, + Rotate270ClockAnverse when relativeY < 4 || IsNubOnBottomRight(relativeX, relativeY) => Grass, + _ => River + }, + // River4B river side with nub bottom land left side with nub bottom right only + River4B => landAngle switch + { + Default when relativeX < 4 || IsNubOnBottomRight(relativeX, relativeY) => Grass, + Rotate90ClockAnverse when relativeY >= 12 || IsNubOnTopRight(relativeX, relativeY) => Grass, + Rotate180ClockAnverse when relativeX >= 12 || IsNubOnTopLeft(relativeX, relativeY) => Grass, + Rotate270ClockAnverse when relativeY < 4 || IsNubOnBottomLeft(relativeX, relativeY) => Grass, + _ => River + }, + // River4C narrow 4 way nub on all 4 corners, 4 sides water. rotation does nothing + River4C when (IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) || IsNubOnTopLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY)) => Grass, + // River5A river corner to 2 narrow Nub on top left, top right, and bottom right. 2 narrows meet a river + River5A => landAngle switch + { + Default when IsNubOnTopLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) => Grass, + Rotate90ClockAnverse when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnTopLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass, + Rotate180ClockAnverse when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) || IsNubOnTopLeft(relativeX, relativeY) => Grass, + Rotate270ClockAnverse when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass, + _ => River + }, + // River5B river side land on left side + River5B => landAngle switch + { + Default when relativeX < 4 => Grass, + Rotate90ClockAnverse when relativeY >= 12 => Grass, + Rotate180ClockAnverse when relativeX >= 12 => Grass, + Rotate270ClockAnverse when relativeY < 4 => Grass, + _ => River + }, + // River6A river 2 opposing nubs nub on top left and bottom right + River6A => landAngle switch + { + Default when IsNubOnTopLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) => Grass, + Rotate90ClockAnverse when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass, + Rotate180ClockAnverse when IsNubOnTopLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) => Grass, + Rotate270ClockAnverse when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass, + _ => River + }, + // River6B river 2 nubs same side nub on bottom left and bottom right corner, where 1 narrow meets river bottom side + River6B => landAngle switch + { + Default when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) => Grass, + Rotate90ClockAnverse when IsNubOnBottomRight(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass, + Rotate180ClockAnverse when IsNubOnTopRight(relativeX, relativeY) || IsNubOnTopLeft(relativeX, relativeY) => Grass, + Rotate270ClockAnverse when IsNubOnTopLeft(relativeX, relativeY) || IsNubOnBottomLeft(relativeX, relativeY) => Grass, + _ => River + }, + // River7A river 1 nub nub on bottom left corner, fills gaps of diagonal bank + River7A => landAngle switch + { + Default when IsNubOnBottomLeft(relativeX, relativeY) => Grass, + Rotate90ClockAnverse when IsNubOnBottomRight(relativeX, relativeY) => Grass, + Rotate180ClockAnverse when IsNubOnTopRight(relativeX, relativeY) => Grass, + Rotate270ClockAnverse when IsNubOnTopLeft(relativeX, relativeY) => Grass, + _ => River + }, + // River8A river is no land, just water. Rotation doesn't matter + River8A => River, + _ => River + }; + } + + private static bool IsNubOnTopLeft(int relativeX, int relativeY) => IsPointInTriangle(relativeX, relativeY, new(0, 4), new(0, 0), new(4, 0)); + private static bool IsNubOnTopRight(int relativeX, int relativeY) => IsPointInTriangle(relativeX, relativeY, new(12, 0), new(15, 0), new(15, 4)); + private static bool IsNubOnBottomLeft(int relativeX, int relativeY) => IsPointInTriangle(relativeX, relativeY, new(0, 12), new(0, 15), new(4, 15)); + private static bool IsNubOnBottomRight(int relativeX, int relativeY) => IsPointInTriangle(relativeX, relativeY, new(12, 15), new(15, 15), new(15, 12)); + + private static bool IsPointInMultiTriangle(int px, int py, Coordinate a, Coordinate b, Coordinate c, Coordinate vortexA, Coordinate vortexB) + { + return IsPointInTriangle(px, py, a, vortexA, b) + || IsPointInTriangle(px, py, a, b, c) + || IsPointInTriangle(px, py, c, b, vortexB); + } + + private static bool IsPointInTriangle(int px, int py, Coordinate a, Coordinate b, Coordinate c) + { + Coordinate p = new(px, py); + float areaTotal = GetTriangleArea(a, b, c); + float area1 = GetTriangleArea(p, b, c); + float area2 = GetTriangleArea(a, p, c); + float area3 = GetTriangleArea(a, b, p); + + return Math.Abs(areaTotal - (area1 + area2 + area3)) < 0.0001f; + } + + private static float GetTriangleArea(Coordinate A, Coordinate B, Coordinate C) + { + return Math.Abs((A.X * (B.Y - C.Y) + + B.X * (C.Y - A.Y) + + C.X * (A.Y - B.Y)) / 2.0f); + } + + private readonly record struct Coordinate(int X, int Y); + + private static readonly Color CliffBase = ColorUtil.Blend(Grass, Color.Black, 0.6d); + + private static Color GetTileDefaultColor(TerrainUnitModel mdl, ushort landAngle, int relativeX, int relativeY) + { + var angle = (LandAngles)landAngle; if (mdl.IsRiver()) - return River; + return GetRiverColor(mdl, angle, relativeX, relativeY); if (mdl.IsFall()) return Color.DeepSkyBlue; if (mdl.IsCliff()) return CliffBase; - return Color.ForestGreen; + return Grass; } - private static readonly char[] Numbers = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; + private static readonly char[] Numbers = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; public static string GetTileName(TerrainTile tile) { diff --git a/NHSE.Core/Structures/Map/Layers/TerrainLayer.cs b/NHSE.Core/Structures/Map/Layers/TerrainLayer.cs index 4780335..c426245 100644 --- a/NHSE.Core/Structures/Map/Layers/TerrainLayer.cs +++ b/NHSE.Core/Structures/Map/Layers/TerrainLayer.cs @@ -8,8 +8,8 @@ namespace NHSE.Core /// public class TerrainLayer : MapGrid { - public readonly TerrainTile[] Tiles; - public readonly byte[] BaseAcres; + public TerrainTile[] Tiles { get; init; } + public byte[] BaseAcres { get; init; } public TerrainLayer(TerrainTile[] tiles, byte[] acres) : base(16, 16, AcreWidth * 16, AcreHeight * 16) { @@ -46,6 +46,7 @@ public byte[] DumpAcre(int acre) var bytes = tile.ToBytesClass(); bytes.CopyTo(result, i * TerrainTile.SIZE); } + return result; } @@ -138,7 +139,7 @@ public bool IsWithinGrid(int acreScale, int relX, int relY) return true; } - public int GetTileColor(int x, in int y) + public int GetTileColor(int x, in int y, int relativeX, int relativeY) { var acre = GetTileAcre(x, y); if (acre != 0) @@ -149,7 +150,7 @@ public int GetTileColor(int x, in int y) } var tile = GetTile(x, y); - return TerrainTileColor.GetTileColor(tile).ToArgb(); + return TerrainTileColor.GetTileColor(tile, relativeX, relativeY).ToArgb(); } private ushort GetTileAcre(int x, int y) diff --git a/NHSE.Core/Structures/Map/Terrain/TerrainUnitModel.cs b/NHSE.Core/Structures/Map/Terrain/TerrainUnitModel.cs index 5a22c8c..a980ec3 100644 --- a/NHSE.Core/Structures/Map/Terrain/TerrainUnitModel.cs +++ b/NHSE.Core/Structures/Map/Terrain/TerrainUnitModel.cs @@ -255,6 +255,14 @@ public enum TerrainUnitModel : ushort RoadWood8A = 0x111, } + public enum LandAngles + { + Default = 0, + Rotate90ClockAnverse = 1, + Rotate180ClockAnverse = 2, + Rotate270ClockAnverse = 3, + } + public static class TerrainUnitModelExtensions { public static bool IsRoad(this TerrainUnitModel t) => t >= RoadBrick0A || (RoadSoil0A <= t && t <= RoadStone8A); diff --git a/NHSE.Injection/NHSE.Injection.csproj b/NHSE.Injection/NHSE.Injection.csproj index 0c19d3e..53f5dfe 100644 --- a/NHSE.Injection/NHSE.Injection.csproj +++ b/NHSE.Injection/NHSE.Injection.csproj @@ -5,7 +5,7 @@ - + diff --git a/NHSE.Sprites/Field/MapViewer.cs b/NHSE.Sprites/Field/MapViewer.cs index 6a2b02c..142c67d 100644 --- a/NHSE.Sprites/Field/MapViewer.cs +++ b/NHSE.Sprites/Field/MapViewer.cs @@ -30,7 +30,7 @@ public MapViewer(MapManager m, int scale) : base(m, scale) PixelsItemMap = new int[l1.MaxWidth * l1.MaxHeight * MapScale * MapScale]; MapReticle = new Bitmap(l1.MaxWidth * MapScale, l1.MaxHeight * MapScale); - PixelsBackgroundAcre1 = new int[16 * 16]; + PixelsBackgroundAcre1 = new int[(int)Math.Pow(16, 4)]; PixelsBackgroundAcreX = new int[PixelsItemAcreX.Length]; BackgroundAcre = new Bitmap(ScaleAcre.Width, ScaleAcre.Height); diff --git a/NHSE.Sprites/Field/TerrainSprite.cs b/NHSE.Sprites/Field/TerrainSprite.cs index b0b4b81..8487188 100644 --- a/NHSE.Sprites/Field/TerrainSprite.cs +++ b/NHSE.Sprites/Field/TerrainSprite.cs @@ -1,6 +1,6 @@ -using System.Collections.Generic; +using NHSE.Core; +using System.Collections.Generic; using System.Drawing; -using NHSE.Core; namespace NHSE.Sprites { @@ -23,19 +23,11 @@ public static void CreateMap(TerrainLayer mgr, int[] pixels) { for (int x = 0; x < mgr.MaxWidth; x++, i++) { - pixels[i] = mgr.GetTileColor(x, y); + pixels[i] = mgr.GetTileColor(x, y, x, y); } } } - public static Bitmap CreateMap(TerrainLayer mgr, int scale, int x, int y, int[] scale1, int[] scaleX, Bitmap map) - { - CreateMap(mgr, scale1); - ImageUtil.ScalePixelImage(scale1, scaleX, map.Width, map.Height, scale); - ImageUtil.SetBitmapData(map, scaleX); - return DrawReticle(map, mgr, x, y, scale); - } - public static Bitmap CreateMap(TerrainLayer mgr, int[] scale1, int[] scaleX, Bitmap map, int scale, int acreIndex = -1) { CreateMap(mgr, scale1); @@ -111,17 +103,28 @@ private static void DrawBuilding(Graphics gfx, Font? f, int scale, Brush pen, in private static void SetAcreTerrainPixels(int x, int y, TerrainLayer t, int[] data, int[] scaleX, int scale) { GetAcre1(x, y, t, data); - ImageUtil.ScalePixelImage(data, scaleX, 16 * scale, 16 * scale, scale); + ImageUtil.ScalePixelImage(data, scaleX, 16 * scale, 16 * scale, scale / 16); } - private static void GetAcre1(int topX, int topY, TerrainLayer t, int[] data) + private static void GetAcre1(int tileTopX, int tileTopY, TerrainLayer t, int[] data) { int index = 0; - for (int y = 0; y < 16; y++) + + for (int tileY = 0; tileY < 16; tileY++) { - var yi = y + topY; - for (int x = 0; x < 16; x++, index++) - data[index] = t.GetTileColor(x + topX, yi); + var tileYIx = tileY + tileTopY; + for (int pixelY = 0; pixelY < 16; pixelY++) + { + for (int tileX = 0; tileX < 16; tileX++) + { + var tileXIx = tileX + tileTopX; + for (int pixelX = 0; pixelX < 16; pixelX++) + { + data[index] = t.GetTileColor(tileXIx, tileYIx, pixelX, pixelY); + index++; + } + } + } } } @@ -149,9 +152,10 @@ public static Bitmap GetAcre(MapView m, Font f, int[] scale1, int[] scaleX, Bitm var pen = index == i ? Selected : Others; if (tbuild != byte.MaxValue) { - var orig = ((SolidBrush) pen).Color; + var orig = ((SolidBrush)pen).Color; pen = new SolidBrush(Color.FromArgb(tbuild, orig)); } + DrawBuilding(gfx, null, m.TerrainScale, pen, x, y, b, Text); } @@ -177,7 +181,7 @@ public static Bitmap GetAcre(MapView m, Font f, int[] scale1, int[] scaleX, Bitm private static void DrawTerrainTileNames(int topX, int topY, Graphics gfx, TerrainLayer t, Font f, int scale, byte transparency) { - var pen= transparency != byte.MaxValue ? new SolidBrush(Color.FromArgb(transparency, Color.Black)) : Tile; + var pen = transparency != byte.MaxValue ? new SolidBrush(Color.FromArgb(transparency, Color.Black)) : Tile; for (int y = 0; y < 16; y++) { diff --git a/NHSE.Sprites/NHSE.Sprites.csproj b/NHSE.Sprites/NHSE.Sprites.csproj index 332fb7a..12420b9 100644 --- a/NHSE.Sprites/NHSE.Sprites.csproj +++ b/NHSE.Sprites/NHSE.Sprites.csproj @@ -1,7 +1,7 @@  - net46;net6.0-windows + net46;net8.0-windows en @@ -10,9 +10,9 @@ - - - + + + diff --git a/NHSE.Sprites/Util/ImageUtil.cs b/NHSE.Sprites/Util/ImageUtil.cs index 3ac1bd7..9a0b633 100644 --- a/NHSE.Sprites/Util/ImageUtil.cs +++ b/NHSE.Sprites/Util/ImageUtil.cs @@ -99,16 +99,18 @@ public static int[] ScalePixelImage(int[] data, int scale, int w, int h, out int public static void ScalePixelImage(int[] data, int[] scaled, int fW, int fH, int scale) { // For each pixel, copy to the X indexes, then block copy the row to the other rows. - for (int y = 0, i = 0; y < fH; y += scale) + int i = 0; + for (int y = 0; y < fH; y += scale) { // Fill the X pixels var baseIndex = y * fW; - for (int x = 0; x < fW; x += scale, i++) + for (int x = 0; x < fW; x += scale) { var v = data[i]; var xi = baseIndex + x; for (int x1 = 0; x1 < scale; x1++) scaled[xi + x1] = v; + i++; } // Copy entire pixel row down diff --git a/NHSE.Tests/NHSE.Tests.csproj b/NHSE.Tests/NHSE.Tests.csproj index 44555b4..8164338 100644 --- a/NHSE.Tests/NHSE.Tests.csproj +++ b/NHSE.Tests/NHSE.Tests.csproj @@ -1,15 +1,15 @@ - + - net6.0 + net8.0 false - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/NHSE.Villagers/NHSE.Villagers.csproj b/NHSE.Villagers/NHSE.Villagers.csproj index 323e40b..f9edf8e 100644 --- a/NHSE.Villagers/NHSE.Villagers.csproj +++ b/NHSE.Villagers/NHSE.Villagers.csproj @@ -1,7 +1,7 @@ - + - net46;net6.0-windows + net46;net8.0-windows en @@ -24,9 +24,9 @@ - - - + + + diff --git a/NHSE.WinForms/NHSE.WinForms.csproj b/NHSE.WinForms/NHSE.WinForms.csproj index a6271c0..169dc48 100644 --- a/NHSE.WinForms/NHSE.WinForms.csproj +++ b/NHSE.WinForms/NHSE.WinForms.csproj @@ -2,7 +2,7 @@ WinExe - net46;net6.0-windows + net46;net8.0-windows true Animal Crossing - New Horizons Save Editor NHSE.WinForms.Program diff --git a/NHSE.WinForms/Subforms/Map/FieldItemEditor.cs b/NHSE.WinForms/Subforms/Map/FieldItemEditor.cs index 279ee18..cec9dd4 100644 --- a/NHSE.WinForms/Subforms/Map/FieldItemEditor.cs +++ b/NHSE.WinForms/Subforms/Map/FieldItemEditor.cs @@ -726,11 +726,11 @@ private void Menu_SavePNG_Click(object sender, EventArgs e) } else if (!Menu_SavePNGItems.Checked) { - PB_Map.BackgroundImage.Save(sfd.FileName, ImageFormat.Png); + PB_Map.BackgroundImage!.Save(sfd.FileName, ImageFormat.Png); } else { - var img = (Bitmap)PB_Map.BackgroundImage.Clone(); + var img = (Bitmap)PB_Map.BackgroundImage!.Clone(); using var gfx = Graphics.FromImage(img); gfx.DrawImage(PB_Map.Image, new Point(0, 0)); img.Save(sfd.FileName, ImageFormat.Png); diff --git a/NHSE.WinForms/Subforms/SysBot/BatchEditor.cs b/NHSE.WinForms/Subforms/SysBot/BatchEditor.cs index 1c582aa..2df5583 100644 --- a/NHSE.WinForms/Subforms/SysBot/BatchEditor.cs +++ b/NHSE.WinForms/Subforms/SysBot/BatchEditor.cs @@ -50,7 +50,7 @@ private void B_Add_Click(object sender, EventArgs e) { WinFormsUtil.Alert("Invalid Property"); return; } var prefix = StringInstruction.Prefixes; - string s = prefix[CB_Require.SelectedIndex] + CB_Property.Items[CB_Property.SelectedIndex].ToString() + StringInstruction.SplitInstruction; + string s = prefix[CB_Require.SelectedIndex] + CB_Property.Items[CB_Property.SelectedIndex]!.ToString() + StringInstruction.SplitInstruction; if (RTB_Instructions.Lines.Length != 0 && RTB_Instructions.Lines.Last().Length > 0) s = Environment.NewLine + s; diff --git a/NHSE.WinForms/Util/WinFormsTranslator.cs b/NHSE.WinForms/Util/WinFormsTranslator.cs index a9cad66..40b7b28 100644 --- a/NHSE.WinForms/Util/WinFormsTranslator.cs +++ b/NHSE.WinForms/Util/WinFormsTranslator.cs @@ -45,7 +45,7 @@ private static void TranslateForm(Control form, TranslationContext context) } else if (c is ToolStripItem t) { - var current = t.Text; + var current = t.Text!; var updated = context.GetTranslatedText($"{formname}.{t.Name}", current); if (!ReferenceEquals(current, updated)) t.Text = updated; diff --git a/NHSE.WinForms/Util/WinFormsUtil.cs b/NHSE.WinForms/Util/WinFormsUtil.cs index e951f0c..a9fdb95 100644 --- a/NHSE.WinForms/Util/WinFormsUtil.cs +++ b/NHSE.WinForms/Util/WinFormsUtil.cs @@ -47,15 +47,15 @@ internal static DialogResult Prompt(MessageBoxButtons btn, params string[] lines switch (sender) { case ToolStripItem t: - sender = t.Owner; + sender = t.Owner!; continue; case ContextMenuStrip c: - sender = c.SourceControl; + sender = c.SourceControl!; continue; case T p: return p; default: - return default; + return null; } } }