Add basic Path Tiling for TS

This introduces basic Path Tiler map editor tool functionality to TS
with some segmented MultiBrush definitions. The included MultiBrush
definitions cover both the Temperate and Snow tilesets's Beach, Cliff,
and WaterCliff tiles.

MultiBrush now incorporates Height and Ramp information. Some
associated Terraformer utilities are updated with the API surface
change.

There are some known limitations that are not addressed in this PR:
- The path laying UI doesn't account for the map's current heights.
- The preview does not use a correct height or Z-ordering.
This commit is contained in:
Ashley Newson
2025-11-08 19:55:24 +00:00
committed by Gustas Kažukauskas
parent e652f95be9
commit cde5208c54
5 changed files with 2084 additions and 30 deletions

View File

@@ -302,18 +302,24 @@ namespace OpenRA.Mods.Common.MapGenerator
public readonly byte MinIndex;
public readonly byte MaxIndex;
public TileRange(ushort type, byte minIndex, byte maxIndex)
// Height is relative, so can be negative.
public readonly short HeightOffset;
public readonly byte Ramp;
public TileRange(ushort type, byte minIndex, byte maxIndex, short heightOffset, byte ramp)
{
Type = type;
MinIndex = minIndex;
MaxIndex = maxIndex;
HeightOffset = heightOffset;
Ramp = ramp;
}
public TileRange(ushort type, byte index)
: this(type, index, index) { }
public TileRange(ushort type, byte index, short heightOffset, byte ramp)
: this(type, index, index, heightOffset, ramp) { }
public TileRange(TerrainTile tile)
: this(tile.Type, tile.Index) { }
public TileRange(TerrainTile tile, short heightOffset, byte ramp)
: this(tile.Type, tile.Index, heightOffset, ramp) { }
/// <summary>Pick a non-randomized tile.</summary>
public TerrainTile DefaultTile => new(Type, MinIndex);
@@ -328,6 +334,12 @@ namespace OpenRA.Mods.Common.MapGenerator
return new TerrainTile(Type, (byte)random.Next(MinIndex, MaxIndex + 1));
}
/// <summary>Create a copy of this TileRange, adding an additional heightOffset.</summary>
public TileRange WithHeightOffset(short heightOffset)
{
return new(Type, MinIndex, MaxIndex, (short)(HeightOffset + heightOffset), Ramp);
}
}
public int Weight;
@@ -353,6 +365,11 @@ namespace OpenRA.Mods.Common.MapGenerator
/// </summary>
public CVec FirstCell => GetShape()[0];
public IEnumerable<(CVec XY, short Height, byte Ramp)> GetHeightsAndRamps()
{
return tiles.Select(t => (t.XY, t.TileRange.HeightOffset, t.TileRange.Ramp));
}
public Replaceability Contract()
{
var hasTiles = tiles.Count != 0;
@@ -461,19 +478,32 @@ namespace OpenRA.Mods.Common.MapGenerator
/// default, it will be auto-offset such that the first tile is
/// under (0, 0).
/// </summary>
public MultiBrush WithTemplate(Map map, ushort templateId, CVec offset)
public MultiBrush WithTemplate(Map map, ushort templateId, CVec offset, short heightOffset = 0)
{
var tileset = map.Rules.TerrainInfo as ITemplatedTerrainInfo;
if (!tileset.Templates.TryGetValue(templateId, out var templateInfo))
throw new ArgumentException($"Map's tileset does not contain template with ID {templateId}.");
return WithTemplate(templateInfo, offset);
var itti = (ITemplatedTerrainInfo)map.Rules.TerrainInfo;
return WithTemplate(itti, templateId, offset, heightOffset);
}
public MultiBrush WithTemplate(TerrainTemplateInfo templateInfo, CVec offset)
public MultiBrush WithTemplate(ITemplatedTerrainInfo itti, ushort templateId, CVec offset, short heightOffset = 0)
{
if (!itti.Templates.TryGetValue(templateId, out var templateInfo))
throw new ArgumentException($"Tileset does not contain template with ID {templateId}.");
return WithTemplate(templateInfo, offset, heightOffset);
}
public MultiBrush WithTemplate(TerrainTemplateInfo templateInfo, CVec offset, short heightOffset = 0)
{
if (templateInfo.PickAny)
{
tiles.Add((offset, new(templateInfo.Id, 0, (byte)(templateInfo.TilesCount - 1))));
// Assume that single tiles have equal height.
tiles.Add((
offset,
new(
templateInfo.Id,
0,
(byte)(templateInfo.TilesCount - 1),
(short)(templateInfo[0].Height + heightOffset),
templateInfo[0].RampType)));
}
else
{
@@ -482,7 +512,13 @@ namespace OpenRA.Mods.Common.MapGenerator
{
var i = y * templateInfo.Size.X + x;
if (templateInfo[i] != null)
tiles.Add((new CVec(x, y) + offset, new(templateInfo.Id, (byte)i)));
tiles.Add((
new CVec(x, y) + offset,
new(
templateInfo.Id,
(byte)i,
(short)(templateInfo[i].Height + heightOffset),
templateInfo[i].RampType)));
}
}
@@ -494,9 +530,9 @@ namespace OpenRA.Mods.Common.MapGenerator
/// Add a single tile, optionally with a given offset. By default, it
/// will be positioned under (0, 0).
/// </summary>
public MultiBrush WithTile(TerrainTile tile, CVec offset)
public MultiBrush WithTile(TerrainTile tile, CVec offset, short heightOffset = 0, byte ramp = 0)
{
tiles.Add((offset, new(tile)));
tiles.Add((offset, new(tile, heightOffset, ramp)));
shape = null;
return this;
}
@@ -518,7 +554,7 @@ namespace OpenRA.Mods.Common.MapGenerator
if (Area == 0)
throw new InvalidOperationException("No area");
foreach (var xy in shape)
tiles.Add((xy, new(tile)));
tiles.Add((xy, new(tile, 0, 0)));
return this;
}
@@ -545,7 +581,7 @@ namespace OpenRA.Mods.Common.MapGenerator
/// Add the tiles and actors from another MultiBrush into this one at a given offset.
/// (Does not copy segments.)
/// </summary>
public void MergeFrom(MultiBrush other, CVec at, MapGridType mapGridType)
public void MergeFrom(MultiBrush other, CVec at, MapGridType mapGridType, short heightOffset = 0)
{
foreach (var original in other.actorPlans)
{
@@ -555,13 +591,14 @@ namespace OpenRA.Mods.Common.MapGenerator
}
foreach (var (xy, tile) in other.tiles)
tiles.Add((xy + at, tile));
tiles.Add((xy + at, tile.WithHeightOffset(heightOffset)));
shape = null;
}
/// <summary>
/// <para>Paint tiles onto the map and/or add actors to actorPlans at the given location.</para>
/// <para>A specific height offset can be supplied, else one will be assumed from the map.</para>
/// <para>contract specifies whether tiles or actors are allowed to be painted.</para>
/// <para>An optional MersenneTwister can be provided to vary randomizable elements.</para>
/// <para>If nothing could be painted, throws ArgumentException.</para>
@@ -570,9 +607,27 @@ namespace OpenRA.Mods.Common.MapGenerator
Map map,
List<ActorPlan> actorPlans,
CPos paintAt,
short? heightOffset,
Replaceability contract,
MersenneTwister random)
{
short finalHeightOffset = 0;
if (heightOffset.HasValue)
{
finalHeightOffset = heightOffset.Value;
}
else
{
foreach (var cpos in Shape)
{
if (map.Height.Contains(paintAt + cpos))
{
finalHeightOffset = map.Height[paintAt + cpos];
break;
}
}
}
switch (contract)
{
case Replaceability.None:
@@ -581,7 +636,7 @@ namespace OpenRA.Mods.Common.MapGenerator
if (this.actorPlans.Count == 0 && tiles.Count == 0)
throw new ArgumentException("Cannot paint: no tiles or actors");
PaintTiles(map, paintAt, random);
PaintTiles(map, paintAt, finalHeightOffset, random);
PaintActors(map, actorPlans, paintAt);
break;
@@ -589,7 +644,7 @@ namespace OpenRA.Mods.Common.MapGenerator
if (tiles.Count == 0)
throw new ArgumentException("Cannot paint: no tiles");
PaintTiles(map, paintAt, random);
PaintTiles(map, paintAt, finalHeightOffset, random);
PaintActors(map, actorPlans, paintAt);
break;
case Replaceability.Actor:
@@ -601,13 +656,17 @@ namespace OpenRA.Mods.Common.MapGenerator
}
}
void PaintTiles(Map map, CPos paintAt, MersenneTwister random)
void PaintTiles(Map map, CPos paintAt, short heightOffset, MersenneTwister random)
{
foreach (var (xy, tile) in tiles)
{
var mpos = (paintAt + xy).ToMPos(map);
if (map.Tiles.Contains(mpos))
{
// map.Ramp does not need to be updated here.
map.Tiles[mpos] = tile.Pick(random);
map.Height[mpos] = (byte)Math.Clamp(tile.HeightOffset + heightOffset, byte.MinValue, byte.MaxValue);
}
}
}
@@ -633,7 +692,8 @@ namespace OpenRA.Mods.Common.MapGenerator
CellLayer<Replaceability> replace,
IReadOnlyList<MultiBrush> availableBrushes,
MersenneTwister random,
bool alwaysPreferLargerBrushes = false)
bool alwaysPreferLargerBrushes = false,
short? heightOffset = null)
{
var brushesByAreaDict = new Dictionary<int, List<MultiBrush>>();
foreach (var brush in availableBrushes)
@@ -745,7 +805,7 @@ namespace OpenRA.Mods.Common.MapGenerator
var paintAt = mpos.ToCPos(map) - brush.FirstCell;
var contract = ReserveShape(paintAt, brush.Shape, brush.Contract());
if (contract != Replaceability.None)
brush.Paint(map, actorPlans, paintAt, contract, random);
brush.Paint(map, actorPlans, paintAt, heightOffset, contract, random);
remainingQuota -= brushArea;
if (remainingQuota <= 0)
@@ -763,7 +823,8 @@ namespace OpenRA.Mods.Common.MapGenerator
public EditorBlitSource ToEditorBlitSource(
WorldRenderer worldRenderer,
MersenneTwister random,
PlayerReference defaultActorOwner = null)
PlayerReference defaultActorOwner = null,
short heightOffset = 0)
{
var world = worldRenderer.World;
var map = world.Map;
@@ -812,10 +873,10 @@ namespace OpenRA.Mods.Common.MapGenerator
tiles
.Where(t => map.Tiles.Contains(CPos.Zero + t.XY))
.DistinctBy(t => t.XY)
.Select(t => (t.XY, Tile: t.TileRange.Pick(random)))
.Select(t => (t.XY, Tile: t.TileRange.Pick(random), t.TileRange.HeightOffset))
.ToDictionary(
t => CPos.Zero + t.XY,
t => new BlitTile(t.Tile, default, null, map.Height[CPos.Zero + t.XY]));
t => new BlitTile(t.Tile, default, null, (byte)Math.Clamp(heightOffset + t.HeightOffset, byte.MinValue, byte.MaxValue)));
return new EditorBlitSource(
cellRegion,

View File

@@ -951,9 +951,10 @@ namespace OpenRA.Mods.Common.MapGenerator
/// <summary>Wrapper around MultiBrush.Paint for path tiling results.</summary>
public void PaintTiling(
MersenneTwister random,
MultiBrush brush)
MultiBrush brush,
short? heightOffset = null)
{
brush.Paint(Map, ActorPlans, CPos.Zero, MultiBrush.Replaceability.Any, random);
brush.Paint(Map, ActorPlans, CPos.Zero, heightOffset, MultiBrush.Replaceability.Any, random);
}
/// <summary>
@@ -1591,13 +1592,15 @@ namespace OpenRA.Mods.Common.MapGenerator
/// <param name="outside">If non-null, these MultiBrushes are painted over outside regions.</param>
/// <param name="inside">If non-null, these MultiBrushes are painted over inside regions.</param>
/// <param name="replaceMask">Optional replaceability constraints for filling. Ignored for path tiling.</param>
/// <param name="heightOffset">Optional explicit height to paint at. Otherwise a height is picked automatically.</param>
public CellLayer<Side> PaintLoopsAndFill(
MersenneTwister random,
IReadOnlyList<TilingPath> tilingPaths,
Side fallback,
IReadOnlyList<MultiBrush> outside,
IReadOnlyList<MultiBrush> inside,
CellLayer<MultiBrush.Replaceability> replaceMask = null)
CellLayer<MultiBrush.Replaceability> replaceMask = null,
short? heightOffset = null)
{
CheckHasMapShapeOrNull(replaceMask);
@@ -1612,7 +1615,7 @@ namespace OpenRA.Mods.Common.MapGenerator
}
foreach (var tiling in tilings)
tiling.Paint(Map, ActorPlans, CPos.Zero, MultiBrush.Replaceability.Any, random);
tiling.Paint(Map, ActorPlans, CPos.Zero, heightOffset, MultiBrush.Replaceability.Any, random);
if (inside == null && outside == null)
return null;

