Implement proper TS Tiberium and Vein logic.
110
OpenRA.Mods.Cnc/Traits/World/TSEditorResourceLayer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
120
OpenRA.Mods.Cnc/Traits/World/TSResourceLayer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
92
OpenRA.Mods.Cnc/Traits/World/TSTiberiumRenderer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
338
OpenRA.Mods.Cnc/Traits/World/TSVeinsRenderer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
@@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
@@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
@@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
@@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@@ -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:
|
||||
|
||||
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
@@ -508,7 +508,7 @@ NAWAST:
|
||||
Tooltip:
|
||||
Name: Waste Refinery
|
||||
Buildable:
|
||||
Queue: Buildig
|
||||
Queue: Building
|
||||
BuildPaletteOrder: 130
|
||||
Prerequisites: namisl, ~structures.nod, ~techlevel.superweapons
|
||||
BuildLimit: 1
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
ResourceValues:
|
||||
Tiberium: 25
|
||||
BlueTiberium: 40
|
||||
Veins: 0
|
||||
|
||||
EditorPlayer:
|
||||
Inherits: ^BasePlayer
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -192,5 +192,5 @@ veinhole:
|
||||
idle:
|
||||
ShadowStart: 1
|
||||
UseTilesetExtension: true
|
||||
Offset: 0,-60
|
||||
Offset: 0, -36
|
||||
ZRamp: 1
|
||||
|
||||