Add layer2->layer1 check (isSupported), add logic for isActive

isActive: indicates if the tile is animated (eg: bonfire has fire or is extinguished)
isSupported: layer2 tile checks beneath it (layer1) to see if there's something to hold it up on that layer (sanity check)

Dunno how to handle the isActive set-on-save. I saw there was an isActive flag in my plaza, assumedly left there from a radio or something. NHSE does not change flag state at this time.
This commit is contained in:
Kurt 2020-05-03 16:29:28 -07:00
parent 8edf0a6ec4
commit 4a49caf6da
5 changed files with 85 additions and 23 deletions

View File

@ -182,8 +182,19 @@ public void SetAcreBytes(byte[] data)
public TerrainTile[] GetTerrainTiles() => TerrainTile.GetArray(Data.Slice(Offsets.LandMakingMap, MapGrid.MapTileCount16x16 * TerrainTile.SIZE));
public void SetTerrainTiles(IReadOnlyList<TerrainTile> array) => TerrainTile.SetArray(array).CopyTo(Data, Offsets.LandMakingMap);
public FieldItem[] GetFieldItems() => FieldItem.GetArray(Data.Slice(Offsets.FieldItem, MapGrid.MapTileCount32x32 * FieldItem.SIZE * 2));
public void SetFieldItems(IReadOnlyList<FieldItem> array) => FieldItem.SetArray(array).CopyTo(Data, Offsets.FieldItem);
private const int FieldItemLayerSize = MapGrid.MapTileCount32x32 * FieldItem.SIZE;
private const int FieldItemFlagSize = MapGrid.MapTileCount32x32 / 8; // bitflags
private int FieldItemLayer1 => Offsets.FieldItem;
private int FieldItemLayer2 => Offsets.FieldItem + FieldItemLayerSize;
public int FieldItemFlag1 => Offsets.FieldItem + (FieldItemLayerSize * 2);
public int FieldItemFlag2 => Offsets.FieldItem + (FieldItemLayerSize * 2) + FieldItemFlagSize;
public FieldItem[] GetFieldItemLayer1() => FieldItem.GetArray(Data.Slice(FieldItemLayer1, FieldItemLayerSize));
public void SetFieldItemLayer1(IReadOnlyList<FieldItem> array) => FieldItem.SetArray(array).CopyTo(Data, FieldItemLayer1);
public FieldItem[] GetFieldItemLayer2() => FieldItem.GetArray(Data.Slice(FieldItemLayer2, FieldItemLayerSize));
public void SetFieldItemLayer2(IReadOnlyList<FieldItem> array) => FieldItem.SetArray(array).CopyTo(Data, FieldItemLayer2);
public ushort OutsideFieldTemplateUniqueId
{

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace NHSE.Core
{
@ -7,30 +8,67 @@ public class FieldItemManager
public readonly FieldItemLayer Layer1;
public readonly FieldItemLayer Layer2;
public FieldItemManager(FieldItem[] layer1, FieldItem[] layer2)
public readonly MainSave SAV;
public FieldItemManager(MainSave sav)
{
Layer1 = new FieldItemLayer(layer1);
Layer2 = new FieldItemLayer(layer2);
Layer1 = new FieldItemLayer(sav.GetFieldItemLayer1());
Layer2 = new FieldItemLayer(sav.GetFieldItemLayer2());
SAV = sav;
}
public FieldItemManager(FieldItem[] items, int layers = 2) // Two halves; split and assign.
: this(items.Slice(0, items.Length / 2), items.SliceEnd(items.Length / 2))
public void Save()
{
if (layers != 2)
throw new ArgumentOutOfRangeException(nameof(layers));
SAV.SetFieldItemLayer1(Layer1.Tiles);
SAV.SetFieldItemLayer2(Layer2.Tiles);
SetTileActiveFlags(Layer1, SAV.FieldItemFlag1);
SetTileActiveFlags(Layer2, SAV.FieldItemFlag2);
}
public FieldItem GetTile(int layer, int x, int y) => GetLayer(layer).GetTile(x, y);
public FieldItem GetTile(int layer, int acreX, int acreY, int gridX, int gridY) => GetLayer(layer).GetTile(acreX, acreY, gridX, gridY);
public FieldItem GetAcreTile(int layer, int acreIndex, int tileIndex) => GetLayer(layer).GetAcreTile(acreIndex, tileIndex);
public FieldItemLayer GetLayer(int layer)
public List<string> GetUnsupportedTiles()
{
if ((uint) layer >= 2)
throw new ArgumentOutOfRangeException(nameof(layer));
return layer == 0 ? Layer1 : Layer2;
var result = new List<string>();
for (int x = 0; x < 224; x++)
{
for (int y = 0; y < 192; y++)
{
var tile = Layer2.GetTile(x, y);
if (tile.IsNone)
continue;
var support = Layer1.GetTile(x, y);
if (!support.IsNone)
continue; // dunno how to check if the tile can actually have an item put on top of it...
result.Add($"{x:000},{y:000}");
}
}
return result;
}
private void SetTileActiveFlags(FieldItemLayer tiles, int ofs)
{
// Although the Tiles are arranged y-column (y-x) based, the 'isActive' flags are arranged x-row (x-y) based.
// We can turn the isActive flag off if the item is not a root or the item cannot be animated.
for (int x = 0; x < 224; x++)
{
for (int y = 0; y < 192; y++)
{
var tile = tiles.GetTile(x, y);
var isActive = IsActive(ofs, x, y);
if (!isActive)
continue;
bool empty = tile.IsNone;
if (empty)
Debug.WriteLine($"Flag at ({x},{y}) is not a root object.");
}
}
}
public bool IsActive(bool baseLayer, int x, int y) => IsActive(baseLayer ? SAV.FieldItemFlag1 : SAV.FieldItemFlag2, x, y);
private bool IsActive(int ofs, int x, int y) => FlagUtil.GetFlag(SAV.Data, ofs, (y * 224) + x);
public bool IsOccupied(int x, int y) => !Layer1.GetTile(x, y).IsNone || !Layer2.GetTile(x, y).IsNone;
}
}

