Add Terrain editor

most complex editor yet

could be better if I did PictureBox'es instead of buttons, so I could put an image instead of just color

ppl will have to document more on what each terrain looks like, maybe could show a pic of each terrain tile?
This commit is contained in:
Kurt 2020-04-03 12:12:21 -07:00
parent deb4d51149
commit 8b3c650eff
19 changed files with 1349 additions and 30 deletions

View File

@ -61,5 +61,11 @@ public void SetAcreBytes(byte[] data)
throw new ArgumentOutOfRangeException(nameof(data.Length));
data.CopyTo(Data, Offsets.Acres);
}
public TerrainTile[] Terrain
{
get => TerrainTile.GetArray(Data.Slice(Offsets.Terrain, TerrainManager.TileCount * TerrainTile.SIZE));
set => TerrainTile.SetArray(value).CopyTo(Data, Offsets.Terrain);
}
}
}

View File

@ -23,6 +23,7 @@ public abstract class MainSaveOffsets
public abstract int TurnipExchange { get; }
public abstract int Acres { get; }
public abstract int Terrain { get; }
public static MainSaveOffsets GetOffsets(FileHeaderInfo Info)
{

View File

@ -7,6 +7,7 @@ public class MainSaveOffsets10 : MainSaveOffsets
{
public override int Villager => 0x110;
public override int Patterns => 0x1D72F0;
public override int Terrain => Buildings - 0x24C00; // dunno where exactly it starts???
public override int Buildings => 0x2D0EFC;
public override int Acres => 0x2D1294;
public override int TurnipExchange => 0x4118C0;

View File

@ -7,6 +7,7 @@ public class MainSaveOffsets11 : MainSaveOffsets
{
public override int Villager => 0x120;
public override int Patterns => 0x1D7310; // +0x20 from v1.0
public override int Terrain => Buildings - 0x24C00; // dunno where exactly it starts??? // +0x20 from v1.0
public override int Buildings => 0x2D0F1C; // +0x20 from v1.0
public override int Acres => 0x2D12B4; // +0x20 from v1.0
public override int TurnipExchange => 0x412060; // +0x7A0 from v1.0

View File

@ -0,0 +1,29 @@
namespace NHSE.Core
{
public class AcreCoordinate
{
public readonly string Name;
public readonly int X, Y;
public AcreCoordinate(int x, int y)
{
char yName = (char)('A' + y);
char xName = (char)('0' + x);
Name = $"{yName}{xName}";
X = x;
Y = y;
}
public static AcreCoordinate[] GetGrid(int width, int height)
{
var result = new AcreCoordinate[width * height];
int i = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
result[i++] = new AcreCoordinate(x, y);
}
return result;
}
}
}

View File

@ -0,0 +1,91 @@
using System.Diagnostics;
namespace NHSE.Core
{
public class TerrainManager
{
public readonly TerrainTile[] Tiles;
public readonly AcreCoordinate[] Acres = AcreCoordinate.GetGrid(AcreWidth, AcreHeight);
public const int GridWidth = 16;
public const int GridHeight = 16;
public const int AcreWidth = 7;
public const int AcreHeight = 6;
public const int AcreSize = GridWidth * GridHeight;
public const int MapHeight = AcreHeight * GridHeight; // 92
public const int MapWidth = AcreWidth * GridWidth; // 112
public const int TileCount = MapWidth * MapHeight; // 0x2A00
public TerrainManager(TerrainTile[] tiles)
{
Debug.Assert(TileCount == tiles.Length);
Tiles = tiles;
}
private static int GetIndex(int x, int y) => (MapHeight * x) + y;
public TerrainTile GetTile(int x, int y) => this[GetIndex(x, y)];
public TerrainTile GetTile(int acreX, int acreY, int gridX, int gridY)
{
var x = (acreX * GridWidth) + gridX;
var y = (acreY * GridHeight) + gridY;
return this[GetIndex(x, y)];
}
public TerrainTile GetAcreTile(int acreIndex, int tileIndex)
{
var acre = Acres[acreIndex];
var x = (tileIndex % GridWidth);
var y = (tileIndex / GridHeight);
return GetTile(acre.X, acre.Y, x, y);
}
public TerrainTile this[int index]
{
get => Tiles[index];
set => Tiles[index] = value;
}
public byte[] DumpAllAcres()
{
var result = new byte[Tiles.Length * TerrainTile.SIZE];
for (int i = 0; i < Tiles.Length; i++)
Tiles[i].ToBytesClass().CopyTo(result, i * TerrainTile.SIZE);
return result;
}
public byte[] DumpAcre(int acre)
{
const int count = (GridWidth * GridHeight);
var result = new byte[TerrainTile.SIZE * count];
for (int i = 0; i < count; i++)
{
var tile = GetTile(acre, i);
var bytes = tile.ToBytesClass();
bytes.CopyTo(result, i * TerrainTile.SIZE);
}
return result;
}
public void ImportAllAcres(byte[] data)
{
var tiles = TerrainTile.GetArray(data);
for (int i = 0; i < tiles.Length; i++)
Tiles[i].CopyFrom(tiles[i]);
}
public void ImportAcre(int acre, byte[] data)
{
const int count = (GridWidth * GridHeight);
var tiles = TerrainTile.GetArray(data);
for (int i = 0; i < count; i++)
{
var tile = GetTile(acre, i);
tile.CopyFrom(tiles[i]);
}
}
}
}

View File

@ -0,0 +1,45 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace NHSE.Core
{
[StructLayout(LayoutKind.Sequential)]
public class TerrainTile
{
public const int SIZE = 0xE;
private const string Details = nameof(Details);
[Category(Details), Description("Terrain model to be loaded for this tile.")]
public TerrainUnitModel UnitModel { get; set; }
public ushort Unk2 { get; set; }
public ushort Unk4 { get; set; }
public ushort Unk6 { get; set; }
public ushort Unk8 { get; set; }
public ushort UnkA { get; set; }
[Category(Details), Description("How high the terrain tile is elevated.")]
public ushort Elevation { get; set; }
public static TerrainTile[] GetArray(byte[] data) => data.GetArray<TerrainTile>(SIZE);
public static byte[] SetArray(IReadOnlyList<TerrainTile> data) => data.SetArray(SIZE);
public void Clear()
{
UnitModel = 0;
Unk2 = Unk4 = Unk6 = Unk8 = UnkA = Elevation = 0;
}
public void CopyFrom(TerrainTile source)
{
UnitModel = source.UnitModel;
Unk2 = source.Unk2;
Unk4 = source.Unk4;
Unk6 = source.Unk6;
Unk8 = source.Unk8;
UnkA = source.UnkA;
Elevation = source.Elevation;
}
}
}

View File

@ -0,0 +1,252 @@
namespace NHSE.Core
{
public enum TerrainUnitModel : ushort
{
Base = 0x00,
River0A = 0x01,
River1A = 0x04,
River2A = 0x07,
River2B = 0x08,
River2C = 0x09,
River3A = 0x0A,
River3B = 0x0B,
River3C = 0x0C,
River4A = 0x0D,
River4B = 0x0E,
River4C = 0x0F,
River5A = 0x10,
River5B = 0x11,
River6A = 0x12,
River6B = 0x13,
River7A = 0x14,
River8A = 0x15,
Cliff0A = 0x16,
Cliff0A_2 = 0x17,
Cliff0A_3 = 0x18,
Cliff1A = 0x19,
Cliff1A_2 = 0x1A,
Cliff1A_3 = 0x1B,
Cliff2A = 0x1C,
Cliff2C = 0x1D,
Cliff3A = 0x1E,
Cliff3B = 0x1F,
Cliff3C = 0x20,
Cliff4A = 0x21,
Cliff4B = 0x22,
Cliff4C = 0x23,
Cliff5A = 0x24,
Cliff5B = 0x25,
Cliff6A = 0x26,
Cliff6B = 0x27,
Cliff7A = 0x28,
Cliff8 = 0x29,
Fall101 = 0x3A,
Fall100 = 0x3B,
Fall300 = 0x3C,
Fall301 = 0x3D,
Fall302 = 0x3E,
Fall200 = 0x3F,
Fall201 = 0x40,
Fall202 = 0x41,
Fall400 = 0x42,
Fall401 = 0x43,
Fall402 = 0x44,
Fall403 = 0x45,
Fall404 = 0x46,
RoadSoil0A = 0x47,
RoadSoil1A = 0x48,
RoadSoil0B = 0x49,
RoadSoil1B = 0x4B,
RoadSoil1C = 0x4C,
RoadSoil2A = 0x4D,
RoadSoil2B = 0x4E,
RoadSoil2C = 0x4F,
RoadSoil3A = 0x50,
RoadSoil3B = 0x51,
RoadSoil3C = 0x52,
RoadSoil4A = 0x53,
RoadSoil4B = 0x54,
RoadSoil4C = 0x55,
RoadSoil5A = 0x56,
RoadSoil5B = 0x57,
RoadSoil6A = 0x58,
RoadSoil6B = 0x59,
RoadSoil7A = 0x5A,
RoadSoil8A = 0x5B,
RoadStone0A = 0x5C,
RoadStone0B = 0x5D,
RoadStone1A = 0x5F,
RoadStone1B = 0x60,
RoadStone1C = 0x61,
RoadStone2A = 0x62,
RoadStone2B = 0x63,
RoadStone2C = 0x64,
RoadStone3A = 0x65,
RoadStone3B = 0x66,
RoadStone3C = 0x67,
RoadStone4A = 0x68,
RoadStone4B = 0x69,
RoadStone4C = 0x6A,
RoadStone5A = 0x6B,
RoadStone5B = 0x6C,
RoadStone6A = 0x6D,
RoadStone6B = 0x6E,
RoadStone7A = 0x6F,
RoadStone8A = 0x70,
Fall103 = 0x71,
Fall102 = 0x72,
Fall303 = 0x73,
Fall304 = 0x74,
Fall305 = 0x75,
Fall306 = 0x76,
Fall307 = 0x77,
Fall308 = 0x78,
Fall203 = 0x79,
Fall204 = 0x7A,
Fall205 = 0x7B,
Fall206 = 0x7C,
Fall207 = 0x7D,
Fall208 = 0x7E,
Fall405 = 0x7F,
Fall406 = 0x80,
Fall407 = 0x81,
Fall408 = 0x82,
Fall410 = 0x83,
Fall409 = 0x84,
Fall411 = 0x85,
Fall412 = 0x86,
Fall414 = 0x87,
Fall413 = 0x88,
Fall415 = 0x89,
Fall416 = 0x8A,
Fall418 = 0x8B,
Fall417 = 0x8C,
Fall419 = 0x8D,
Fall420 = 0x8E,
Fall422 = 0x8F,
Fall421 = 0x90,
Fall423 = 0x91,
Fall424 = 0x92,
Cliff2B = 0x93,
RoadBrick0A = 0x94,
RoadBrick0B = 0x95,
RoadBrick1A = 0x97,
RoadBrick1B = 0x98,
RoadBrick1C = 0x99,
RoadBrick2A = 0x9A,
RoadBrick2B = 0x9B,
RoadBrick2C = 0x9C,
RoadBrick3A = 0x9D,
RoadBrick3B = 0x9E,
RoadBrick3C = 0x9F,
RoadBrick4A = 0xA0,
RoadBrick4B = 0xA1,
RoadBrick4C = 0xA2,
RoadBrick5A = 0xA3,
RoadBrick5B = 0xA4,
RoadBrick6A = 0xA5,
RoadBrick6B = 0xA6,
RoadBrick7A = 0xA7,
RoadBrick8A = 0xA8,
RoadDarkSoil0A = 0xA9,
RoadDarkSoil0B = 0xAA,
RoadDarkSoil1A = 0xAC,
RoadDarkSoil1B = 0xAD,
RoadDarkSoil1C = 0xAE,
RoadDarkSoil2A = 0xAF,
RoadDarkSoil2B = 0xB0,
RoadDarkSoil2C = 0xB1,
RoadDarkSoil3A = 0xB2,
RoadDarkSoil3B = 0xB3,
RoadDarkSoil3C = 0xB4,
RoadDarkSoil4A = 0xB5,
RoadDarkSoil4B = 0xB6,
RoadDarkSoil4C = 0xB7,
RoadDarkSoil5A = 0xB8,
RoadDarkSoil5B = 0xB9,
RoadDarkSoil6A = 0xBA,
RoadDarkSoil6B = 0xBB,
RoadDarkSoil7A = 0xBC,
RoadDarkSoil8A = 0xBD,
RoadFanPattern0A = 0xBE,
RoadFanPattern0B = 0xBF,
RoadFanPattern1A = 0xC1,
RoadFanPattern1B = 0xC2,
RoadFanPattern1C = 0xC3,
RoadFanPattern2A = 0xC4,
RoadFanPattern2B = 0xC5,
RoadFanPattern2C = 0xC6,
RoadFanPattern3A = 0xC7,
RoadFanPattern3B = 0xC8,
RoadFanPattern3C = 0xC9,
RoadFanPattern4A = 0xCA,
RoadFanPattern4B = 0xCB,
RoadFanPattern4C = 0xCC,
RoadFanPattern5A = 0xCD,
RoadFanPattern5B = 0xCE,
RoadFanPattern6A = 0xCF,
RoadFanPattern6B = 0xD0,
RoadFanPattern7A = 0xD1,
RoadFanPattern8A = 0xD2,
RoadSand0A = 0xD3,
RoadSand0B = 0xD4,
RoadSand1A = 0xD6,
RoadSand1B = 0xD7,
RoadSand1C = 0xD8,
RoadSand2A = 0xD9,
RoadSand2B = 0xDA,
RoadSand2C = 0xDB,
RoadSand3A = 0xDC,
RoadSand3B = 0xDD,
RoadSand3C = 0xDE,
RoadSand4A = 0xDF,
RoadSand4B = 0xE0,
RoadSand4C = 0xE1,
RoadSand5A = 0xE2,
RoadSand5B = 0xE3,
RoadSand6A = 0xE4,
RoadSand6B = 0xE5,
RoadSand7A = 0xE6,
RoadSand8A = 0xE7,
RoadTile0A = 0xE8,
RoadTile0B = 0xE9,
RoadTile1A = 0xEB,
RoadTile1B = 0xEC,
RoadTile1C = 0xED,
RoadTile2A = 0xEE,
RoadTile2B = 0xEF,
RoadTile2C = 0xF0,
RoadTile3A = 0xF1,
RoadTile3B = 0xF2,
RoadTile3C = 0xF3,
RoadTile4A = 0xF4,
RoadTile4B = 0xF5,
RoadTile4C = 0xF6,
RoadTile5A = 0xF7,
RoadTile5B = 0xF8,
RoadTile6A = 0xF9,
RoadTile6B = 0xFA,
RoadTile7A = 0xFB,
RoadTile8A = 0xFC,
RoadWood0A = 0xFD,
RoadWood0B = 0xFE,
RoadWood1A = 0x100,
RoadWood1B = 0x101,
RoadWood1C = 0x102,
RoadWood2A = 0x103,
RoadWood2B = 0x104,
RoadWood2C = 0x105,
RoadWood3A = 0x106,
RoadWood3B = 0x107,
RoadWood3C = 0x108,
RoadWood4A = 0x109,
RoadWood4B = 0x10A,
RoadWood4C = 0x10B,
RoadWood5A = 0x10C,
RoadWood5B = 0x10D,
RoadWood6A = 0x10E,
RoadWood6B = 0x10F,
RoadWood7A = 0x110,
RoadWood8A = 0x111,
}
}

View File

@ -43,6 +43,7 @@ void DumpB(string fn, byte[] bytes)
DumpS("ItemKind.txt", GetPossibleEnum(pathBCSV, "ItemParam.bcsv", 0xFC275E86));
DumpS("PlantKind.txt", GetPossibleEnum(pathBCSV, "FgMainParam.bcsv", 0x48EF0398));
DumpS("TerrainKind.txt", GetNumberedEnumValues(pathBCSV, "FieldLandMakingUnitModelParam.bcsv", 0x39B5A93D, 0x54706054));
DumpB("item_kind.bin", GetItemKindArray(pathBCSV));
DumpS("plants.txt", GetPlantedNames(pathBCSV));
@ -68,6 +69,40 @@ private static IEnumerable<string> GetPossibleEnumValues(string pathBCSV, string
return types;
}
private static IEnumerable<string> GetNumberedEnumValues(string pathBCSV, string fn, uint keyName, uint keyValue)
{
var path = Path.Combine(pathBCSV, fn);
var data = File.ReadAllBytes(path);
var bcsv = new BCSV(data);
var dict = bcsv.GetFieldDictionary();
var fType = dict[keyName];
var fitemID = dict[keyValue];
var types = new List<string>();
var set = new Dictionary<string, int>();
for (int i = 0; i < bcsv.EntryCount; i++)
{
var iid = bcsv.ReadValue(i, fitemID);
var ival = ushort.Parse(iid);
var type = bcsv.ReadValue(i, fType).TrimEnd('\0');
if (set.TryGetValue(type, out var count))
{
count++;
set[type] = count;
type += $"_{count}";
}
else
{
set.Add(type, 1);
}
types.Add($"{type} = 0x{ival:X2},");
}
return types;
}
private static IEnumerable<string> GetPossibleEnum(string pathBCSV, string fn, uint key)
{
var kinds = GetPossibleEnumValues(pathBCSV, fn, key);

15
NHSE.Sprites/ColorUtil.cs Normal file
View File

@ -0,0 +1,15 @@
using System.Drawing;
namespace NHSE.Sprites
{
public static class ColorUtil
{
public static Color Blend(Color color, Color backColor, double amount)
{
byte r = (byte)((color.R * amount) + (backColor.R * (1 - amount)));
byte g = (byte)((color.G * amount) + (backColor.G * (1 - amount)));
byte b = (byte)((color.B * amount) + (backColor.B * (1 - amount)));
return Color.FromArgb(r, g, b);
}
}
}

View File

@ -75,15 +75,7 @@ private static Color GetItemColor(Item item)
var known = Colors[(int)kind];
var color = Color.FromKnownColor(known);
// soften the colors a little
return Blend(Color.White, color, 0.5d);
}
public static Color Blend(Color color, Color backColor, double amount)
{
byte r = (byte)((color.R * amount) + (backColor.R * (1 - amount)));
byte g = (byte)((color.G * amount) + (backColor.G * (1 - amount)));
byte b = (byte)((color.B * amount) + (backColor.B * (1 - amount)));
return Color.FromArgb(r, g, b);
return ColorUtil.Blend(Color.White, color, 0.5d);
}
private static readonly KnownColor[] Colors = (KnownColor[])Enum.GetValues(typeof(KnownColor));

View File

@ -0,0 +1,57 @@
using System;
using System.Drawing;
using NHSE.Core;
namespace NHSE.Sprites
{
public static class TerrainSprite
{
public static Color GetTileColor(TerrainTile tile)
{
var name = tile.UnitModel.ToString();
var baseColor = GetTileColor(name);
if (tile.Elevation == 0)
return baseColor;
return ColorUtil.Blend(baseColor, Color.White, 1d / (tile.Elevation + 1));
}
private static Color GetTileColor(string name)
{
if (name.StartsWith("River")) // River
return Color.DeepSkyBlue;
if (name.StartsWith("Fall")) // Waterfall
return Color.DarkBlue;
if (name.Contains("Cliff"))
return ColorUtil.Blend(Color.ForestGreen, Color.Black, 0.5d);
return Color.ForestGreen;
}
private static readonly char[] Numbers = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
public static string GetTileName(TerrainTile tile)
{
var name = tile.UnitModel.ToString();
var num = name.IndexOfAny(Numbers);
if (num < 0)
return name;
return name.Substring(0, num) + Environment.NewLine + name.Substring(num);
}
public static Bitmap CreateMap(TerrainManager mgr)
{
var bmp = new Bitmap(TerrainManager.MapWidth, TerrainManager.MapHeight);
for (int x = 0; x < TerrainManager.MapWidth; x++)
{
for (int y = 0; y < TerrainManager.MapHeight; y++)
{
var tile = mgr.GetTile(x, y);
var color = GetTileColor(tile);
bmp.SetPixel(x, y, color);
}
}
return bmp;
}
}
}

View File

@ -4,7 +4,7 @@
namespace NHSE.Tests
{
public class ItemTests
public class MarshalTests
{
[Fact]
public void ItemMarshal()
@ -29,5 +29,13 @@ public void MapItemMarshal()
var bytes = item.ToBytesClass();
bytes.Length.Should().Be(MapItem.SIZE);
}
[Fact]
public void TerrainTileMarshal()
{
var obj = new TerrainTile();
var bytes = obj.ToBytesClass();
bytes.Length.Should().Be(TerrainTile.SIZE);
}
}
}

View File

@ -85,10 +85,11 @@ private void InitializeComponent()
this.NUD_PatternIndex = new System.Windows.Forms.NumericUpDown();
this.PB_Pattern = new System.Windows.Forms.PictureBox();
this.Tab_Map = new System.Windows.Forms.TabPage();
this.B_EditAcres = new System.Windows.Forms.Button();
this.B_EditTurnipExchange = new System.Windows.Forms.Button();
this.B_EditBuildings = new System.Windows.Forms.Button();
this.B_RecycleBin = new System.Windows.Forms.Button();
this.B_EditTurnipExchange = new System.Windows.Forms.Button();
this.B_EditAcres = new System.Windows.Forms.Button();
this.B_EditTerrain = new System.Windows.Forms.Button();
this.Menu_Editor.SuspendLayout();
this.TC_Editors.SuspendLayout();
this.Tab_Players.SuspendLayout();
@ -711,6 +712,7 @@ private void InitializeComponent()
//
// Tab_Map
//
this.Tab_Map.Controls.Add(this.B_EditTerrain);
this.Tab_Map.Controls.Add(this.B_EditAcres);
this.Tab_Map.Controls.Add(this.B_EditTurnipExchange);
this.Tab_Map.Controls.Add(this.B_EditBuildings);
@ -723,6 +725,26 @@ private void InitializeComponent()
this.Tab_Map.Text = "Map";
this.Tab_Map.UseVisualStyleBackColor = true;
//
// B_EditAcres
//
this.B_EditAcres.Location = new System.Drawing.Point(300, 168);
this.B_EditAcres.Name = "B_EditAcres";
this.B_EditAcres.Size = new System.Drawing.Size(92, 40);
this.B_EditAcres.TabIndex = 16;
this.B_EditAcres.Text = "Edit Acres";
this.B_EditAcres.UseVisualStyleBackColor = true;
this.B_EditAcres.Click += new System.EventHandler(this.B_EditAcres_Click);
//
// B_EditTurnipExchange
//
this.B_EditTurnipExchange.Location = new System.Drawing.Point(202, 168);
this.B_EditTurnipExchange.Name = "B_EditTurnipExchange";
this.B_EditTurnipExchange.Size = new System.Drawing.Size(92, 40);
this.B_EditTurnipExchange.TabIndex = 15;
this.B_EditTurnipExchange.Text = "Edit Turnip Exchange";
this.B_EditTurnipExchange.UseVisualStyleBackColor = true;
this.B_EditTurnipExchange.Click += new System.EventHandler(this.B_EditTurnipExchange_Click);
//
// B_EditBuildings
//
this.B_EditBuildings.Location = new System.Drawing.Point(104, 168);
@ -743,25 +765,15 @@ private void InitializeComponent()
this.B_RecycleBin.UseVisualStyleBackColor = true;
this.B_RecycleBin.Click += new System.EventHandler(this.B_RecycleBin_Click);
//
// B_EditTurnipExchange
// B_EditTerrain
//
this.B_EditTurnipExchange.Location = new System.Drawing.Point(202, 168);
this.B_EditTurnipExchange.Name = "B_EditTurnipExchange";
this.B_EditTurnipExchange.Size = new System.Drawing.Size(92, 40);
this.B_EditTurnipExchange.TabIndex = 15;
this.B_EditTurnipExchange.Text = "Edit Turnip Exchange";
this.B_EditTurnipExchange.UseVisualStyleBackColor = true;
this.B_EditTurnipExchange.Click += new System.EventHandler(this.B_EditTurnipExchange_Click);
//
// B_EditAcres
//
this.B_EditAcres.Location = new System.Drawing.Point(300, 168);
this.B_EditAcres.Name = "B_EditAcres";
this.B_EditAcres.Size = new System.Drawing.Size(92, 40);
this.B_EditAcres.TabIndex = 16;
this.B_EditAcres.Text = "Edit Acres";
this.B_EditAcres.UseVisualStyleBackColor = true;
this.B_EditAcres.Click += new System.EventHandler(this.B_EditAcres_Click);
this.B_EditTerrain.Location = new System.Drawing.Point(300, 122);
this.B_EditTerrain.Name = "B_EditTerrain";
this.B_EditTerrain.Size = new System.Drawing.Size(92, 40);
this.B_EditTerrain.TabIndex = 17;
this.B_EditTerrain.Text = "Edit Terrain";
this.B_EditTerrain.UseVisualStyleBackColor = true;
this.B_EditTerrain.Click += new System.EventHandler(this.B_EditTerrain_Click);
//
// Editor
//
@ -865,6 +877,7 @@ private void InitializeComponent()
private System.Windows.Forms.ToolStripMenuItem Menu_RAMEdit;
private System.Windows.Forms.Button B_EditTurnipExchange;
private System.Windows.Forms.Button B_EditAcres;
private System.Windows.Forms.Button B_EditTerrain;
}
}

