Implement proper TS Tiberium and Vein logic.

This commit is contained in:
Paul Chote
2021-02-24 20:26:49 +00:00
committed by reaperrr
parent 0bdd46451e
commit fcc3008b00
39 changed files with 739 additions and 41 deletions

View File

@@ -0,0 +1,110 @@
#region Copyright & License Information
/*
* Copyright 2007-2021 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Traits
{
class TSEditorResourceLayerInfo : EditorResourceLayerInfo, Requires<EditorActorLayerInfo>
{
public readonly string VeinType = "Veins";
[ActorReference]
[Desc("Actor types that should be treated as veins for adjacency.")]
public readonly HashSet<string> VeinholeActors = new HashSet<string> { };
public override object Create(ActorInitializer init) { return new TSEditorResourceLayer(init.Self, this); }
}
class TSEditorResourceLayer : EditorResourceLayer
{
readonly TSEditorResourceLayerInfo info;
readonly EditorActorLayer actorLayer;
public TSEditorResourceLayer(Actor self, TSEditorResourceLayerInfo info)
: base(self, info)
{
this.info = info;
actorLayer = self.Trait<EditorActorLayer>();
}
bool IsValidVeinNeighbour(CPos cell, CPos neighbour)
{
// Cell is automatically valid if it contains a veinhole actor
if (actorLayer.PreviewsAt(neighbour).Any(a => info.VeinholeActors.Contains(a.Info.Name)))
return true;
// Neighbour must be flat or a cardinal slope, unless the resource cell itself is a slope
if (Map.Ramp[cell] == 0 && Map.Ramp[neighbour] > 4)
return false;
var terrainInfo = Map.Rules.TerrainInfo;
var terrainType = terrainInfo.TerrainTypes[terrainInfo.GetTerrainInfo(Map.Tiles[neighbour]).TerrainType].Type;
return info.ResourceTypes[info.VeinType].AllowedTerrainTypes.Contains(terrainType);
}
protected override bool AllowResourceAt(string resourceType, CPos cell)
{
var mapResources = Map.Resources;
if (!mapResources.Contains(cell))
return false;
// Resources are allowed on flat terrain and cardinal slopes
if (Map.Ramp[cell] > 4)
return false;
if (!info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
return false;
// Ignore custom terrain types when spawning resources in the editor
var terrainInfo = Map.Rules.TerrainInfo;
var terrainType = terrainInfo.TerrainTypes[terrainInfo.GetTerrainInfo(Map.Tiles[cell]).TerrainType].Type;
if (!resourceInfo.AllowedTerrainTypes.Contains(terrainType))
return false;
// Veins must be placed next to a compatible border cell
if (resourceType == info.VeinType)
{
var neighboursValid = Common.Util.ExpandFootprint(cell, false)
.All(c => IsValidVeinNeighbour(cell, c));
if (!neighboursValid)
return false;
}
// TODO: Check against actors in the EditorActorLayer
return true;
}
protected override int AddResource(string resourceType, CPos cell, int amount = 1)
{
var added = base.AddResource(resourceType, cell, amount);
// Update neighbouring cells if needed to provide space for vein borders
var resourceIsVeins = resourceType == info.VeinType;
foreach (var c in Common.Util.ExpandFootprint(cell, false))
{
var resourceIndex = Map.Resources[c].Type;
if (resourceIndex == 0 || !ResourceTypesByIndex.TryGetValue(resourceIndex, out var neighourResourceType))
neighourResourceType = null;
var neighbourIsVeins = neighourResourceType == info.VeinType;
if (resourceIsVeins ^ neighbourIsVeins)
ClearResources(c);
}
return added;
}
}
}

View File

@@ -0,0 +1,120 @@
#region Copyright & License Information
/*
* Copyright 2007-2021 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Traits
{
class TSResourceLayerInfo : ResourceLayerInfo
{
public readonly string VeinType = "Veins";
[ActorReference]
[Desc("Actor types that should be treated as veins for adjacency.")]
public readonly HashSet<string> VeinholeActors = new HashSet<string> { };
public override object Create(ActorInitializer init) { return new TSResourceLayer(init.Self, this); }
}
class TSResourceLayer : ResourceLayer, INotifyActorDisposing
{
readonly TSResourceLayerInfo info;
readonly HashSet<CPos> veinholeCells = new HashSet<CPos>();
public TSResourceLayer(Actor self, TSResourceLayerInfo info)
: base(self, info)
{
this.info = info;
}
protected override void WorldLoaded(World w, WorldRenderer wr)
{
// Cache locations of veinhole actors
w.ActorAdded += ActorAddedToWorld;
w.ActorRemoved += ActorRemovedFromWorld;
foreach (var a in w.Actors)
ActorAddedToWorld(a);
base.WorldLoaded(w, wr);
}
void ActorAddedToWorld(Actor a)
{
if (info.VeinholeActors.Contains(a.Info.Name))
foreach (var cell in a.OccupiesSpace.OccupiedCells())
veinholeCells.Add(cell.Cell);
}
void ActorRemovedFromWorld(Actor a)
{
if (info.VeinholeActors.Contains(a.Info.Name))
foreach (var cell in a.OccupiesSpace.OccupiedCells())
veinholeCells.Remove(cell.Cell);
}
void INotifyActorDisposing.Disposing(Actor self)
{
self.World.ActorAdded -= ActorAddedToWorld;
self.World.ActorRemoved -= ActorRemovedFromWorld;
}
bool IsValidResourceNeighbour(CPos cell, CPos neighbour)
{
// Non-vein resources are not allowed in the cardinal neighbours to
// an already existing vein cell
return Content[neighbour].Type != info.VeinType;
}
bool IsValidVeinNeighbour(CPos cell, CPos neighbour)
{
// Cell is automatically valid if it contains a veinhole actor
if (veinholeCells.Contains(neighbour))
return true;
// Neighbour must be flat or a cardinal slope, unless the resource cell itself is a slope
if (Map.Ramp[cell] == 0 && Map.Ramp[neighbour] > 4)
return false;
// Neighbour must be have a compatible terrain type (which also implies no other resources)
var neighbourTerrain = Map.GetTerrainInfo(neighbour).Type;
var veinInfo = info.ResourceTypes[info.VeinType];
return neighbourTerrain == veinInfo.TerrainType || veinInfo.AllowedTerrainTypes.Contains(neighbourTerrain);
}
protected override bool AllowResourceAt(string resourceType, CPos cell)
{
if (!Map.Contains(cell))
return false;
// Resources are allowed on flat terrain and cardinal slopes
if (Map.Ramp[cell] > 4)
return false;
if (!info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
return false;
if (!resourceInfo.AllowedTerrainTypes.Contains(Map.GetTerrainInfo(cell).Type))
return false;
// Ensure there is space for the vein border tiles (not needed on ramps)
var check = resourceType == info.VeinType ? (Func<CPos, CPos, bool>)IsValidVeinNeighbour : IsValidResourceNeighbour;
var blockedByNeighbours = Map.Ramp[cell] == 0 && !Common.Util.ExpandFootprint(cell, false)
.All(c => check(cell, c));
return !blockedByNeighbours && (resourceType == info.VeinType || BuildingInfluence.GetBuildingAt(cell) == null);
}
}
}

View File

@@ -0,0 +1,92 @@
#region Copyright & License Information
/*
* Copyright 2007-2021 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
namespace OpenRA.Mods.Cnc.Traits
{
[Desc("Renders the Tiberian Sun Tiberium resources.", "Attach this to the world actor")]
public class TSTiberiumRendererInfo : ResourceRendererInfo
{
[Desc("Sequences to use for ramp type 1.", "Dictionary of [resource type]: [list of sequences].")]
public readonly Dictionary<string, string[]> Ramp1Sequences = new Dictionary<string, string[]>();
[Desc("Sequences to use for ramp type 2.", "Dictionary of [resource type]: [list of sequences].")]
public readonly Dictionary<string, string[]> Ramp2Sequences = new Dictionary<string, string[]>();
[Desc("Sequences to use for ramp type 3.", "Dictionary of [resource type]: [list of sequences].")]
public readonly Dictionary<string, string[]> Ramp3Sequences = new Dictionary<string, string[]>();
[Desc("Sequences to use for ramp type 4.", "Dictionary of [resource type]: [list of sequences].")]
public readonly Dictionary<string, string[]> Ramp4Sequences = new Dictionary<string, string[]>();
public override object Create(ActorInitializer init) { return new TSTiberiumRenderer(init.Self, this); }
}
public class TSTiberiumRenderer : ResourceRenderer
{
readonly TSTiberiumRendererInfo info;
readonly World world;
readonly Dictionary<string, Dictionary<string, ISpriteSequence>> ramp1Variants = new Dictionary<string, Dictionary<string, ISpriteSequence>>();
readonly Dictionary<string, Dictionary<string, ISpriteSequence>> ramp2Variants = new Dictionary<string, Dictionary<string, ISpriteSequence>>();
readonly Dictionary<string, Dictionary<string, ISpriteSequence>> ramp3Variants = new Dictionary<string, Dictionary<string, ISpriteSequence>>();
readonly Dictionary<string, Dictionary<string, ISpriteSequence>> ramp4Variants = new Dictionary<string, Dictionary<string, ISpriteSequence>>();
public TSTiberiumRenderer(Actor self, TSTiberiumRendererInfo info)
: base(self, info)
{
this.info = info;
world = self.World;
}
void LoadVariants(Dictionary<string, string[]> rampSequences, Dictionary<string, Dictionary<string, ISpriteSequence>> rampVariants)
{
var sequences = world.Map.Rules.Sequences;
foreach (var kv in rampSequences)
{
if (!Info.ResourceTypes.TryGetValue(kv.Key, out var resourceInfo))
continue;
var resourceVariants = kv.Value
.ToDictionary(v => v, v => sequences.GetSequence(resourceInfo.Image, v));
rampVariants.Add(kv.Key, resourceVariants);
}
}
protected override void WorldLoaded(World w, WorldRenderer wr)
{
LoadVariants(info.Ramp1Sequences, ramp1Variants);
LoadVariants(info.Ramp2Sequences, ramp2Variants);
LoadVariants(info.Ramp3Sequences, ramp3Variants);
LoadVariants(info.Ramp4Sequences, ramp4Variants);
base.WorldLoaded(w, wr);
}
protected override ISpriteSequence ChooseVariant(string resourceType, CPos cell)
{
Dictionary<string, Dictionary<string, ISpriteSequence>> variants;
switch (world.Map.Ramp[cell])
{
case 1: variants = ramp1Variants; break;
case 2: variants = ramp2Variants; break;
case 3: variants = ramp3Variants; break;
case 4: variants = ramp4Variants; break;
default: variants = Variants; break;
}
return variants[resourceType].Values.Random(world.LocalRandom);
}
}
}

View File

@@ -0,0 +1,338 @@
#region Copyright & License Information
/*
* Copyright 2007-2021 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Traits
{
[Desc("Renders the Tiberian Sun Vein resources.", "Attach this to the world actor")]
public class TSVeinsRendererInfo : TraitInfo, Requires<IResourceLayerInfo>
{
[FieldLoader.Require]
[Desc("Resource type used for veins.")]
public readonly string ResourceType = null;
[Desc("Sequence image that holds the different variants.")]
public readonly string Image = "resources";
[SequenceReference(nameof(Image))]
[Desc("Vein sequence name.")]
public readonly string Sequence = "veins";
[PaletteReference]
[Desc("Palette used for rendering the resource sprites.")]
public readonly string Palette = TileSet.TerrainPaletteInternalName;
[FieldLoader.Require]
[Desc("Resource name used by tooltips.")]
public readonly string Name = null;
[ActorReference]
[Desc("Actor types that should be treated as veins for adjacency.")]
public readonly HashSet<string> VeinholeActors = new HashSet<string> { };
public override object Create(ActorInitializer init) { return new TSVeinsRenderer(init.Self, this); }
}
public class TSVeinsRenderer : IResourceRenderer, IWorldLoaded, IRenderOverlay, ITickRender, INotifyActorDisposing
{
[Flags]
enum Adjacency : byte
{
None = 0x0,
MinusX = 0x1,
PlusX = 0x2,
MinusY = 0x4,
PlusY = 0x8,
}
static readonly Dictionary<Adjacency, int[]> BorderIndices = new Dictionary<Adjacency, int[]>()
{
{ Adjacency.MinusY, new[] { 3, 4, 5 } },
{ Adjacency.PlusX, new[] { 6, 7, 8 } },
{ Adjacency.MinusY | Adjacency.PlusX, new[] { 9, 10, 11 } },
{ Adjacency.PlusY, new[] { 12, 13, 14 } },
{ Adjacency.MinusY | Adjacency.PlusY, new[] { 15, 16, 17 } },
{ Adjacency.PlusY | Adjacency.PlusX, new[] { 18, 19, 20 } },
{ Adjacency.MinusY | Adjacency.PlusY | Adjacency.PlusX, new[] { 21, 22, 23 } },
{ Adjacency.MinusX, new[] { 24, 25, 26 } },
{ Adjacency.MinusX | Adjacency.MinusY, new[] { 27, 28, 29 } },
{ Adjacency.MinusX | Adjacency.PlusX, new[] { 30, 31, 32 } },
{ Adjacency.MinusX | Adjacency.PlusX | Adjacency.MinusY, new[] { 33, 34, 35 } },
{ Adjacency.MinusX | Adjacency.PlusY, new[] { 36, 37, 38 } },
{ Adjacency.MinusX | Adjacency.MinusY | Adjacency.PlusY, new[] { 39, 40, 41 } },
{ Adjacency.MinusX | Adjacency.PlusX | Adjacency.PlusY, new[] { 42, 43, 44 } },
{ Adjacency.MinusX | Adjacency.PlusX | Adjacency.MinusY | Adjacency.PlusY, new[] { 45, 46, 47 } },
};
static readonly int[] HeavyIndices = { 48, 49, 50, 51 };
static readonly int[] LightIndices = { 52 };
static readonly int[] Ramp1Indices = { 53, 54 };
static readonly int[] Ramp2Indices = { 55, 56 };
static readonly int[] Ramp3Indices = { 57, 58 };
static readonly int[] Ramp4Indices = { 59, 60 };
readonly TSVeinsRendererInfo info;
readonly World world;
readonly IResourceLayer resourceLayer;
readonly CellLayer<int[]> renderIndices;
readonly CellLayer<Adjacency> borders;
readonly HashSet<CPos> dirty = new HashSet<CPos>();
readonly Queue<CPos> cleanDirty = new Queue<CPos>();
readonly HashSet<CPos> veinholeCells = new HashSet<CPos>();
readonly int maxDensity;
ISpriteSequence veinSequence;
PaletteReference veinPalette;
TerrainSpriteLayer spriteLayer;
public TSVeinsRenderer(Actor self, TSVeinsRendererInfo info)
{
this.info = info;
world = self.World;
resourceLayer = self.Trait<IResourceLayer>();
resourceLayer.CellChanged += AddDirtyCell;
maxDensity = resourceLayer.GetMaxDensity(info.ResourceType);
renderIndices = new CellLayer<int[]>(world.Map);
borders = new CellLayer<Adjacency>(world.Map);
}
void AddDirtyCell(CPos cell, string resourceType)
{
if (resourceType == null || resourceType == info.ResourceType)
dirty.Add(cell);
}
void IWorldLoaded.WorldLoaded(World w, WorldRenderer wr)
{
// Cache locations of veinhole actors
// TODO: Add support for monitoring actors placed in the map editor!
w.ActorAdded += ActorAddedToWorld;
w.ActorRemoved += ActorRemovedFromWorld;
foreach (var a in w.Actors)
ActorAddedToWorld(a);
veinSequence = w.Map.Rules.Sequences.GetSequence(info.Image, info.Sequence);
veinPalette = wr.Palette(info.Palette);
var first = veinSequence.GetSprite(0);
var emptySprite = new Sprite(first.Sheet, Rectangle.Empty, TextureChannel.Alpha);
spriteLayer = new TerrainSpriteLayer(w, wr, emptySprite, first.BlendMode, wr.World.Type != WorldType.Editor);
// Initialize the renderIndices with the initial map state so it is visible
// through the fog with the Explored Map option enabled
foreach (var cell in w.Map.AllCells)
{
var resource = resourceLayer.GetResource(cell);
var cellIndices = CalculateCellIndices(resource, cell);
if (cellIndices != null)
{
renderIndices[cell] = cellIndices;
UpdateRenderedSprite(cell, cellIndices);
}
}
}
int[] CalculateCellIndices(ResourceLayerContents contents, CPos cell)
{
if (contents.Type != info.ResourceType || contents.Density == 0)
return null;
var ramp = world.Map.Ramp[cell];
switch (ramp)
{
case 1: return Ramp1Indices;
case 2: return Ramp2Indices;
case 3: return Ramp3Indices;
case 4: return Ramp4Indices;
default: return contents.Density == maxDensity ? HeavyIndices : LightIndices;
}
}
void IRenderOverlay.Render(WorldRenderer wr)
{
spriteLayer.Draw(wr.Viewport);
}
void ITickRender.TickRender(WorldRenderer wr, Actor self)
{
foreach (var cell in dirty)
{
if (!resourceLayer.IsVisible(cell))
continue;
var contents = resourceLayer.GetResource(cell);
var cellIndices = CalculateCellIndices(contents, cell);
if (cellIndices != renderIndices[cell])
{
renderIndices[cell] = cellIndices;
UpdateRenderedSprite(cell, cellIndices);
}
cleanDirty.Enqueue(cell);
}
while (cleanDirty.Count > 0)
dirty.Remove(cleanDirty.Dequeue());
}
bool HasBorder(CPos cell)
{
if (!renderIndices.Contains(cell))
return false;
// Draw the vein border if this is a flat cell with veins, or a veinhole
return (world.Map.Ramp[cell] == 0 && renderIndices[cell] != null) || veinholeCells.Contains(cell);
}
Adjacency CalculateBorders(CPos cell)
{
// Borders are only valid on flat cells
if (world.Map.Ramp[cell] != 0)
return Adjacency.None;
var ret = Adjacency.None;
if (HasBorder(cell + new CVec(0, -1)))
ret |= Adjacency.MinusY;
if (HasBorder(cell + new CVec(-1, 0)))
ret |= Adjacency.MinusX;
if (HasBorder(cell + new CVec(1, 0)))
ret |= Adjacency.PlusX;
if (HasBorder(cell + new CVec(0, 1)))
ret |= Adjacency.PlusY;
return ret;
}
void UpdateRenderedSprite(CPos cell, int[] indices)
{
borders[cell] = Adjacency.None;
UpdateSpriteLayers(cell, indices);
foreach (var c in Common.Util.ExpandFootprint(cell, false))
UpdateBorderSprite(c);
}
void UpdateBorderSprite(CPos cell)
{
// Borders are never drawn on ramps or in cells that contain resources
if (HasBorder(cell) || world.Map.Ramp[cell] != 0)
return;
var adjacency = CalculateBorders(cell);
if (borders[cell] == adjacency)
return;
borders[cell] = adjacency;
if (adjacency == Adjacency.None)
UpdateSpriteLayers(cell, null);
else if (BorderIndices.TryGetValue(adjacency, out var indices))
UpdateSpriteLayers(cell, indices);
else
throw new InvalidOperationException("SpriteMap does not contain an index for Adjacency type '{0}'".F(adjacency));
}
void UpdateSpriteLayers(CPos cell, int[] indices)
{
if (indices != null)
spriteLayer.Update(cell, veinSequence, veinPalette, indices.Random(world.LocalRandom));
else
spriteLayer.Clear(cell);
}
void ActorAddedToWorld(Actor a)
{
if (info.VeinholeActors.Contains(a.Info.Name))
{
foreach (var cell in a.OccupiesSpace.OccupiedCells())
{
veinholeCells.Add(cell.Cell);
AddDirtyCell(cell.Cell, info.ResourceType);
}
}
}
void ActorRemovedFromWorld(Actor a)
{
if (info.VeinholeActors.Contains(a.Info.Name))
{
foreach (var cell in a.OccupiesSpace.OccupiedCells())
{
veinholeCells.Remove(cell.Cell);
AddDirtyCell(cell.Cell, null);
}
}
}
void INotifyActorDisposing.Disposing(Actor self)
{
resourceLayer.CellChanged -= AddDirtyCell;
world.ActorAdded -= ActorAddedToWorld;
world.ActorRemoved -= ActorRemovedFromWorld;
}
IEnumerable<string> IResourceRenderer.ResourceTypes { get { yield return info.ResourceType; } }
string IResourceRenderer.GetRenderedResourceType(CPos cell)
{
if (renderIndices[cell] != null)
return info.ResourceType;
// This makes sure harvesters will get a harvest cursor but then move to the next actual resource cell to start harvesting
return borders[cell] != Adjacency.None ? info.ResourceType : null;
}
string IResourceRenderer.GetRenderedResourceTooltip(CPos cell)
{
if (renderIndices[cell] != null)
return info.Name;
return borders[cell] != Adjacency.None ? info.Name : null;
}
IEnumerable<IRenderable> IResourceRenderer.RenderUIPreview(WorldRenderer wr, string resourceType, int2 origin, float scale)
{
if (resourceType != info.ResourceType)
yield break;
var sprite = veinSequence.GetSprite(HeavyIndices.First());
var palette = wr.Palette(info.Palette);
yield return new UISpriteRenderable(sprite, WPos.Zero, origin, 0, palette, scale);
}
IEnumerable<IRenderable> IResourceRenderer.RenderPreview(WorldRenderer wr, string resourceType, WPos origin)
{
if (resourceType != info.ResourceType)
yield break;
var frame = HeavyIndices.First();
var sprite = veinSequence.GetSprite(frame);
var alpha = veinSequence.GetAlpha(frame);
var palette = wr.Palette(info.Palette);
var tintModifiers = veinSequence.IgnoreWorldTint ? TintModifiers.IgnoreWorldTint : TintModifiers.None;
yield return new SpriteRenderable(sprite, origin, WVec.Zero, 0, palette, veinSequence.Scale, alpha, float3.Ones, tintModifiers, false);
}
}
}

View File

@@ -237,6 +237,9 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
{ 0x03, new byte[] { 0x7E } }
};
// Veins and vein holes
static readonly int[] ValidVeinNeighbours = { 0x7E, 0xA7, 0xB2 };
static readonly Dictionary<string, string> DeployableActors = new Dictionary<string, string>()
{
{ "gadpsa", "lpst" },
@@ -428,9 +431,14 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
continue;
}
// Fix position of vein hole actors
var location = cell;
if (actorType == "veinhole")
location -= new CVec(1, 1);
var ar = new ActorReference(actorType)
{
new LocationInit(cell),
new LocationInit(location),
new OwnerInit("Neutral")
};
@@ -453,6 +461,19 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
continue;
}
// TS maps encode the non-harvestable border tiles as overlay
// Only convert to vein resources if the overlay data specifies non-border frames
if (overlayType == 0x7E)
{
var frame = overlayDataPack[overlayIndex[cell]];
if (frame < 48 || frame > 60)
continue;
// Pick half or full density based on the frame
map.Resources[cell] = new ResourceTile(3, (byte)(frame == 52 ? 1 : 2));
continue;
}
var resourceType = ResourceFromOverlay
.Where(kv => kv.Value.Contains(overlayType))
.Select(kv => kv.Key)

View File

@@ -157,6 +157,11 @@ namespace OpenRA.Mods.Common
return Math.Abs(offset.X) < 2 && Math.Abs(offset.Y) < 2;
}
public static IEnumerable<CPos> ExpandFootprint(CPos cell, bool allowDiagonal)
{
return Neighbours(cell, allowDiagonal);
}
public static IEnumerable<CPos> ExpandFootprint(IEnumerable<CPos> cells, bool allowDiagonal)
{
return cells.SelectMany(c => Neighbours(c, allowDiagonal)).Distinct();

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

@@ -465,7 +465,7 @@ Actors:
Location: 68,-48
Owner: Neutral
Actor136: veinhole
Location: 63,-35
Location: 62,-36
Owner: Neutral
Actor137: srock01
Location: 60,-19
@@ -567,7 +567,7 @@ Actors:
Location: 80,25
Owner: Neutral
Actor170: veinhole
Location: 71,41
Location: 70,40
Owner: Neutral
Actor171: srock02
Location: 133,-14

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

View File

@@ -1264,7 +1264,7 @@ Actors:
Location: 75,48
Owner: Neutral
Actor384: veinhole
Location: 138,-13
Location: 137,-14
Owner: Neutral
Actor385: trock01
Location: 144,-16

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -651,7 +651,7 @@ Actors:
Location: 70,-20
Owner: Neutral
Actor195: veinhole
Location: 31,11
Location: 30,10
Owner: Neutral
Actor196: srock05
Location: 34,14
@@ -666,13 +666,13 @@ Actors:
Location: 98,-44
Owner: Neutral
Actor200: veinhole
Location: 97,-39
Location: 96,-40
Owner: Neutral
Actor201: trock03
Location: 102,-44
Owner: Neutral
Actor202: veinhole
Location: 71,-8
Location: 70,-9
Owner: Neutral
Actor203: trock01
Location: 96,-33

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -872,10 +872,10 @@ Actors:
Location: 129,-36
Owner: Neutral
Actor253: veinhole
Location: 101,-6
Location: 100,-7
Owner: Neutral
Actor254: veinhole
Location: 86,18
Location: 85,17
Owner: Neutral
Actor255: trock03
Location: 80,60

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -437,13 +437,13 @@ Actors:
Location: 55,-5
Owner: Neutral
Actor129: veinhole
Location: 61,-18
Location: 60,-19
Owner: Neutral
Actor130: veinhole
Location: 72,-11
Location: 71,-12
Owner: Neutral
Actor131: veinhole
Location: 62,1
Location: 61,0
Owner: Neutral
Rules:

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View File

@@ -508,7 +508,7 @@ NAWAST:
Tooltip:
Name: Waste Refinery
Buildable:
Queue: Buildig
Queue: Building
BuildPaletteOrder: 130
Prerequisites: namisl, ~structures.nod, ~techlevel.superweapons
BuildLimit: 1

View File

@@ -5,6 +5,7 @@
ResourceValues:
Tiberium: 25
BlueTiberium: 40
Veins: 0
EditorPlayer:
Inherits: ^BasePlayer

View File

@@ -99,20 +99,17 @@ TREE24:
TREE25:
Inherits: ^Tree
VEINHOLEDUMMY:
VEINHOLE:
HiddenUnderShroud:
RadarColorFromTerrain:
Terrain: Veins
AppearsOnMapPreview:
Terrain: Veins
Building:
Footprint: xx xx
Dimensions: 2, 2
Footprint: xxx xxx xxx
Dimensions: 3, 3
BodyOrientation:
QuantizedFacings: 1
VEINHOLE:
Inherits: VEINHOLEDUMMY
AppearsOnRadar:
Tooltip:
Name: Veinhole

View File

@@ -204,7 +204,7 @@
CliffBackImpassabilityLayer:
SubterraneanActorLayer:
JumpjetActorLayer:
ResourceRenderer:
TSTiberiumRenderer:
ResourceTypes:
Tiberium:
Sequences: tib01, tib02, tib03, tib04, tib05, tib06, tib07, tib08, tib09, tib10, tib11, tib12
@@ -214,10 +214,23 @@
Sequences: tib01, tib02, tib03, tib04, tib05, tib06, tib07, tib08, tib09, tib10, tib11, tib12
Palette: bluetiberium
Name: Tiberium
Veins:
Sequences: veins
Palette: player
Name: Veins
Ramp1Sequences:
Tiberium: tib13, tib14
BlueTiberium: tib13, tib14
Ramp2Sequences:
Tiberium: tib15, tib16
BlueTiberium: tib15, tib16
Ramp3Sequences:
Tiberium: tib17, tib18
BlueTiberium: tib17, tib18
Ramp4Sequences:
Tiberium: tib19, tib20
BlueTiberium: tib19, tib20
TSVeinsRenderer:
ResourceType: Veins
Name: Veins
Palette: player
VeinholeActors: veinhole
World:
Inherits: ^BaseWorld
@@ -257,7 +270,8 @@ World:
SmudgeLayer@LARGECRATER:
Type: LargeCrater
Sequence: largecraters
ResourceLayer:
TSResourceLayer:
VeinholeActors: veinhole
ResourceTypes:
Tiberium:
ResourceIndex: 1
@@ -273,7 +287,7 @@ World:
ResourceIndex: 3
TerrainType: Veins
AllowedTerrainTypes: Clear, Rough, DirtRoad
MaxDensity: 1
MaxDensity: 2
BridgeLayer:
CustomTerrainDebugOverlay:
ResourceClaimLayer:
@@ -371,7 +385,7 @@ EditorWorld:
Inherits: ^BaseWorld
EditorActorLayer:
EditorCursorLayer:
EditorResourceLayer:
TSEditorResourceLayer:
ResourceTypes:
Tiberium:
ResourceIndex: 1
@@ -387,7 +401,8 @@ EditorWorld:
ResourceIndex: 3
TerrainType: Veins
AllowedTerrainTypes: Clear, Rough, DirtRoad
MaxDensity: 1
MaxDensity: 2
VeinholeActors: veinhole
EditorSelectionLayer:
Palette: ra
FootprintAlpha: 0.7

View File

@@ -400,20 +400,19 @@ resources:
tib10: tib10
tib11: tib11
tib12: tib12
tib13: tib13 # TODO: NW ramp variant, currently unused
tib14: tib14 # TODO: NW ramp variant, currently unused
tib15: tib15 # TODO: NE ramp variant, currently unused
tib16: tib16 # TODO: NE ramp variant, currently unused
tib17: tib17 # TODO: SE ramp variant, currently unused
tib18: tib18 # TODO: SE ramp variant, currently unused
tib19: tib19 # TODO: SW ramp variant, currently unused
tib20: tib20 # TODO: SW ramp variant, currently unused
tib13: tib13
tib14: tib14
tib15: tib15
tib16: tib16
tib17: tib17
tib18: tib18
tib19: tib19
tib20: tib20
veins: veins
Length: 1
Start: 52
Length: *
ShadowStart: -1
Tick: 180
Offset: 0, -12, 2.5
# TODO: Reduce z offset again after fixing #12229
Offset: 0, -12, 5.5
IgnoreWorldTint: false
veins:

View File

@@ -192,5 +192,5 @@ veinhole:
idle:
ShadowStart: 1
UseTilesetExtension: true
Offset: 0,-60
Offset: 0, -36
ZRamp: 1