NHSE/NHSE.WinForms/Subforms/Map/FieldItemEditor.cs
2020-05-02 23:25:14 -07:00

595 lines
20 KiB
C#

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using NHSE.Core;
using NHSE.Sprites;
namespace NHSE.WinForms
{
public sealed partial class FieldItemEditor : Form
{
private readonly MainSave SAV;
private readonly MapManager Map;
private readonly MapViewer View;
private bool Loading;
private int SelectedBuildingIndex;
private int HoverX;
private int HoverY;
public FieldItemEditor(MainSave sav)
{
InitializeComponent();
this.TranslateInterface(GameInfo.CurrentLanguage);
SAV = sav;
Map = new MapManager(sav);
View = new MapViewer(Map);
Loading = true;
foreach (var acre in MapGrid.Acres)
CB_Acre.Items.Add(acre.Name);
NUD_PlazaX.Value = sav.PlazaX;
NUD_PlazaY.Value = sav.PlazaY;
foreach (var obj in Map.Buildings)
LB_Items.Items.Add(obj.ToString());
ReloadMapBackground();
PG_Tile.SelectedObject = new FieldItem();
PG_TerrainTile.SelectedObject = new TerrainTile();
LB_Items.SelectedIndex = 0;
CB_Acre.SelectedIndex = 0;
Loading = false;
LoadItemGridAcre();
}
private int AcreIndex => CB_Acre.SelectedIndex;
private void ChangeAcre(object sender, EventArgs e) => ChangeViewToAcre(AcreIndex);
private void ChangeViewToAcre(int acre)
{
View.SetViewToAcre(acre);
LoadItemGridAcre();
}
private void LoadItemGridAcre()
{
ReloadItems();
ReloadAcreBackground();
UpdateArrowVisibility();
}
private int GetItemTransparency() => ((int)(0xFF * TR_Transparency.Value / 100d) << 24) | 0x00FF_FFFF;
private void ReloadMapBackground()
{
PB_Map.BackgroundImage = View.GetBackgroundTerrain(SelectedBuildingIndex);
PB_Map.Invalidate(); // background image reassigning to same img doesn't redraw; force it
}
private void ReloadAcreBackground()
{
PB_Acre.BackgroundImage = View.GetBackgroundAcre(L_Coordinates.Font, (byte)TR_BuildingTransparency.Value, SelectedBuildingIndex);
PB_Acre.Invalidate(); // background image reassigning to same img doesn't redraw; force it
}
private void ReloadMapItemGrid() => PB_Map.Image = View.GetMapWithReticle(GetItemTransparency());
private void ReloadAcreItemGrid() => PB_Acre.Image = View.GetLayerAcre(GetItemTransparency());
private void ReloadItems()
{
ReloadAcreItemGrid();
ReloadMapItemGrid();
}
private void ReloadBuildingsTerrain()
{
ReloadAcreBackground();
ReloadMapBackground();
}
private void UpdateArrowVisibility()
{
B_Up.Enabled = View.CanUp;
B_Down.Enabled = View.CanDown;
B_Left.Enabled = View.CanLeft;
B_Right.Enabled = View.CanRight;
}
private void PB_Acre_MouseClick(object sender, MouseEventArgs e)
{
if (RB_Item.Checked)
OmniTile(e);
else if (RB_Terrain.Checked)
OmniTileTerrain(e);
}
private void OmniTile(MouseEventArgs e)
{
var tile = GetTile(Map.CurrentLayer, e, out var x, out var y);
OmniTile(tile, x, y);
}
private void OmniTileTerrain(MouseEventArgs e)
{
SetHoveredItem(e);
var x = View.X + HoverX;
var y = View.Y + HoverY;
var tile = Map.Terrain.GetTile(x / 2, y / 2);
OmniTileTerrain(tile);
}
private void OmniTile(FieldItem tile, int x, int y)
{
switch (ModifierKeys)
{
default:
ViewTile(tile);
return;
case Keys.Shift:
SetTile(tile, x, y);
return;
case Keys.Alt:
DeleteTile(tile, x, y);
return;
}
}
private void OmniTileTerrain(TerrainTile tile)
{
switch (ModifierKeys)
{
default:
ViewTile(tile);
return;
case Keys.Shift:
SetTile(tile);
return;
case Keys.Alt:
DeleteTile(tile);
return;
}
}
private FieldItem GetTile(FieldItemLayer layer, MouseEventArgs e, out int x, out int y)
{
SetHoveredItem(e);
return layer.GetTile(x = View.X + HoverX, y = View.Y + HoverY);
}
private void SetHoveredItem(MouseEventArgs e)
{
// Mouse event may fire with a slightly too large x/y; clamp just in case.
HoverX = (e.X / View.AcreScale) & 0x1F;
HoverY = (e.Y / View.AcreScale) & 0x1F;
}
private void PB_Acre_MouseMove(object sender, MouseEventArgs e)
{
var l = Map.CurrentLayer;
var oldTile = l.GetTile(View.X + HoverX, View.Y + HoverY);
var tile = GetTile(l, e, out _, out _);
if (tile == oldTile)
return;
var str = GameInfo.Strings;
var name = str.GetItemName(tile);
TT_Hover.SetToolTip(PB_Acre, name);
}
private void ViewTile(FieldItem tile)
{
var pgt = (FieldItem)PG_Tile.SelectedObject;
pgt.CopyFrom(tile);
PG_Tile.SelectedObject = pgt;
TC_Editor.SelectedTab = Tab_Item;
}
private void ViewTile(TerrainTile tile)
{
var pgt = (TerrainTile)PG_TerrainTile.SelectedObject;
pgt.CopyFrom(tile);
PG_TerrainTile.SelectedObject = pgt;
TC_Editor.SelectedTab = Tab_Terrain;
}
private void SetTile(FieldItem tile, int x, int y)
{
var l = Map.CurrentLayer;
var pgt = (FieldItem)PG_Tile.SelectedObject;
var permission = l.IsOccupied(pgt, x, y);
switch (permission)
{
case FieldItemPermission.OutOfBounds:
case FieldItemPermission.Collision when CHK_NoOverwrite.Checked:
System.Media.SystemSounds.Asterisk.Play();
return;
}
// Clean up original placed data
if (tile.IsRoot && CHK_AutoExtension.Checked)
l.DeleteExtensionTiles(tile, x, y);
// Set new placed data
if (pgt.IsRoot && CHK_AutoExtension.Checked)
l.SetExtensionTiles(pgt, x, y);
tile.CopyFrom(pgt);
ReloadItems();
}
private void SetTile(TerrainTile tile)
{
var pgt = (TerrainTile)PG_TerrainTile.SelectedObject;
tile.CopyFrom(pgt);
ReloadBuildingsTerrain();
}
private void DeleteTile(FieldItem tile, int x, int y)
{
if (tile.IsRoot && CHK_AutoExtension.Checked)
Map.CurrentLayer.DeleteExtensionTiles(tile, x, y);
tile.Delete();
ReloadItems();
}
private void DeleteTile(TerrainTile tile)
{
tile.Clear();
ReloadBuildingsTerrain();
}
private void B_Cancel_Click(object sender, EventArgs e) => Close();
private void B_Save_Click(object sender, EventArgs e)
{
var all = ArrayUtil.ConcatAll(Map.Items.Layer1.Tiles, Map.Items.Layer2.Tiles);
SAV.SetFieldItems(all);
SAV.SetTerrainTiles(Map.Terrain.Tiles);
SAV.Buildings = Map.Buildings;
SAV.PlazaX = Map.PlazaX;
SAV.PlazaY = Map.PlazaY;
Close();
}
private void Menu_View_Click(object sender, EventArgs e)
{
var x = View.X + HoverX;
var y = View.Y + HoverY;
if (RB_Item.Checked)
{
var tile = Map.CurrentLayer.GetTile(x, y);
ViewTile(tile);
}
else if (RB_Terrain.Checked)
{
var tile = Map.Terrain.GetTile(x / 2, y / 2);
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 = Map.CurrentLayer.GetTile(x, y);
SetTile(tile, x, y);
}
else if (RB_Terrain.Checked)
{
var tile = Map.Terrain.GetTile(x / 2, y / 2);
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 = Map.CurrentLayer.GetTile(x, y);
DeleteTile(tile, x, y);
}
else if (RB_Terrain.Checked)
{
var tile = Map.Terrain.GetTile(x / 2, y / 2);
DeleteTile(tile);
}
}
private void B_Up_Click(object sender, EventArgs e)
{
if (ModifierKeys == Keys.Shift)
CB_Acre.SelectedIndex -= MapGrid.AcreWidth;
else if (View.ArrowUp())
LoadItemGridAcre();
}
private void B_Left_Click(object sender, EventArgs e)
{
if (ModifierKeys == Keys.Shift)
--CB_Acre.SelectedIndex;
else if (View.ArrowLeft())
LoadItemGridAcre();
}
private void B_Right_Click(object sender, EventArgs e)
{
if (ModifierKeys == Keys.Shift)
++CB_Acre.SelectedIndex;
else if (View.ArrowRight())
LoadItemGridAcre();
}
private void B_Down_Click(object sender, EventArgs e)
{
if (ModifierKeys == Keys.Shift)
CB_Acre.SelectedIndex += MapGrid.AcreWidth;
else if (View.ArrowDown())
LoadItemGridAcre();
}
private void B_DumpAcre_Click(object sender, EventArgs e) => MapDumpHelper.DumpLayerAcreSingle(Map.CurrentLayer, AcreIndex, CB_Acre.Text, (int)NUD_Layer.Value);
private void B_DumpAllAcres_Click(object sender, EventArgs e) => MapDumpHelper.DumpLayerAcreAll(Map.CurrentLayer);
private void B_ImportAcre_Click(object sender, EventArgs e)
{
var layer = Map.CurrentLayer;
if (!MapDumpHelper.ImportToLayerAcreSingle(layer, AcreIndex, CB_Acre.Text, (int)NUD_Layer.Value))
return;
ChangeViewToAcre(AcreIndex);
System.Media.SystemSounds.Asterisk.Play();
}
private void B_ImportAllAcres_Click(object sender, EventArgs e)
{
if (!MapDumpHelper.ImportToLayerAcreAll(Map.CurrentLayer))
return;
ChangeViewToAcre(AcreIndex);
System.Media.SystemSounds.Asterisk.Play();
}
private void B_DumpBuildings_Click(object sender, EventArgs e) => MapDumpHelper.DumpBuildings(Map.Buildings);
private void B_ImportBuildings_Click(object sender, EventArgs e)
{
if (!MapDumpHelper.ImportBuildings(Map.Buildings))
return;
for (int i = 0; i < Map.Buildings.Count; i++)
LB_Items.Items[i] = Map.Buildings[i].ToString();
LB_Items.SelectedIndex = 0;
System.Media.SystemSounds.Asterisk.Play();
ReloadBuildingsTerrain();
}
private void B_DumpTerrainAcre_Click(object sender, EventArgs e) => MapDumpHelper.DumpTerrainAcre(Map.Terrain, AcreIndex, CB_Acre.Text);
private void B_DumpTerrainAll_Click(object sender, EventArgs e) => MapDumpHelper.DumpTerrainAll(Map.Terrain);
private void B_ImportTerrainAcre_Click(object sender, EventArgs e)
{
if (!MapDumpHelper.ImportTerrainAcre(Map.Terrain, AcreIndex, CB_Acre.Text))
return;
ChangeViewToAcre(AcreIndex);
System.Media.SystemSounds.Asterisk.Play();
}
private void B_ImportTerrainAll_Click(object sender, EventArgs e)
{
if (!MapDumpHelper.ImportTerrainAll(Map.Terrain))
return;
ChangeViewToAcre(AcreIndex);
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(MessageStrings.MsgNoPictureLoaded);
return;
}
const string name = "map";
var bmp = FieldItemSpriteDrawer.GetBitmapItemLayer(Map.Items.Layer1);
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);
}
private void PB_Map_MouseDown(object sender, MouseEventArgs e) => ClickMapAt(e, true);
private void ClickMapAt(MouseEventArgs e, bool skipLagCheck)
{
var layer = Map.Items.Layer1;
int mX = e.X;
int mY = e.Y;
bool centerReticle = CHK_SnapToAcre.Checked;
View.GetViewAnchorCoordinates(mX, mY, out var x, out var y, centerReticle);
x &= 0xFFFE;
y &= 0xFFFE;
var acre = layer.GetAcre(x, y);
bool sameAcre = AcreIndex == acre;
if (!skipLagCheck)
{
if (CHK_SnapToAcre.Checked)
{
if (sameAcre)
return;
}
else
{
const int delta = 0; // disabled = 0
var dx = Math.Abs(View.X - x);
var dy = Math.Abs(View.Y - y);
if (dx <= delta && dy <= delta && !sameAcre)
return;
}
}
if (!CHK_SnapToAcre.Checked)
{
View.SetViewTo(x, y);
LoadItemGridAcre();
return;
}
if (!sameAcre)
CB_Acre.SelectedIndex = acre;
}
private void PB_Map_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
ClickMapAt(e, false);
}
else
{
View.GetCursorCoordinates(e.X, e.Y, out var x, out var y);
L_Coordinates.Text = $"({x:000},{y:000}) = (0x{x:X2},0x{y:X2})";
}
}
private void NUD_Layer_ValueChanged(object sender, EventArgs e)
{
Map.MapLayer = (int) NUD_Layer.Value - 1;
LoadItemGridAcre();
}
private void Remove(ToolStripItem sender, Func<int, int, int, int, int> removal)
{
bool wholeMap = ModifierKeys == Keys.Shift;
string q = string.Format(MessageStrings.MsgFieldItemRemoveAsk, sender.Text);
var question = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, q);
if (question != DialogResult.Yes)
return;
int count = View.RemoveFieldItems(removal, wholeMap);
if (count == 0)
{
WinFormsUtil.Alert(MessageStrings.MsgFieldItemRemoveNone);
return;
}
LoadItemGridAcre();
WinFormsUtil.Alert(string.Format(MessageStrings.MsgFieldItemRemoveCount, count));
}
private void B_RemoveAllWeeds_Click(object sender, EventArgs e) => Remove(B_RemoveAllWeeds, Map.CurrentLayer.RemoveAllWeeds);
private void B_FillHoles_Click(object sender, EventArgs e) => Remove(B_FillHoles, Map.CurrentLayer.RemoveAllHoles);
private void B_RemovePlants_Click(object sender, EventArgs e) => Remove(B_RemovePlants, Map.CurrentLayer.RemoveAllPlants);
private void B_RemoveFences_Click(object sender, EventArgs e) => Remove(B_RemoveFences, Map.CurrentLayer.RemoveAllFences);
private void B_RemoveObjects_Click(object sender, EventArgs e) => Remove(B_RemoveObjects, Map.CurrentLayer.RemoveAllObjects);
private void B_RemoveAll_Click(object sender, EventArgs e) => Remove(B_RemoveAll, Map.CurrentLayer.RemoveAll);
private void B_RemovePlacedItems_Click(object sender, EventArgs e) => Remove(B_RemovePlacedItems, Map.CurrentLayer.RemoveAllPlacedItems);
private void PG_Tile_PropertyValueChanged(object s, PropertyValueChangedEventArgs e) => PG_Tile.SelectedObject = PG_Tile.SelectedObject;
private static void ShowContextMenuBelow(ToolStripDropDown c, Control n) => c.Show(n.PointToScreen(new Point(0, n.Height)));
private void B_RemoveItemDropDown_Click(object sender, EventArgs e) => ShowContextMenuBelow(CM_Remove, B_RemoveItemDropDown);
private void B_DumpLoadField_Click(object sender, EventArgs e) => ShowContextMenuBelow(CM_DLField, B_DumpLoadField);
private void B_DumpLoadTerrain_Click(object sender, EventArgs e) => ShowContextMenuBelow(CM_DLTerrain, B_DumpLoadTerrain);
private void B_DumpLoadBuildings_Click(object sender, EventArgs e) => ShowContextMenuBelow(CM_DLBuilding, B_DumpLoadBuildings);
private void B_ModifyAllTerrain_Click(object sender, EventArgs e) => ShowContextMenuBelow(CM_Terrain, B_ModifyAllTerrain);
private void TR_Transparency_Scroll(object sender, EventArgs e) => ReloadItems();
private void TR_BuildingTransparency_Scroll(object sender, EventArgs e) => ReloadBuildingsTerrain();
#region Buildings
private void B_Help_Click(object sender, EventArgs e)
{
using var form = new BuildingHelp();
form.ShowDialog();
}
private void NUD_PlazaX_ValueChanged(object sender, EventArgs e)
{
if (Loading)
return;
Map.PlazaX = (uint) NUD_PlazaX.Value;
ReloadBuildingsTerrain();
}
private void NUD_PlazaY_ValueChanged(object sender, EventArgs e)
{
if (Loading)
return;
Map.PlazaY = (uint) NUD_PlazaY.Value;
ReloadBuildingsTerrain();
}
private void LB_Items_SelectedIndexChanged(object sender, EventArgs e)
{
if (LB_Items.SelectedIndex < 0)
return;
LoadIndex(LB_Items.SelectedIndex);
ReloadBuildingsTerrain();
}
private void LoadIndex(int index)
{
Loading = true;
SelectedBuildingIndex = index;
var b = Map.Buildings[index];
NUD_BuildingType.Value = (int)b.BuildingType;
NUD_X.Value = b.X;
NUD_Y.Value = b.Y;
NUD_Angle.Value = b.Angle;
NUD_Bit.Value = b.Bit;
NUD_Type.Value = b.Type;
NUD_TypeArg.Value = b.TypeArg;
NUD_UniqueID.Value = b.UniqueID;
Loading = false;
}
private void NUD_BuildingType_ValueChanged(object sender, EventArgs e)
{
if (Loading || !(sender is NumericUpDown n))
return;
var b = Map.Buildings[SelectedBuildingIndex];
if (sender == NUD_BuildingType)
b.BuildingType = (BuildingType)n.Value;
else if (sender == NUD_X)
b.X = (ushort)n.Value;
else if (sender == NUD_Y)
b.Y = (ushort)n.Value;
else if (sender == NUD_Angle)
b.Angle = (byte)n.Value;
else if (sender == NUD_Bit)
b.Bit = (sbyte)n.Value;
else if (sender == NUD_Type)
b.Type = (ushort)n.Value;
else if (sender == NUD_TypeArg)
b.TypeArg = (byte)n.Value;
else if (sender == NUD_UniqueID)
b.UniqueID = (ushort)n.Value;
LB_Items.Items[SelectedBuildingIndex] = Map.Buildings[SelectedBuildingIndex].ToString();
ReloadBuildingsTerrain();
}
#endregion
}
}