Fixed gen2 map importer Bounds calculations
This commit is contained in:
@@ -26,6 +26,19 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
|||||||
{
|
{
|
||||||
public abstract class ImportGen2MapCommand
|
public abstract class ImportGen2MapCommand
|
||||||
{
|
{
|
||||||
|
// The top few map rows on the original games are visible but not part of the interactable map area.
|
||||||
|
// These are imported as being outside the map bounds but within the shroud visible margin,
|
||||||
|
// which requires some extra padding to make the PPos calculations work.
|
||||||
|
// The first value is added to the left, right, and bottom edges. The second value is added to the top.
|
||||||
|
// There are 6 tiles in the top uninteractable region (3 in og TS, double for OpenRA).
|
||||||
|
// So far testing hasn't shown the need for a left/right/bottom uninteractable margin except in cases of odd elevation, which affects us but not the original games.
|
||||||
|
protected virtual int2 UninteractableMargin { get; } = new int2(0, 6);
|
||||||
|
|
||||||
|
// Having the top cordon equal the maximum possible elevation for the mod is important!
|
||||||
|
// It needs to be equal to the top row's maximum elevation because there may be an actor there that will later crash the game.
|
||||||
|
// But since we need to create the map's canvas before importing tile data (like elevation), we opt for the maximum possible elevation to be safe.
|
||||||
|
protected virtual (int Left, int Top, int Right, int Bottom) Cordon { get; } = (1, 16, 1, 4);
|
||||||
|
|
||||||
protected abstract Dictionary<byte, string> OverlayToActor { get; }
|
protected abstract Dictionary<byte, string> OverlayToActor { get; }
|
||||||
|
|
||||||
protected abstract Dictionary<byte, Size> OverlayShapes { get; }
|
protected abstract Dictionary<byte, Size> OverlayShapes { get; }
|
||||||
@@ -52,16 +65,17 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
|||||||
var tileset = mapSection.GetValue("Theater", "");
|
var tileset = mapSection.GetValue("Theater", "");
|
||||||
var iniSize = mapSection.GetValue("Size", "0, 0, 0, 0").Split(',').Select(int.Parse).ToArray();
|
var iniSize = mapSection.GetValue("Size", "0, 0, 0, 0").Split(',').Select(int.Parse).ToArray();
|
||||||
var iniBounds = mapSection.GetValue("LocalSize", "0, 0, 0, 0").Split(',').Select(int.Parse).ToArray();
|
var iniBounds = mapSection.GetValue("LocalSize", "0, 0, 0, 0").Split(',').Select(int.Parse).ToArray();
|
||||||
var size = new Size(iniSize[2], 2 * iniSize[3]);
|
|
||||||
|
|
||||||
if (!utility.ModData.DefaultTerrainInfo.TryGetValue(tileset, out var terrainInfo))
|
if (!utility.ModData.DefaultTerrainInfo.TryGetValue(tileset, out var terrainInfo))
|
||||||
throw new InvalidDataException($"Unknown tileset {tileset}");
|
throw new InvalidDataException($"Unknown tileset {tileset}");
|
||||||
|
|
||||||
var map = new Map(Game.ModData, terrainInfo, size.Width, size.Height)
|
var usedAreaSize = new Size(iniSize[2], 2 * iniSize[3]);
|
||||||
|
var mapCanvasSize = new Size(usedAreaSize.Width + Cordon.Left + Cordon.Right, usedAreaSize.Height + Cordon.Top + Cordon.Bottom);
|
||||||
|
|
||||||
|
var map = new Map(Game.ModData, terrainInfo, mapCanvasSize.Width, mapCanvasSize.Height)
|
||||||
{
|
{
|
||||||
Title = basic.GetValue("Name", Path.GetFileNameWithoutExtension(filename)),
|
Title = basic.GetValue("Name", Path.GetFileNameWithoutExtension(filename)),
|
||||||
Author = "Westwood Studios",
|
Author = "Westwood Studios",
|
||||||
Bounds = new Rectangle(iniBounds[0], iniBounds[1], iniBounds[2], 2 * iniBounds[3] + 2 * iniBounds[1]),
|
|
||||||
RequiresMod = utility.ModData.Manifest.Id
|
RequiresMod = utility.ModData.Manifest.Id
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -80,6 +94,9 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
|||||||
var mapPlayers = new MapPlayers(map.Rules, spawnCount);
|
var mapPlayers = new MapPlayers(map.Rules, spawnCount);
|
||||||
map.PlayerDefinitions = mapPlayers.ToMiniYaml();
|
map.PlayerDefinitions = mapPlayers.ToMiniYaml();
|
||||||
|
|
||||||
|
// This needs to be called after ReadTiles because it depends on terrain elevation.
|
||||||
|
SetInteractableBounds(map, iniBounds);
|
||||||
|
|
||||||
var dest = Path.GetFileNameWithoutExtension(args[1]) + ".oramap";
|
var dest = Path.GetFileNameWithoutExtension(args[1]) + ".oramap";
|
||||||
map.Save(ZipFileLoader.Create(dest));
|
map.Save(ZipFileLoader.Create(dest));
|
||||||
Console.WriteLine(dest + " saved.");
|
Console.WriteLine(dest + " saved.");
|
||||||
@@ -107,18 +124,15 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
|||||||
var z = mf.ReadUInt8();
|
var z = mf.ReadUInt8();
|
||||||
/*var zero2 = */mf.ReadUInt8();
|
/*var zero2 = */mf.ReadUInt8();
|
||||||
|
|
||||||
var dx = rx - ry + fullSize.X - 1;
|
var uv = ToMPos(rx, ry, fullSize.X);
|
||||||
var dy = rx + ry - fullSize.X - 1;
|
|
||||||
var mapCell = new MPos(dx / 2, dy);
|
|
||||||
var cell = mapCell.ToCPos(map);
|
|
||||||
|
|
||||||
if (map.Tiles.Contains(cell))
|
if (map.Tiles.Contains(uv))
|
||||||
{
|
{
|
||||||
if (!terrainInfo.Templates.ContainsKey(tilenum))
|
if (!terrainInfo.Templates.ContainsKey(tilenum))
|
||||||
tilenum = subtile = 0;
|
tilenum = subtile = 0;
|
||||||
|
|
||||||
map.Tiles[cell] = new TerrainTile(tilenum, subtile);
|
map.Tiles[uv] = new TerrainTile(tilenum, subtile);
|
||||||
map.Height[cell] = z;
|
map.Height[uv] = z;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,7 +160,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
|||||||
var dx = (ushort)x;
|
var dx = (ushort)x;
|
||||||
var dy = (ushort)(y * 2 + x % 2);
|
var dy = (ushort)(y * 2 + x % 2);
|
||||||
|
|
||||||
var uv = new MPos(dx / 2, dy);
|
var uv = ToMPos(dx, dy);
|
||||||
var rx = (ushort)((dx + dy) / 2 + 1);
|
var rx = (ushort)((dx + dy) / 2 + 1);
|
||||||
var ry = (ushort)(dy - rx + fullSize.X + 1);
|
var ry = (ushort)(dy - rx + fullSize.X + 1);
|
||||||
|
|
||||||
@@ -192,9 +206,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
|||||||
var pos = int.Parse(kv.Value);
|
var pos = int.Parse(kv.Value);
|
||||||
var ry = pos / 1000;
|
var ry = pos / 1000;
|
||||||
var rx = pos - ry * 1000;
|
var rx = pos - ry * 1000;
|
||||||
var dx = rx - ry + fullSize.X - 1;
|
var cell = ToMPos(rx, ry, fullSize.X).ToCPos(map);
|
||||||
var dy = rx + ry - fullSize.X - 1;
|
|
||||||
var cell = new MPos(dx / 2, dy).ToCPos(map);
|
|
||||||
|
|
||||||
var ar = new ActorReference((!int.TryParse(kv.Key, out var wpindex) || wpindex > 7) ? "waypoint" : "mpspawn")
|
var ar = new ActorReference((!int.TryParse(kv.Key, out var wpindex) || wpindex > 7) ? "waypoint" : "mpspawn")
|
||||||
{
|
{
|
||||||
@@ -214,9 +226,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
|||||||
var pos = int.Parse(kv.Key);
|
var pos = int.Parse(kv.Key);
|
||||||
var ry = pos / 1000;
|
var ry = pos / 1000;
|
||||||
var rx = pos - ry * 1000;
|
var rx = pos - ry * 1000;
|
||||||
var dx = rx - ry + fullSize.X - 1;
|
var cell = ToMPos(rx, ry, fullSize.X).ToCPos(map);
|
||||||
var dy = rx + ry - fullSize.X - 1;
|
|
||||||
var cell = new MPos(dx / 2, dy).ToCPos(map);
|
|
||||||
var name = kv.Value.ToLowerInvariant();
|
var name = kv.Value.ToLowerInvariant();
|
||||||
|
|
||||||
var ar = new ActorReference(name)
|
var ar = new ActorReference(name)
|
||||||
@@ -253,9 +263,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
|||||||
var ry = int.Parse(entries[4]);
|
var ry = int.Parse(entries[4]);
|
||||||
var facing = (byte)(224 - byte.Parse(entries[type == "Infantry" ? 7 : 5]));
|
var facing = (byte)(224 - byte.Parse(entries[type == "Infantry" ? 7 : 5]));
|
||||||
|
|
||||||
var dx = rx - ry + fullSize.X - 1;
|
var cell = ToMPos(rx, ry, fullSize.X).ToCPos(map);
|
||||||
var dy = rx + ry - fullSize.X - 1;
|
|
||||||
var cell = new MPos(dx / 2, dy).ToCPos(map);
|
|
||||||
|
|
||||||
var ar = new ActorReference(name)
|
var ar = new ActorReference(name)
|
||||||
{
|
{
|
||||||
@@ -380,6 +388,26 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void SetInteractableBounds(Map map, int[] iniBounds)
|
||||||
|
{
|
||||||
|
// Apply cordon cells + the ini-specified map margin.
|
||||||
|
var left = Cordon.Left + iniBounds[0];
|
||||||
|
var top = Cordon.Top + 2 * iniBounds[1];
|
||||||
|
|
||||||
|
var playableAreaWidth = iniBounds[2];
|
||||||
|
var playableAreaHeight = 2 * iniBounds[3] - UninteractableMargin.Y;
|
||||||
|
|
||||||
|
// Black magic, don't ask. Non-flat maps seen to require additional height padding.
|
||||||
|
var unknownHeightPadding = map.Height.Max() == 0 ? 0 : 8;
|
||||||
|
|
||||||
|
// Calculate Bounds edge tile coordinates.
|
||||||
|
// Reduce bottom and right by 1 because map.SetBounds() increases them.
|
||||||
|
var topLeft = new PPos(left, top);
|
||||||
|
var bottomRight = new PPos(playableAreaWidth + left - 1, playableAreaHeight + unknownHeightPadding + top - 1);
|
||||||
|
|
||||||
|
map.SetBounds(topLeft, bottomRight);
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual bool TryHandleOverlayToActorInner(CPos cell, byte[] overlayPack, CellLayer<int> overlayIndex, byte overlayType, out ActorReference actorReference)
|
protected virtual bool TryHandleOverlayToActorInner(CPos cell, byte[] overlayPack, CellLayer<int> overlayIndex, byte overlayType, out ActorReference actorReference)
|
||||||
{
|
{
|
||||||
actorReference = null;
|
actorReference = null;
|
||||||
@@ -486,6 +514,24 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert TS relative position to OpenRA MPos, accounting for map cordons.
|
||||||
|
/// </summary>
|
||||||
|
protected MPos ToMPos(int dx, int dy)
|
||||||
|
{
|
||||||
|
return new MPos(Cordon.Left + (dx + Cordon.Top % 2) / 2, Cordon.Top + dy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert TS relative position to OpenRA MPos, accounting for map cordons.
|
||||||
|
/// </summary>
|
||||||
|
protected MPos ToMPos(int rx, int ry, int mapWidthWithoutCordon)
|
||||||
|
{
|
||||||
|
var dx = rx - ry + mapWidthWithoutCordon - 1;
|
||||||
|
var dy = rx + ry - mapWidthWithoutCordon - 1;
|
||||||
|
return ToMPos(dx, dy);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user