Support for 16x16px per tile and river editor (#668)

* Upgrade to .NET 8 + Nugets

* Add support for tile 16x16px instead 1px

* Rivers except 2B

* River2B
This commit is contained in:
Alca259
2025-07-28 07:02:34 +02:00
committed by GitHub
parent e0e0656d6e
commit a20bd1c419
15 changed files with 251 additions and 58 deletions

View File

@@ -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)
/// <summary>Notes about rivers the number is how many sides / diagonals are water.</summary>
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)
{

View File

@@ -8,8 +8,8 @@ namespace NHSE.Core
/// </summary>
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)

View File

@@ -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);

View File

@@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LibUsbDotNet" Version="2.2.29" />
<PackageReference Include="LibUsbDotNet" Version="2.2.75" />
</ItemGroup>
<ItemGroup>

View File

@@ -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);

View File

@@ -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++)
{

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net46;net6.0-windows</TargetFrameworks>
<TargetFrameworks>net46;net8.0-windows</TargetFrameworks>
<NeutralLanguage>en</NeutralLanguage>
</PropertyGroup>
@@ -10,9 +10,9 @@
<Folder Include="Resources\Villagers\" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith('net6'))">
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
<PackageReference Include="System.Resources.Extensions" Version="6.0.0" />
<ItemGroup Condition="$(TargetFramework.StartsWith('net8'))">
<PackageReference Include="System.Drawing.Common" Version="8.0.18" />
<PackageReference Include="System.Resources.Extensions" Version="8.0.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -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

View File

@@ -1,15 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="5.10.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PackageReference Include="FluentAssertions" Version="8.5.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net46;net6.0-windows</TargetFrameworks>
<TargetFrameworks>net46;net8.0-windows</TargetFrameworks>
<NeutralLanguage>en</NeutralLanguage>
</PropertyGroup>
@@ -24,9 +24,9 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith('net6'))">
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
<PackageReference Include="System.Resources.Extensions" Version="6.0.0" />
<ItemGroup Condition="$(TargetFramework.StartsWith('net8'))">
<PackageReference Include="System.Drawing.Common" Version="8.0.18" />
<PackageReference Include="System.Resources.Extensions" Version="8.0.0" />
</ItemGroup>
</Project>

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFrameworks>net46;net6.0-windows</TargetFrameworks>
<TargetFrameworks>net46;net8.0-windows</TargetFrameworks>
<UseWindowsForms>true</UseWindowsForms>
<Description>Animal Crossing - New Horizons Save Editor</Description>
<StartupObject>NHSE.WinForms.Program</StartupObject>

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}
}
}