View File

@ -514,5 +514,11 @@ private void B_EditAcres_Click(object sender, EventArgs e)
using var editor = new AcreEditor(SAV.Main);
editor.ShowDialog();
}
private void B_EditTerrain_Click(object sender, EventArgs e)
{
using var editor = new TerrainEditor(SAV.Main);
editor.ShowDialog();
}
}
}

View File

@ -40,6 +40,9 @@
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
<Compile Update="Subforms\TerrainEditor.cs">
<SubType>Form</SubType>
</Compile>
<Compile Update="Subforms\SingleObjectEditor.cs">
<SubType>Form</SubType>
</Compile>

View File

@ -0,0 +1,336 @@
namespace NHSE.WinForms
{
partial class TerrainEditor
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.B_Cancel = new System.Windows.Forms.Button();
this.B_Save = new System.Windows.Forms.Button();
this.FLP_Tile = new System.Windows.Forms.FlowLayoutPanel();
this.PG_Tile = new System.Windows.Forms.PropertyGrid();
this.CB_Acre = new System.Windows.Forms.ComboBox();
this.L_Acre = new System.Windows.Forms.Label();
this.B_ZeroElevation = new System.Windows.Forms.Button();
this.B_ClearAll = new System.Windows.Forms.Button();
this.B_DumpAcre = new System.Windows.Forms.Button();
this.B_DumpAllAcres = new System.Windows.Forms.Button();
this.B_ImportAllAcres = new System.Windows.Forms.Button();
this.B_ImportAcre = new System.Windows.Forms.Button();
this.CM_Click = new System.Windows.Forms.ContextMenuStrip(this.components);
this.Menu_View = new System.Windows.Forms.ToolStripMenuItem();
this.Menu_Set = new System.Windows.Forms.ToolStripMenuItem();
this.Menu_Reset = new System.Windows.Forms.ToolStripMenuItem();
this.B_Up = new System.Windows.Forms.Button();
this.B_Left = new System.Windows.Forms.Button();
this.B_Right = new System.Windows.Forms.Button();
this.B_Down = new System.Windows.Forms.Button();
this.PB_Map = new System.Windows.Forms.PictureBox();
this.CM_Picture = new System.Windows.Forms.ContextMenuStrip(this.components);
this.Menu_SavePNG = new System.Windows.Forms.ToolStripMenuItem();
this.CM_Click.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.PB_Map)).BeginInit();
this.CM_Picture.SuspendLayout();
this.SuspendLayout();
//
// B_Cancel
//
this.B_Cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.B_Cancel.Location = new System.Drawing.Point(899, 787);
this.B_Cancel.Name = "B_Cancel";
this.B_Cancel.Size = new System.Drawing.Size(72, 23);
this.B_Cancel.TabIndex = 7;
this.B_Cancel.Text = "Cancel";
this.B_Cancel.UseVisualStyleBackColor = true;
this.B_Cancel.Click += new System.EventHandler(this.B_Cancel_Click);
//
// B_Save
//
this.B_Save.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.B_Save.Location = new System.Drawing.Point(977, 787);
this.B_Save.Name = "B_Save";
this.B_Save.Size = new System.Drawing.Size(72, 23);
this.B_Save.TabIndex = 6;
this.B_Save.Text = "Save";
this.B_Save.UseVisualStyleBackColor = true;
this.B_Save.Click += new System.EventHandler(this.B_Save_Click);
//
// FLP_Tile
//
this.FLP_Tile.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.FLP_Tile.Location = new System.Drawing.Point(12, 12);
this.FLP_Tile.Name = "FLP_Tile";
this.FLP_Tile.Size = new System.Drawing.Size(802, 802);
this.FLP_Tile.TabIndex = 8;
//
// PG_Tile
//
this.PG_Tile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.PG_Tile.Location = new System.Drawing.Point(820, 12);
this.PG_Tile.Name = "PG_Tile";
this.PG_Tile.Size = new System.Drawing.Size(229, 244);
this.PG_Tile.TabIndex = 9;
//
// CB_Acre
//
this.CB_Acre.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.CB_Acre.FormattingEnabled = true;
this.CB_Acre.Location = new System.Drawing.Point(978, 588);
this.CB_Acre.Name = "CB_Acre";
this.CB_Acre.Size = new System.Drawing.Size(49, 21);
this.CB_Acre.TabIndex = 10;
this.CB_Acre.SelectedIndexChanged += new System.EventHandler(this.ChangeAcre);
//
// L_Acre
//
this.L_Acre.AutoSize = true;
this.L_Acre.Location = new System.Drawing.Point(940, 591);
this.L_Acre.Name = "L_Acre";
this.L_Acre.Size = new System.Drawing.Size(32, 13);
this.L_Acre.TabIndex = 11;
this.L_Acre.Text = "Acre:";
//
// B_ZeroElevation
//
this.B_ZeroElevation.Location = new System.Drawing.Point(820, 276);
this.B_ZeroElevation.Name = "B_ZeroElevation";
this.B_ZeroElevation.Size = new System.Drawing.Size(112, 40);
this.B_ZeroElevation.TabIndex = 12;
this.B_ZeroElevation.Text = "Zero All Elevation";
this.B_ZeroElevation.UseVisualStyleBackColor = true;
this.B_ZeroElevation.Click += new System.EventHandler(this.B_ZeroElevation_Click);
//
// B_ClearAll
//
this.B_ClearAll.Location = new System.Drawing.Point(937, 276);
this.B_ClearAll.Name = "B_ClearAll";
this.B_ClearAll.Size = new System.Drawing.Size(112, 40);
this.B_ClearAll.TabIndex = 13;
this.B_ClearAll.Text = "Reset All To Base\r\n(no Terrain)";
this.B_ClearAll.UseVisualStyleBackColor = true;
this.B_ClearAll.Click += new System.EventHandler(this.B_ClearAll_Click);
//
// B_DumpAcre
//
this.B_DumpAcre.Location = new System.Drawing.Point(820, 662);
this.B_DumpAcre.Name = "B_DumpAcre";
this.B_DumpAcre.Size = new System.Drawing.Size(112, 40);
this.B_DumpAcre.TabIndex = 14;
this.B_DumpAcre.Text = "Dump Acre";
this.B_DumpAcre.UseVisualStyleBackColor = true;
this.B_DumpAcre.Click += new System.EventHandler(this.B_DumpAcre_Click);
//
// B_DumpAllAcres
//
this.B_DumpAllAcres.Location = new System.Drawing.Point(820, 708);
this.B_DumpAllAcres.Name = "B_DumpAllAcres";
this.B_DumpAllAcres.Size = new System.Drawing.Size(112, 40);
this.B_DumpAllAcres.TabIndex = 15;
this.B_DumpAllAcres.Text = "Dump All Acres";
this.B_DumpAllAcres.UseVisualStyleBackColor = true;
this.B_DumpAllAcres.Click += new System.EventHandler(this.B_DumpAllAcres_Click);
//
// B_ImportAllAcres
//
this.B_ImportAllAcres.Location = new System.Drawing.Point(937, 708);
this.B_ImportAllAcres.Name = "B_ImportAllAcres";
this.B_ImportAllAcres.Size = new System.Drawing.Size(112, 40);
this.B_ImportAllAcres.TabIndex = 17;
this.B_ImportAllAcres.Text = "Import All Acres";
this.B_ImportAllAcres.UseVisualStyleBackColor = true;
this.B_ImportAllAcres.Click += new System.EventHandler(this.B_ImportAllAcres_Click);
//
// B_ImportAcre
//
this.B_ImportAcre.Location = new System.Drawing.Point(937, 662);
this.B_ImportAcre.Name = "B_ImportAcre";
this.B_ImportAcre.Size = new System.Drawing.Size(112, 40);
this.B_ImportAcre.TabIndex = 16;
this.B_ImportAcre.Text = "Import Acre";
this.B_ImportAcre.UseVisualStyleBackColor = true;
this.B_ImportAcre.Click += new System.EventHandler(this.B_ImportAcre_Click);
//
// CM_Click
//
this.CM_Click.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.Menu_View,
this.Menu_Set,
this.Menu_Reset});
this.CM_Click.Name = "CM_Click";
this.CM_Click.Size = new System.Drawing.Size(103, 70);
//
// Menu_View
//
this.Menu_View.Name = "Menu_View";
this.Menu_View.Size = new System.Drawing.Size(102, 22);
this.Menu_View.Text = "View";
this.Menu_View.Click += new System.EventHandler(this.Menu_View_Click);
//
// Menu_Set
//
this.Menu_Set.Name = "Menu_Set";
this.Menu_Set.Size = new System.Drawing.Size(102, 22);
this.Menu_Set.Text = "Set";
this.Menu_Set.Click += new System.EventHandler(this.Menu_Set_Click);
//
// Menu_Reset
//
this.Menu_Reset.Name = "Menu_Reset";
this.Menu_Reset.Size = new System.Drawing.Size(102, 22);
this.Menu_Reset.Text = "Reset";
this.Menu_Reset.Click += new System.EventHandler(this.Menu_Reset_Click);
//
// B_Up
//
this.B_Up.Location = new System.Drawing.Point(868, 551);
this.B_Up.Name = "B_Up";
this.B_Up.Size = new System.Drawing.Size(32, 32);
this.B_Up.TabIndex = 18;
this.B_Up.Text = "↑";
this.B_Up.UseVisualStyleBackColor = true;
this.B_Up.Click += new System.EventHandler(this.B_Up_Click);
//
// B_Left
//
this.B_Left.Location = new System.Drawing.Point(838, 581);
this.B_Left.Name = "B_Left";
this.B_Left.Size = new System.Drawing.Size(32, 32);
this.B_Left.TabIndex = 19;
this.B_Left.Text = "←";
this.B_Left.UseVisualStyleBackColor = true;
this.B_Left.Click += new System.EventHandler(this.B_Left_Click);
//
// B_Right
//
this.B_Right.Location = new System.Drawing.Point(898, 581);
this.B_Right.Name = "B_Right";
this.B_Right.Size = new System.Drawing.Size(32, 32);
this.B_Right.TabIndex = 20;
this.B_Right.Text = "→";
this.B_Right.UseVisualStyleBackColor = true;
this.B_Right.Click += new System.EventHandler(this.B_Right_Click);
//
// B_Down
//
this.B_Down.Location = new System.Drawing.Point(868, 611);
this.B_Down.Name = "B_Down";
this.B_Down.Size = new System.Drawing.Size(32, 32);
this.B_Down.TabIndex = 22;
this.B_Down.Text = "↓";
this.B_Down.UseVisualStyleBackColor = true;
this.B_Down.Click += new System.EventHandler(this.B_Down_Click);
//
// PB_Map
//
this.PB_Map.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.PB_Map.ContextMenuStrip = this.CM_Picture;
this.PB_Map.Location = new System.Drawing.Point(821, 338);
this.PB_Map.Name = "PB_Map";
this.PB_Map.Size = new System.Drawing.Size(226, 194);
this.PB_Map.TabIndex = 23;
this.PB_Map.TabStop = false;
//
// CM_Picture
//
this.CM_Picture.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.Menu_SavePNG});
this.CM_Picture.Name = "CM_Picture";
this.CM_Picture.ShowImageMargin = false;
this.CM_Picture.Size = new System.Drawing.Size(101, 26);
//
// Menu_SavePNG
//
this.Menu_SavePNG.Name = "Menu_SavePNG";
this.Menu_SavePNG.Size = new System.Drawing.Size(155, 22);
this.Menu_SavePNG.Text = "Save .png";
this.Menu_SavePNG.Click += new System.EventHandler(this.Menu_SavePNG_Click);
//
// TerrainEditor
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1061, 822);
this.Controls.Add(this.PB_Map);
this.Controls.Add(this.B_Down);
this.Controls.Add(this.B_Right);
this.Controls.Add(this.B_Left);
this.Controls.Add(this.B_Up);
this.Controls.Add(this.B_ImportAllAcres);
this.Controls.Add(this.B_ImportAcre);
this.Controls.Add(this.B_DumpAllAcres);
this.Controls.Add(this.B_DumpAcre);
this.Controls.Add(this.B_ClearAll);
this.Controls.Add(this.B_ZeroElevation);
this.Controls.Add(this.L_Acre);
this.Controls.Add(this.CB_Acre);
this.Controls.Add(this.PG_Tile);
this.Controls.Add(this.FLP_Tile);
this.Controls.Add(this.B_Cancel);
this.Controls.Add(this.B_Save);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = global::NHSE.WinForms.Properties.Resources.icon;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "TerrainEditor";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Terrain Editor";
this.CM_Click.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.PB_Map)).EndInit();
this.CM_Picture.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button B_Cancel;
private System.Windows.Forms.Button B_Save;
private System.Windows.Forms.FlowLayoutPanel FLP_Tile;
private System.Windows.Forms.PropertyGrid PG_Tile;
private System.Windows.Forms.ComboBox CB_Acre;
private System.Windows.Forms.Label L_Acre;
private System.Windows.Forms.Button B_ZeroElevation;
private System.Windows.Forms.Button B_ClearAll;
private System.Windows.Forms.Button B_DumpAcre;
private System.Windows.Forms.Button B_DumpAllAcres;
private System.Windows.Forms.Button B_ImportAllAcres;
private System.Windows.Forms.Button B_ImportAcre;
private System.Windows.Forms.ContextMenuStrip CM_Click;
private System.Windows.Forms.ToolStripMenuItem Menu_View;
private System.Windows.Forms.ToolStripMenuItem Menu_Set;
private System.Windows.Forms.ToolStripMenuItem Menu_Reset;
private System.Windows.Forms.Button B_Up;
private System.Windows.Forms.Button B_Left;
private System.Windows.Forms.Button B_Right;
private System.Windows.Forms.Button B_Down;
private System.Windows.Forms.PictureBox PB_Map;
private System.Windows.Forms.ContextMenuStrip CM_Picture;
private System.Windows.Forms.ToolStripMenuItem Menu_SavePNG;
}
}