View File

@ -10,7 +10,7 @@ public class MapManager : MapTerrainStructure
public MapManager(MainSave sav) : base(sav)
{
Items = new FieldItemManager(sav.GetFieldItems());
Items = new FieldItemManager(sav);
}
public FieldItemLayer CurrentLayer => MapLayer == 0 ? Items.Layer1 : Items.Layer2;

View File

@ -176,12 +176,13 @@ 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 _);
var tile = GetTile(l, e, out var x, out var y);
if (tile == oldTile)
return;
var str = GameInfo.Strings;
var name = str.GetItemName(tile);
TT_Hover.SetToolTip(PB_Acre, name);
SetCoordinateText(x, y);
}
private void ViewTile(FieldItem tile)
@ -251,8 +252,17 @@ private void DeleteTile(TerrainTile tile)
private void B_Save_Click(object sender, EventArgs e)
{
var all = ArrayUtil.ConcatAll(Map.Items.Layer1.Tiles, Map.Items.Layer2.Tiles);
SAV.SetFieldItems(all);
var unsupported = Map.Items.GetUnsupportedTiles();
if (unsupported.Count != 0)
{
var err = MessageStrings.MsgFieldItemUnsupportedLayer2Tile;
var ask = MessageStrings.MsgAskContinue;
var prompt = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, err, ask);
if (prompt != DialogResult.Yes)
return;
}
Map.Items.Save();
SAV.SetTerrainTiles(Map.Terrain.Tiles);
SAV.Buildings = Map.Buildings;
SAV.PlazaX = Map.PlazaX;
@ -468,10 +478,12 @@ private void PB_Map_MouseMove(object sender, MouseEventArgs e)
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})";
SetCoordinateText(x, y);
}
}
private void SetCoordinateText(int x, int 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;

View File

@ -32,6 +32,7 @@ public static class MessageStrings
public static string MsgFieldItemRemoveAsk { get; set; } = "Are you sure you want to remove {0}?";
public static string MsgFieldItemRemoveNone { get; set; } = "Nothing removed (none found).";
public static string MsgFieldItemRemoveCount { get; set; } = "Removed {0} from the map.";
public static string MsgFieldItemUnsupportedLayer2Tile { get; set; } = "Unsupported Layer2 items detected.";
public static string MsgSysBotInfo { get; set; } = "This SysBot reads and writes RAM directly to your game when called to Read/Write.";
public static string MsgSysBotRequired { get; set; } = "Using this functionality requires the sys-botbase sysmodule running on the console. Your console must be on the same network as the PC running this program.";