View File

@@ -430,3 +430,7 @@ EditorWorld:
Palette: ra
Alpha: 0.35
MarkerLayerOverlay:
TilingPathTool:
DefaultStart: Clear
DefaultInner: Beach
DefaultEnd: Clear

File diff suppressed because it is too large Load Diff

View File

@@ -20475,3 +20475,956 @@ Templates:
MaxColor: 585349
ZOffset: -12
ZRamp: 0
MultiBrushCollections:
Segmented:
MultiBrush@Cliff_60:
Template: 60
Segment:
Start: Cliff.L
End: Cliff.U
Points: 2,2, 1,2, 1,1, 0,1, 0,0
MultiBrush@Cliff_61:
Template: 61
Segment:
Start: Cliff.L
End: Cliff.U
Points: 1,1, 0,1, 0,0
MultiBrush@Cliff_62:
Template: 62
Segment:
Start: Cliff.L
End: Cliff.L
Points: 2,2, 1,2, 1,1, 0,1
MultiBrush@Cliff_63:
Template: 63
Segment:
Start: Cliff.L
End: Cliff.L
Points: 2,2, 1,2, 1,1, 0,1
MultiBrush@Cliff_64:
Template: 64
Segment:
Start: Cliff.L
End: Cliff.L
Points: 2,1, 1,1, 0,1
MultiBrush@Cliff_65:
Template: 65
Segment:
Start: Cliff.L
End: Cliff.L
Points: 2,1, 1,1, 0,1
MultiBrush@Cliff_66:
Template: 66
Segment:
Start: Cliff.L
End: Cliff.L
Points: 2,1, 1,1, 0,1
MultiBrush@Cliff_67:
Template: 67
Segment:
Start: Cliff.L
End: Cliff.L
Points: 1,1, 0,1
MultiBrush@Cliff_68:
Template: 68
Segment:
Start: Cliff.MaybeCorner.D
End: Cliff.MaybeCorner.L
Inner: Cliff
Points: 2,0, 1,0, 1,1, 0,1, 0,2
MultiBrush@Cliff_69:
Template: 69
Segment:
Start: Cliff.MaybeCorner.D
End: Cliff.MaybeCorner.L
Inner: Cliff
Points: 2,0, 1,0, 1,1, 0,1, 0,2
MultiBrush@Cliff_70:
Template: 70
Segment:
Start: Cliff.MaybeCorner.D
End: Cliff.MaybeCorner.L
Inner: Cliff
Points: 2,0, 1,0, 1,1, 0,1, 0,2
MultiBrush@Cliff_71:
Template: 71
Segment:
Start: Cliff.MaybeCorner.D
End: Cliff.MaybeCorner.L
Inner: Cliff
Points: 1,0, 0,0, 0,1
MultiBrush@Cliff_72:
Template: 72
Segment:
Start: Cliff.MaybeCorner.D
End: Cliff.MaybeCorner.L
Inner: Cliff
Points: 1,0, 0,0, 0,1
MultiBrush@Cliff_73:
Template: 73
Segment:
Start: Cliff.MaybeCorner.D
End: Cliff.MaybeCorner.L
Inner: Cliff
Points: 1,0, 0,0, 0,1
MultiBrush@Cliff_74:
Template: 74
Segment:
Start: Cliff.D
End: Cliff.D
Points: 1,0, 1,1, 1,2
MultiBrush@Cliff_75:
Template: 75
Segment:
Start: Cliff.D
End: Cliff.D
Points: 1,0, 1,1, 1,2
MultiBrush@Cliff_76:
Template: 76
Segment:
Start: Cliff.D
End: Cliff.D
Points: 1,0, 1,1, 1,2
MultiBrush@Cliff_77:
Template: 77
Segment:
Start: Cliff.D
End: Cliff.D
Points: 1,0, 1,1
MultiBrush@Cliff_78:
Template: 78
Segment:
Start: Cliff.D
End: Cliff.D
Points: 1,0, 1,1, 2,1, 2,2
MultiBrush@Cliff_79:
Template: 79
Segment:
Start: Cliff.D
End: Cliff.D
Points: 1,0, 1,1, 2,1, 2,2
MultiBrush@Cliff_80:
Template: 80
Segment:
Start: Cliff.R
End: Cliff.D
Points: 0,0, 1,0, 1,1, 2,1, 2,2
MultiBrush@Cliff_81:
Template: 81
Segment:
Start: Cliff.R
End: Cliff.D
Points: 0,0, 1,0, 1,1
MultiBrush@Cliff_82:
Template: 82
Segment:
Start: Cliff.R
End: Cliff.R
Points: 0,0, 1,0, 2,0
MultiBrush@Cliff_83:
Template: 83
Segment:
Start: Cliff.R
End: Cliff.R
Points: 0,0, 1,0, 2,0
MultiBrush@Cliff_84:
Template: 84
Segment:
Start: Cliff.R
End: Cliff.R
Points: 0,0, 1,0, 2,0
MultiBrush@Cliff_85:
Template: 85
Segment:
Start: Cliff.R
End: Cliff.R
Points: 0,0, 1,0
MultiBrush@Cliff_86:
Template: 86
Segment:
Start: Cliff.U
End: Cliff.R
Points: 0,2, 0,1, 0,0, 1,0, 2,0
MultiBrush@Cliff_87:
Template: 87
Segment:
Start: Cliff.U
End: Cliff.R
Points: 0,2, 0,1, 1,1, 1,0, 2,0
MultiBrush@Cliff_88:
Template: 88
Segment:
Start: Cliff.U
End: Cliff.R
Points: 0,1, 0,0, 1,0
MultiBrush@Cliff_89:
Template: 89
Segment:
Start: Cliff.U
End: Cliff.R
Points: 0,1, 0,0, 1,0
MultiBrush@Cliff_90:
Template: 90
Segment:
Start: Cliff.R
End: Cliff.U
Points: 0,1, 0,0, 1,0
MultiBrush@Cliff_91:
Template: 91
Segment:
Start: Cliff.R
End: Cliff.U
Points: 0,1, 1,1, 1,0
MultiBrush@Cliff_92:
Template: 92
Segment:
Start: Cliff.R
End: Cliff.U
Points: 0,0
MultiBrush@Cliff_93_CornerLD:
Template: 93
Segment:
Start: Cliff.MaybeCorner.L
End: Cliff.MaybeCorner.D
Inner: Cliff
Points: 1,1
MultiBrush@Cliff_93_CornerL:
Template: 93
Segment:
Start: Cliff.MaybeCorner.L
End: Cliff.D
Inner: Cliff
Points: 1,1
MultiBrush@Cliff_93_CornerD:
Template: 93
Segment:
Start: Cliff.L
End: Cliff.MaybeCorner.D
Inner: Cliff
Points: 1,1
MultiBrush@Cliff_94:
Template: 94
Segment:
Start: Cliff.U
End: Cliff.U
Points: 0,2, 0,1, 0,0
MultiBrush@Cliff_95:
Template: 95
Segment:
Start: Cliff.U
End: Cliff.U
Points: 0,2, 0,1, 0,0
MultiBrush@Cliff_96:
Template: 96
Segment:
Start: Cliff.U
End: Cliff.U
Points: 0,2, 0,1, 0,0
MultiBrush@Cliff_97:
Template: 97
Segment:
Start: Cliff.U
End: Cliff.U
Points: 0,1, 0,0
MultiBrush@Cliff_98:
Template: 98
Segment:
Start: Cliff.U
End: Cliff.L
Points: 0,1
MultiBrush@Cliff_99:
Template: 99
Segment:
Start: Cliff.D
End: Cliff.R
Points: 1,0
MultiBrush@Cliff_Interface_FromCornerD:
Segment:
Start: Cliff.D
End: Cliff.MaybeCorner.D
Inner: Cliff
Points: 0,0
MultiBrush@Cliff_Interface_ToCornerL:
Segment:
Start: Cliff.MaybeCorner.L
End: Cliff.L
Inner: Cliff
Points: 0,0
MultiBrush@Shore_108:
Template: 108
Segment:
Start: Shore.L
End: Shore.L
Points: 2,1, 1,1, 0,1
MultiBrush@Shore_109:
Template: 109
Segment:
Start: Shore.L
End: Shore.L
Points: 2,1, 1,1, 0,1
MultiBrush@Shore_110:
Template: 110
Segment:
Start: Shore.L
End: Shore.L
Points: 2,1, 1,1, 0,1
MultiBrush@Shore_111:
Template: 111
Segment:
Start: Shore.L
End: Shore.L
Points: 1,1, 0,1
MultiBrush@Shore_112:
Template: 112
Segment:
Start: Shore.L
End: Shore.L
Points: 2,2, 1,2, 1,1, 0,1
MultiBrush@Shore_113:
Template: 113
Segment:
Start: Shore.L
End: Shore.L
Points: 2,1, 1,1, 1,2, 0,2
MultiBrush@Shore_114:
Template: 114
Segment:
Start: Shore.D
End: Shore.L
Points: 1,0, 1,1, 0,1
MultiBrush@Shore_115:
Template: 115
Segment:
Start: Shore.D
End: Shore.L
Points: 1,0, 1,1, 0,1
MultiBrush@Shore_116:
Template: 116
Segment:
Start: Shore.D
End: Shore.D
Points: 1,0, 1,1, 1,2
MultiBrush@Shore_117:
Template: 117
Segment:
Start: Shore.D
End: Shore.D
Points: 1,0, 1,1, 1,2
MultiBrush@Shore_118:
Template: 118
Segment:
Start: Shore.D
End: Shore.D
Points: 1,0, 1,1, 1,2
MultiBrush@Shore_119:
Template: 119
Segment:
Start: Shore.D
End: Shore.D
Points: 1,0, 1,1
MultiBrush@Shore_120:
Template: 120
Segment:
Start: Shore.D
End: Shore.D
Points: 2,0, 2,1, 1,1, 1,2
MultiBrush@Shore_121:
Template: 121
Segment:
Start: Shore.D
End: Shore.D
Points: 1,0, 1,1, 1,2, 2,2
MultiBrush@Shore_122:
Template: 122
Segment:
Start: Shore.R
End: Shore.D
Points: 0,1, 1,1, 1,2
MultiBrush@Shore_123:
Template: 123
Segment:
Start: Shore.R
End: Shore.D
Points: 0,1, 1,1, 1,2
MultiBrush@Shore_124:
Template: 124
Segment:
Start: Shore.R
End: Shore.R
Points: 0,1, 1,1, 2,1
MultiBrush@Shore_125:
Template: 125
Segment:
Start: Shore.R
End: Shore.R
Points: 0,1, 1,1, 2,1
MultiBrush@Shore_126:
Template: 126
Segment:
Start: Shore.R
End: Shore.R
Points: 0,1, 1,1, 2,1
MultiBrush@Shore_127:
Template: 127
Segment:
Start: Shore.R
End: Shore.R
Points: 0,1, 1,1
MultiBrush@Shore_128:
Template: 128
Segment:
Start: Shore.R
End: Shore.R
Points: 0,2, 1,2, 1,1, 2,1
MultiBrush@Shore_129:
Template: 129
Segment:
Start: Shore.R
End: Shore.R
Points: 0,1, 1,1, 1,2, 2,2
MultiBrush@Shore_130:
Template: 130
Segment:
Start: Shore.U
End: Shore.R
Points: 1,2, 1,1, 2,1
MultiBrush@Shore_131:
Template: 131
Segment:
Start: Shore.U
End: Shore.R
Points: 1,2, 1,1, 2,1
MultiBrush@Shore_132:
Template: 132
Segment:
Start: Shore.U
End: Shore.U
Points: 1,2, 1,1, 1,0
MultiBrush@Shore_133:
Template: 133
Segment:
Start: Shore.U
End: Shore.U
Points: 1,2, 1,1, 1,0
MultiBrush@Shore_134:
Template: 134
Segment:
Start: Shore.U
End: Shore.U
Points: 1,2, 1,1, 1,0
MultiBrush@Shore_135:
Template: 135
Segment:
Start: Shore.U
End: Shore.U
Points: 1,1, 1,0
MultiBrush@Shore_136:
Template: 136
Segment:
Start: Shore.U
End: Shore.U
Points: 2,2, 2,1, 1,1, 1,0
MultiBrush@Shore_137:
Template: 137
Segment:
Start: Shore.U
End: Shore.U
Points: 1,2, 1,1, 2,1, 2,0
MultiBrush@Shore_138:
Template: 138
Segment:
Start: Shore.L
End: Shore.U
Points: 2,1, 1,1, 1,0
MultiBrush@Shore_139:
Template: 139
Segment:
Start: Shore.L
End: Shore.U
Points: 2,1, 1,1, 1,0
MultiBrush@Shore_140:
Template: 140
Segment:
Start: Shore.L
End: Shore.D
Points: 2,1, 1,1, 1,2
MultiBrush@Shore_141:
Template: 141
Segment:
Start: Shore.L
End: Shore.D
Points: 2,1, 1,1, 1,2
MultiBrush@Shore_142:
Template: 142
Segment:
Start: Shore.D
End: Shore.R
Points: 1,0, 1,1, 2,1
MultiBrush@Shore_143:
Template: 143
Segment:
Start: Shore.D
End: Shore.R
Points: 1,0, 1,1, 2,1
MultiBrush@Shore_144:
Template: 144
Segment:
Start: Shore.R
End: Shore.U
Points: 0,1, 1,1, 1,0
MultiBrush@Shore_145:
Template: 145
Segment:
Start: Shore.R
End: Shore.U
Points: 0,1, 1,1, 1,0
MultiBrush@Shore_146:
Template: 146
Segment:
Start: Shore.U
End: Shore.L
Points: 1,2, 1,1, 0,1
MultiBrush@Shore_147:
Template: 147
Segment:
Start: Shore.U
End: Shore.L
Points: 1,2, 1,1, 0,1
MultiBrush@Shore_148:
Template: 148
Segment:
Start: Shore.D
End: Shore.D
Points: 5,1, 5,2, 4,2, 3,2, 2,2, 1,2, 1,3, 1,4
MultiBrush@Shore_149:
Template: 149
Segment:
Start: Shore.D
End: Shore.D
Points: 8,2, 8,3, 8,4, 7,4, 6,4, 5,4, 4,4, 4,3, 3,3, 3,4, 3,5
MultiBrush@WaterCliff_167:
Template: 167
Segment:
Start: WaterCliff.L
End: WaterCliff.U
Points: 2,2, 1,2, 1,1, 0,1, 0,0
MultiBrush@WaterCliff_168:
Template: 168
Segment:
Start: WaterCliff.L
End: WaterCliff.U
Points: 1,1, 0,1, 0,0
MultiBrush@WaterCliff_169:
Template: 169
Segment:
Start: WaterCliff.L
End: WaterCliff.L
Points: 2,2, 1,2, 1,1, 0,1
MultiBrush@WaterCliff_170:
Template: 170
Segment:
Start: WaterCliff.L
End: WaterCliff.L
Points: 2,2, 1,2, 1,1, 0,1
MultiBrush@WaterCliff_171:
Template: 171
Segment:
Start: WaterCliff.L
End: WaterCliff.L
Points: 2,1, 1,1, 0,1
MultiBrush@WaterCliff_172:
Template: 172
Segment:
Start: WaterCliff.L
End: WaterCliff.L
Points: 2,1, 1,1, 0,1
MultiBrush@WaterCliff_173:
Template: 173
Segment:
Start: WaterCliff.L
End: WaterCliff.L
Points: 2,1, 1,1, 0,1
MultiBrush@WaterCliff_174:
Template: 174
Segment:
Start: WaterCliff.L
End: WaterCliff.L
Points: 1,1, 0,1
MultiBrush@WaterCliff_175:
Template: 175
Segment:
Start: WaterCliff.MaybeCorner.D
End: WaterCliff.MaybeCorner.L
Inner: WaterCliff
Points: 2,0, 1,0, 1,1, 0,1, 0,2
MultiBrush@WaterCliff_176:
Template: 176
Segment:
Start: WaterCliff.MaybeCorner.D
End: WaterCliff.MaybeCorner.L
Inner: WaterCliff
Points: 2,0, 1,0, 1,1, 0,1, 0,2
MultiBrush@WaterCliff_177:
Template: 177
Segment:
Start: WaterCliff.MaybeCorner.D
End: WaterCliff.MaybeCorner.L
Inner: WaterCliff
Points: 2,0, 1,0, 1,1, 0,1, 0,2
MultiBrush@WaterCliff_178:
Template: 178
Segment:
Start: WaterCliff.MaybeCorner.D
End: WaterCliff.MaybeCorner.L
Inner: WaterCliff
Points: 1,0, 0,0, 0,1
MultiBrush@WaterCliff_179:
Template: 179
Segment:
Start: WaterCliff.MaybeCorner.D
End: WaterCliff.MaybeCorner.L
Inner: WaterCliff
Points: 1,0, 0,0, 0,1
MultiBrush@WaterCliff_180:
Template: 180
Segment:
Start: WaterCliff.MaybeCorner.D
End: WaterCliff.MaybeCorner.L
Inner: WaterCliff
Points: 1,0, 0,0, 0,1
MultiBrush@WaterCliff_181:
Template: 181
Segment:
Start: WaterCliff.D
End: WaterCliff.D
Points: 1,0, 1,1, 1,2
MultiBrush@WaterCliff_182:
Template: 182
Segment:
Start: WaterCliff.D
End: WaterCliff.D
Points: 1,0, 1,1, 1,2
MultiBrush@WaterCliff_183:
Template: 183
Segment:
Start: WaterCliff.D
End: WaterCliff.D
Points: 1,0, 1,1, 1,2
MultiBrush@WaterCliff_184:
Template: 184
Segment:
Start: WaterCliff.D
End: WaterCliff.D
Points: 1,0, 1,1
MultiBrush@WaterCliff_185:
Template: 185
Segment:
Start: WaterCliff.D
End: WaterCliff.D
Points: 1,0, 1,1, 2,1, 2,2
MultiBrush@WaterCliff_186:
Template: 186
Segment:
Start: WaterCliff.D
End: WaterCliff.D
Points: 1,0, 1,1, 2,1, 2,2
MultiBrush@WaterCliff_187:
Template: 187
Segment:
Start: WaterCliff.R
End: WaterCliff.D
Points: 0,0, 1,0, 1,1, 2,1, 2,2
MultiBrush@WaterCliff_188:
Template: 188
Segment:
Start: WaterCliff.R
End: WaterCliff.D
Points: 0,0, 1,0, 1,1
MultiBrush@WaterCliff_82:
Template: 82
Segment:
Start: WaterCliff.R
End: WaterCliff.R
Points: 0,0, 1,0, 2,0
MultiBrush@WaterCliff_83:
Template: 83
Segment:
Start: WaterCliff.R
End: WaterCliff.R
Points: 0,0, 1,0, 2,0
MultiBrush@WaterCliff_84:
Template: 84
Segment:
Start: WaterCliff.R
End: WaterCliff.R
Points: 0,0, 1,0, 2,0
MultiBrush@WaterCliff_85:
Template: 85
Segment:
Start: WaterCliff.R
End: WaterCliff.R
Points: 0,0, 1,0
MultiBrush@WaterCliff_86:
Template: 86
Segment:
Start: WaterCliff.U
End: WaterCliff.R
Points: 0,2, 0,1, 0,0, 1,0, 2,0
MultiBrush@WaterCliff_87:
Template: 87
Segment:
Start: WaterCliff.U
End: WaterCliff.R
Points: 0,2, 0,1, 1,1, 1,0, 2,0
MultiBrush@WaterCliff_88:
Template: 88
Segment:
Start: WaterCliff.U
End: WaterCliff.R
Points: 0,1, 0,0, 1,0
MultiBrush@WaterCliff_89:
Template: 89
Segment:
Start: WaterCliff.U
End: WaterCliff.R
Points: 0,1, 0,0, 1,0
MultiBrush@WaterCliff_90:
Template: 90
Segment:
Start: WaterCliff.U
End: WaterCliff.U
Points: 0,2, 0,1, 0,0, 1,0
MultiBrush@WaterCliff_91:
Template: 91
Segment:
Start: WaterCliff.R
End: WaterCliff.U
Points: 0,1, 1,1, 1,0
MultiBrush@WaterCliff_92:
Template: 92
Segment:
Start: WaterCliff.R
End: WaterCliff.U
Points: 0,0
MultiBrush@WaterCliff_93_CornerLD:
Template: 93
Segment:
Start: WaterCliff.MaybeCorner.L
End: WaterCliff.MaybeCorner.D
Inner: WaterCliff
Points: 1,1
MultiBrush@WaterCliff_93_CornerL:
Template: 93
Segment:
Start: WaterCliff.MaybeCorner.L
End: WaterCliff.D
Inner: WaterCliff
Points: 1,1
MultiBrush@WaterCliff_93_CornerD:
Template: 93
Segment:
Start: WaterCliff.L
End: WaterCliff.MaybeCorner.D
Inner: WaterCliff
Points: 1,1
MultiBrush@WaterCliff_94:
Template: 94
Segment:
Start: WaterCliff.U
End: WaterCliff.U
Points: 0,2, 0,1, 0,0
MultiBrush@WaterCliff_95:
Template: 95
Segment:
Start: WaterCliff.U
End: WaterCliff.U
Points: 0,2, 0,1, 0,0
MultiBrush@WaterCliff_96:
Template: 96
Segment:
Start: WaterCliff.U
End: WaterCliff.U
Points: 0,2, 0,1, 0,0
MultiBrush@WaterCliff_97:
Template: 97
Segment:
Start: WaterCliff.U
End: WaterCliff.U
Points: 0,1, 0,0
MultiBrush@WaterCliff_98:
Template: 98
Segment:
Start: WaterCliff.U
End: WaterCliff.L
Points: 0,1
MultiBrush@WaterCliff_99:
Template: 99
Segment:
Start: WaterCliff.D
End: WaterCliff.R
Points: 1,0
MultiBrush@WaterCliff_Interface_FromCornerD:
Segment:
Start: WaterCliff.D
End: WaterCliff.MaybeCorner.D
Inner: WaterCliff
Points: 0,0
MultiBrush@WaterCliff_Interface_ToCornerL:
Segment:
Start: WaterCliff.MaybeCorner.L
End: WaterCliff.L
Inner: WaterCliff
Points: 0,0
MultiBrush@Cliff_403:
Template: 403
Segment:
Start: Clear.L
Inner: Cliff
End: Cliff.L
Points: 3,2, 2,2, 1,2, 0,2
MultiBrush@Cliff_404:
Template: 404
Segment:
Start: Cliff.L
Inner: Cliff
End: Clear.L
Points: 3,2, 2,2, 1,2, 0,2
MultiBrush@Cliff_405:
Template: 405
Segment:
Start: Clear.D
Inner: Cliff
End: Cliff.D
Points: 2,0, 2,1, 2,2, 2,3
MultiBrush@Cliff_406:
Template: 406
Segment:
Start: Cliff.D
Inner: Cliff
End: Clear.D
Points: 2,0, 2,1, 2,2, 2,3
MultiBrush@Cliff_407:
Template: 407
Segment:
Start: Cliff.R
Inner: Cliff
End: Clear.R
Points: 0,2, 1,2, 2,2, 3,2
MultiBrush@Cliff_408_409:
Template: 408
Template: 409
Offset: 1,2
Segment:
Start: Clear.R
Inner: Cliff
End: Cliff.R
Points: 0,2, 1,2, 2,2, 3,2
MultiBrush@Cliff_410:
Template: 410
Segment:
Start: Clear.U
Inner: Cliff
End: Cliff.U
Points: 2,3, 2,2, 2,1, 2,0
MultiBrush@Cliff_411_412:
Template: 411
Template: 412
Offset: 2,1
Segment:
Start: Cliff.U
Inner: Cliff
End: Clear.U
Points: 2,3, 2,2, 2,1, 2,0
MultiBrush@Shore_WaterCliff_61_97_132_135_403:
Template: 61
Offset: 2,1
Template: 97
Offset: 2,0
Template: 132
Offset: 0,1
Template: 135
Template: 403
Offset: 3,0
Segment:
Start: Shore.U
End: WaterCliff.U
Points: 1,3, 1,2, 2,2, 2,1, 2,0
MultiBrush@Shore_WaterCliff_111_192_405:
Template: 111
Offset: 3,2
Template: 192
Offset: 1,3
Template: 405
Segment:
Start: Shore.L
End: WaterCliff.L
Points: 4,3, 3,3, 2,3, 2,4, 1,4
MultiBrush@Shore_WaterCliff_119_188_408_409:
Template: 119
Offset: 3,1
Template: 188
Offset: 3,2
Template: 408
Template: 409
Offset: 1,2
Segment:
Start: Shore.D
End: WaterCliff.D
Points: 4,1, 4,2, 4,3
MultiBrush@Shore_WaterCliff_88_127_410:
Template: 88
Offset: 2,0
Template: 127
Template: 410
Offset: 0,1
Segment:
Start: Shore.R
End: WaterCliff.R
Points: 0,1, 1,1, 2,1, 2,0, 3,0
MultiBrush@Shore_WaterCliff_119_193_404:
Template: 119
Offset: 2,3
Template: 193
Offset: 3,1
Template: 404
Segment:
Start: WaterCliff.D
End: Shore.D
Points: 4,1, 4,2, 3,2, 3,3, 3,4
MultiBrush@Shore_WaterCliff_81_85_124_127_406:
Template: 81
Offset: 1,2
Template: 85
Offset: 0,2
Template: 124
Offset: 1,0
Template: 127
Template: 406
Offset: 0,3
Segment:
Start: WaterCliff.R
End: Shore.R
Points: 0,2, 1,2, 2,2, 2,1, 3,1
MultiBrush@Shore_WaterCliff_88_135_407:
Template: 88
Offset: 0,2
Template: 135
Template: 407
Offset: 1,0
Segment:
Start: WaterCliff.U
End: Shore.U
Points: 0,3, 0,2, 1,2, 1,1, 1,0
MultiBrush@Shore_WaterCliff_111_168_411_412:
Template: 111
Offset: 1,3
Template: 168
Offset: 2,3
Template: 411
Template: 412
Offset: 2,1
Segment:
Start: WaterCliff.L
End: Shore.L
Points: 3,4, 2,4, 1,4