View File

@ -0,0 +1,302 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;
using NHSE.Core;
using NHSE.Sprites;
namespace NHSE.WinForms
{
public partial class TerrainEditor : Form
{
private readonly MainSave SAV;
private readonly Button[] Grid;
private readonly TerrainManager Terrain;
private const int SquareSize = 50;
public TerrainEditor(MainSave sav)
{
SAV = sav;
InitializeComponent();
Terrain = new TerrainManager(SAV.Terrain);
Grid = GenerateGrid(TerrainManager.GridWidth, TerrainManager.GridHeight);
foreach (var acre in Terrain.Acres)
CB_Acre.Items.Add(acre.Name);
PG_Tile.SelectedObject = new TerrainTile();
CB_Acre.SelectedIndex = 0;
ReloadMap();
}
private int AcreIndex => CB_Acre.SelectedIndex;
private void ChangeAcre(object sender, EventArgs e) => LoadGrid(AcreIndex);
private void LoadGrid(int index)
{
for (int i = 0; i < TerrainManager.AcreSize; i++)
{
var b = Grid[i];
var tile = Terrain.GetAcreTile(index, i);
RefreshTile(b, tile);
}
UpdateArrowVisibility(index);
}
private void ReloadMap()
{
var img = TerrainSprite.CreateMap(Terrain);
PB_Map.Image = ImageUtil.ResizeImage(img, img.Width * 2, img.Height * 2);
}
private void UpdateArrowVisibility(int index)
{
B_Up.Enabled = index > TerrainManager.AcreWidth;
B_Down.Enabled = index < TerrainManager.AcreWidth * (TerrainManager.AcreHeight - 1);
B_Left.Enabled = index % TerrainManager.AcreWidth != 0;
B_Right.Enabled = index % TerrainManager.AcreWidth != TerrainManager.AcreWidth - 1;
}
private Button[] GenerateGrid(int w, int h)
{
var grid = new Button[w * h];
int i = 0;
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
var item = GetGridItem(index: i, x, y);
FLP_Tile.Controls.Add(item);
grid[i++] = item;
}
var last = grid[i - 1];
FLP_Tile.SetFlowBreak(last, true);
}
return grid;
}
private Button GetGridItem(int index, int x, int y)
{
var button = new Button
{
Name = index.ToString(),
Text = $"{index:000} ({x},{y})",
Size = new Size(SquareSize, SquareSize),
Padding = Padding.Empty,
Margin = Padding.Empty,
ContextMenuStrip = CM_Click,
FlatStyle = FlatStyle.Flat,
};
button.Click += (sender, args) =>
{
GetTile(button, out var tile, out var obj);
switch (ModifierKeys)
{
default: ViewTile(tile); return;
case Keys.Shift: SetTile(tile, obj); return;
case Keys.Alt: DeleteTile(tile, obj); return;
}
};
return button;
}
private void ViewTile(TerrainTile tile)
{
var pgt = (TerrainTile)PG_Tile.SelectedObject;
pgt.CopyFrom(tile);
PG_Tile.SelectedObject = pgt;
}
private void SetTile(TerrainTile tile, Control obj)
{
var pgt = (TerrainTile)PG_Tile.SelectedObject;
tile.CopyFrom(pgt);
RefreshTile(obj, tile);
ReloadMap();
}
private void DeleteTile(TerrainTile tile, Control obj)
{
tile.Clear();
RefreshTile(obj, tile);
ReloadMap();
}
private void B_Cancel_Click(object sender, EventArgs e) => Close();
private void B_Save_Click(object sender, EventArgs e)
{
SAV.Terrain = Terrain.Tiles;
Close();
}
private void Menu_View_Click(object sender, EventArgs e)
{
GetTile(sender, out var tile, out _);
ViewTile(tile);
}
private void Menu_Set_Click(object sender, EventArgs e)
{
GetTile(sender, out var tile, out var obj);
SetTile(tile, obj);
}
private void Menu_Reset_Click(object sender, EventArgs e)
{
GetTile(sender, out var tile, out var obj);
DeleteTile(tile, obj);
}
private void GetTile(object sender, out TerrainTile tile, out Button obj)
{
obj = WinFormsUtil.GetUnderlyingControl<Button>(sender) ?? throw new ArgumentNullException(nameof(sender));
var index = Array.IndexOf(Grid, obj);
if (index < 0)
throw new ArgumentException(nameof(Button));
tile = Terrain.GetAcreTile(AcreIndex, index);
}
private static void RefreshTile(Control button, TerrainTile tile)
{
button.Text = TerrainSprite.GetTileName(tile);
button.BackColor = TerrainSprite.GetTileColor(tile);
}
private void B_Up_Click(object sender, EventArgs e) => CB_Acre.SelectedIndex -= TerrainManager.AcreWidth;
private void B_Left_Click(object sender, EventArgs e) => --CB_Acre.SelectedIndex;
private void B_Right_Click(object sender, EventArgs e) => ++CB_Acre.SelectedIndex;
private void B_Down_Click(object sender, EventArgs e) => CB_Acre.SelectedIndex += TerrainManager.AcreWidth;
private void B_ZeroElevation_Click(object sender, EventArgs e)
{
foreach (var t in Terrain.Tiles)
t.Elevation = 0;
LoadGrid(AcreIndex);
System.Media.SystemSounds.Asterisk.Play();
}
private void B_ClearAll_Click(object sender, EventArgs e)
{
foreach (var t in Terrain.Tiles)
t.Clear();
LoadGrid(AcreIndex);
System.Media.SystemSounds.Asterisk.Play();
}
private void B_DumpAcre_Click(object sender, EventArgs e)
{
using var sfd = new SaveFileDialog
{
Filter = "New Horizons Terrain (*.nht)|*.nht|All files (*.*)|*.*",
FileName = $"{CB_Acre.Text}.nht",
};
if (sfd.ShowDialog() != DialogResult.OK)
return;
var path = sfd.FileName;
var acre = AcreIndex;
var data = Terrain.DumpAcre(acre);
File.WriteAllBytes(path, data);
}
private void B_DumpAllAcres_Click(object sender, EventArgs e)
{
using var sfd = new SaveFileDialog
{
Filter = "New Horizons Terrain (*.nht)|*.nht|All files (*.*)|*.*",
FileName = $"{CB_Acre.Text}.nht",
};
if (sfd.ShowDialog() != DialogResult.OK)
return;
var path = sfd.FileName;
var data = Terrain.DumpAllAcres();
File.WriteAllBytes(path, data);
}
private void B_ImportAcre_Click(object sender, EventArgs e)
{
using var ofd = new OpenFileDialog
{
Filter = "New Horizons Terrain (*.nht)|*.nht|All files (*.*)|*.*",
FileName = "terrainAcres.nht",
};
if (ofd.ShowDialog() != DialogResult.OK)
return;
var path = ofd.FileName;
var fi = new FileInfo(path);
const int expect = TerrainManager.AcreSize * TerrainTile.SIZE;
if (fi.Length != expect)
{
WinFormsUtil.Error($"Expected size (0x{expect:X}) != Input size (0x{fi.Length:X}", path);
return;
}
var data = File.ReadAllBytes(path);
Terrain.ImportAcre(AcreIndex, data);
LoadGrid(AcreIndex);
ReloadMap();
System.Media.SystemSounds.Asterisk.Play();
}
private void B_ImportAllAcres_Click(object sender, EventArgs e)
{
using var ofd = new OpenFileDialog
{
Filter = "New Horizons Terrain (*.nht)|*.nht|All files (*.*)|*.*",
FileName = "terrainAcres.nht",
};
if (ofd.ShowDialog() != DialogResult.OK)
return;
var path = ofd.FileName;
var fi = new FileInfo(path);
const int expect = TerrainManager.TileCount * TerrainTile.SIZE;
if (fi.Length != expect)
{
WinFormsUtil.Error($"Expected size (0x{expect:X}) != Input size (0x{fi.Length:X}", path);
return;
}
var data = File.ReadAllBytes(path);
Terrain.ImportAllAcres(data);
LoadGrid(AcreIndex);
ReloadMap();
System.Media.SystemSounds.Asterisk.Play();
}
private void Menu_SavePNG_Click(object sender, EventArgs e)
{
var pb = WinFormsUtil.GetUnderlyingControl<PictureBox>(sender);
if (pb?.Image == null)
{
WinFormsUtil.Alert("No picture loaded.");
return;
}
const string name = "map";
var bmp = pb.Image;
using var sfd = new SaveFileDialog
{
Filter = "png file (*.png)|*.png|All files (*.*)|*.*",
FileName = $"{name}.png",
};
if (sfd.ShowDialog() != DialogResult.OK)
return;
bmp.Save(sfd.FileName, ImageFormat.Png);
}
}
}

View File

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="CM_Click.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="CM_Picture.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>250, 17</value>
</metadata>
</root>