Overhaul resource layer logic:
* ResourceType trait has been removed. * Simulation-related data is now defined on the ResourceLayer (which mods can subclass/replace). * Support non-money resources by moving the resource values to the PlayerResources trait. * Allow mods to disable the neighbour density override and instead always use the map-defined densities. * Allow mods to define their own resource placement logic (e.g. allow resources on slopes) by subclassing (Editor)ResourceLayer. * Improve ability to subclass/override ResourceRenderer by exposing more virtual methods.
This commit is contained in:
@@ -57,7 +57,6 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
{
|
{
|
||||||
readonly Actor self;
|
readonly Actor self;
|
||||||
readonly RefineryInfo info;
|
readonly RefineryInfo info;
|
||||||
readonly Dictionary<string, int> resourceValues;
|
|
||||||
PlayerResources playerResources;
|
PlayerResources playerResources;
|
||||||
IEnumerable<int> resourceValueModifiers;
|
IEnumerable<int> resourceValueModifiers;
|
||||||
|
|
||||||
@@ -83,8 +82,6 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
this.info = info;
|
this.info = info;
|
||||||
playerResources = self.Owner.PlayerActor.Trait<PlayerResources>();
|
playerResources = self.Owner.PlayerActor.Trait<PlayerResources>();
|
||||||
currentDisplayTick = info.TickRate;
|
currentDisplayTick = info.TickRate;
|
||||||
resourceValues = self.World.WorldActor.TraitsImplementing<ResourceType>()
|
|
||||||
.ToDictionary(r => r.Info.Type, r => r.Info.ValuePerUnit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void INotifyCreated.Created(Actor self)
|
void INotifyCreated.Created(Actor self)
|
||||||
@@ -105,7 +102,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
int IAcceptResources.AcceptResources(string resourceType, int count)
|
int IAcceptResources.AcceptResources(string resourceType, int count)
|
||||||
{
|
{
|
||||||
if (!resourceValues.TryGetValue(resourceType, out var resourceValue))
|
if (!playerResources.Info.ResourceValues.TryGetValue(resourceType, out var resourceValue))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var value = Util.ApplyPercentageModifiers(count * resourceValue, resourceValueModifiers);
|
var value = Util.ApplyPercentageModifiers(count * resourceValue, resourceValueModifiers);
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
[NotificationReference("Sounds")]
|
[NotificationReference("Sounds")]
|
||||||
public readonly string CashTickDownNotification = null;
|
public readonly string CashTickDownNotification = null;
|
||||||
|
|
||||||
|
[Desc("Monetery value of each resource type.", "Dictionary of [resource type]: [value per unit].")]
|
||||||
|
public readonly Dictionary<string, int> ResourceValues = new Dictionary<string, int>();
|
||||||
|
|
||||||
IEnumerable<LobbyOption> ILobbyOptions.LobbyOptions(Ruleset rules)
|
IEnumerable<LobbyOption> ILobbyOptions.LobbyOptions(Ruleset rules)
|
||||||
{
|
{
|
||||||
var startingCash = SelectableCash.ToDictionary(c => c.ToString(), c => "$" + c.ToString());
|
var startingCash = SelectableCash.ToDictionary(c => c.ToString(), c => "$" + c.ToString());
|
||||||
|
|||||||
@@ -13,22 +13,47 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
|
using OpenRA.Primitives;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Traits
|
namespace OpenRA.Mods.Common.Traits
|
||||||
{
|
{
|
||||||
[Desc("Required for the map editor to work. Attach this to the world actor.")]
|
[Desc("Required for the map editor to work. Attach this to the world actor.")]
|
||||||
public class EditorResourceLayerInfo : TraitInfo, IResourceLayerInfo, Requires<ResourceTypeInfo>
|
public class EditorResourceLayerInfo : TraitInfo, IResourceLayerInfo, IMapPreviewSignatureInfo
|
||||||
{
|
{
|
||||||
public override object Create(ActorInitializer init) { return new EditorResourceLayer(init.Self); }
|
[FieldLoader.LoadUsing(nameof(LoadResourceTypes))]
|
||||||
|
public readonly Dictionary<string, ResourceLayerInfo.ResourceTypeInfo> ResourceTypes = null;
|
||||||
|
|
||||||
|
// Copied from ResourceLayerInfo
|
||||||
|
protected static object LoadResourceTypes(MiniYaml yaml)
|
||||||
|
{
|
||||||
|
var ret = new Dictionary<string, ResourceLayerInfo.ResourceTypeInfo>();
|
||||||
|
var resources = yaml.Nodes.FirstOrDefault(n => n.Key == "ResourceTypes");
|
||||||
|
if (resources != null)
|
||||||
|
foreach (var r in resources.Value.Nodes)
|
||||||
|
ret[r.Key] = new ResourceLayerInfo.ResourceTypeInfo(r.Value);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Desc("Override the density saved in maps with values calculated based on the number of neighbouring resource cells.")]
|
||||||
|
public readonly bool RecalculateResourceDensity = false;
|
||||||
|
|
||||||
|
void IMapPreviewSignatureInfo.PopulateMapPreviewSignatureCells(Map map, ActorInfo ai, ActorReference s, List<(MPos, Color)> destinationBuffer)
|
||||||
|
{
|
||||||
|
ResourceLayerInfo.PopulateMapPreviewSignatureCells(map, ResourceTypes, destinationBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Create(ActorInitializer init) { return new EditorResourceLayer(init.Self, this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EditorResourceLayer : IResourceLayer, IWorldLoaded, INotifyActorDisposing
|
public class EditorResourceLayer : IResourceLayer, IWorldLoaded, INotifyActorDisposing
|
||||||
{
|
{
|
||||||
|
readonly EditorResourceLayerInfo info;
|
||||||
protected readonly Map Map;
|
protected readonly Map Map;
|
||||||
protected readonly Dictionary<string, ResourceTypeInfo> ResourceInfo;
|
protected readonly Dictionary<byte, string> ResourceTypesByIndex;
|
||||||
protected readonly Dictionary<int, string> Resources;
|
|
||||||
protected readonly CellLayer<ResourceLayerContents> Tiles;
|
protected readonly CellLayer<ResourceLayerContents> Tiles;
|
||||||
|
protected Dictionary<string, int> resourceValues;
|
||||||
|
|
||||||
public int NetWorth { get; protected set; }
|
public int NetWorth { get; protected set; }
|
||||||
|
|
||||||
@@ -39,10 +64,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
ResourceLayerContents IResourceLayer.GetResource(CPos cell) { return Tiles.Contains(cell) ? Tiles[cell] : default; }
|
ResourceLayerContents IResourceLayer.GetResource(CPos cell) { return Tiles.Contains(cell) ? Tiles[cell] : default; }
|
||||||
int IResourceLayer.GetMaxDensity(string resourceType)
|
int IResourceLayer.GetMaxDensity(string resourceType)
|
||||||
{
|
{
|
||||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
return info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo) ? resourceInfo.MaxDensity : 0;
|
||||||
return 0;
|
|
||||||
|
|
||||||
return resourceInfo.MaxDensity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IResourceLayer.CanAddResource(string resourceType, CPos cell, int amount) { return CanAddResource(resourceType, cell, amount); }
|
bool IResourceLayer.CanAddResource(string resourceType, CPos cell, int amount) { return CanAddResource(resourceType, cell, amount); }
|
||||||
@@ -52,17 +74,17 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
bool IResourceLayer.IsVisible(CPos cell) { return Map.Contains(cell); }
|
bool IResourceLayer.IsVisible(CPos cell) { return Map.Contains(cell); }
|
||||||
bool IResourceLayer.IsEmpty => false;
|
bool IResourceLayer.IsEmpty => false;
|
||||||
|
|
||||||
public EditorResourceLayer(Actor self)
|
public EditorResourceLayer(Actor self, EditorResourceLayerInfo info)
|
||||||
{
|
{
|
||||||
if (self.World.Type != WorldType.Editor)
|
if (self.World.Type != WorldType.Editor)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
this.info = info;
|
||||||
Map = self.World.Map;
|
Map = self.World.Map;
|
||||||
Tiles = new CellLayer<ResourceLayerContents>(Map);
|
Tiles = new CellLayer<ResourceLayerContents>(Map);
|
||||||
ResourceInfo = self.TraitsImplementing<ResourceType>()
|
ResourceTypesByIndex = info.ResourceTypes.ToDictionary(
|
||||||
.ToDictionary(r => r.Info.Type, r => r.Info);
|
kv => kv.Value.ResourceIndex,
|
||||||
Resources = ResourceInfo.Values
|
kv => kv.Key);
|
||||||
.ToDictionary(r => r.ResourceType, r => r.Type);
|
|
||||||
|
|
||||||
Map.Resources.CellEntryChanged += UpdateCell;
|
Map.Resources.CellEntryChanged += UpdateCell;
|
||||||
}
|
}
|
||||||
@@ -72,6 +94,9 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (w.Type != WorldType.Editor)
|
if (w.Type != WorldType.Editor)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var playerResourcesInfo = w.Map.Rules.Actors["player"].TraitInfoOrDefault<PlayerResourcesInfo>();
|
||||||
|
resourceValues = playerResourcesInfo?.ResourceValues ?? new Dictionary<string, int>();
|
||||||
|
|
||||||
foreach (var cell in Map.AllCells)
|
foreach (var cell in Map.AllCells)
|
||||||
UpdateCell(cell);
|
UpdateCell(cell);
|
||||||
}
|
}
|
||||||
@@ -87,9 +112,9 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
var newTile = ResourceLayerContents.Empty;
|
var newTile = ResourceLayerContents.Empty;
|
||||||
var newTerrain = byte.MaxValue;
|
var newTerrain = byte.MaxValue;
|
||||||
if (Resources.TryGetValue(tile.Type, out var resourceType) && ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
if (ResourceTypesByIndex.TryGetValue(tile.Type, out var resourceType) && info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||||
{
|
{
|
||||||
newTile = new ResourceLayerContents(resourceType, CalculateCellDensity(resourceType, cell));
|
newTile = new ResourceLayerContents(resourceType, CalculateCellDensity(new ResourceLayerContents(resourceType, tile.Index), cell));
|
||||||
newTerrain = Map.Rules.TerrainInfo.GetTerrainIndex(resourceInfo.TerrainType);
|
newTerrain = Map.Rules.TerrainInfo.GetTerrainIndex(resourceInfo.TerrainType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +127,10 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
Map.CustomTerrain[uv] = newTerrain;
|
Map.CustomTerrain[uv] = newTerrain;
|
||||||
CellChanged?.Invoke(cell, newTile.Type);
|
CellChanged?.Invoke(cell, newTile.Type);
|
||||||
|
|
||||||
// Neighbouring cell density depends on this cell
|
if (!info.RecalculateResourceDensity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Update neighbour density to account for this cell
|
||||||
foreach (var d in CVec.Directions)
|
foreach (var d in CVec.Directions)
|
||||||
{
|
{
|
||||||
var neighbouringCell = cell + d;
|
var neighbouringCell = cell + d;
|
||||||
@@ -110,7 +138,10 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
var neighbouringTile = Tiles[neighbouringCell];
|
var neighbouringTile = Tiles[neighbouringCell];
|
||||||
var density = CalculateCellDensity(neighbouringTile.Type, neighbouringCell);
|
if (neighbouringTile.Type == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var density = CalculateCellDensity(neighbouringTile, neighbouringCell);
|
||||||
if (neighbouringTile.Density == density)
|
if (neighbouringTile.Density == density)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -124,19 +155,22 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
void UpdateNetWorth(string oldResourceType, int oldDensity, string newResourceType, int newDensity)
|
void UpdateNetWorth(string oldResourceType, int oldDensity, string newResourceType, int newDensity)
|
||||||
{
|
{
|
||||||
// Density + 1 as workaround for fixing ResourceLayer.Harvest as it would be very disruptive to balancing
|
// Density + 1 as workaround for fixing ResourceLayer.Harvest as it would be very disruptive to balancing
|
||||||
if (oldResourceType != null && oldDensity > 0 && ResourceInfo.TryGetValue(oldResourceType, out var oldResourceInfo))
|
if (oldResourceType != null && oldDensity > 0 && resourceValues.TryGetValue(oldResourceType, out var oldResourceValue))
|
||||||
NetWorth -= (oldDensity + 1) * oldResourceInfo.ValuePerUnit;
|
NetWorth -= (oldDensity + 1) * oldResourceValue;
|
||||||
|
|
||||||
if (newResourceType != null && newDensity > 0 && ResourceInfo.TryGetValue(newResourceType, out var newResourceInfo))
|
if (newResourceType != null && newDensity > 0 && resourceValues.TryGetValue(newResourceType, out var newResourceValue))
|
||||||
NetWorth += (newDensity + 1) * newResourceInfo.ValuePerUnit;
|
NetWorth += (newDensity + 1) * newResourceValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int CalculateCellDensity(string resourceType, CPos c)
|
protected virtual int CalculateCellDensity(ResourceLayerContents contents, CPos c)
|
||||||
{
|
{
|
||||||
var resources = Map.Resources;
|
var resources = Map.Resources;
|
||||||
if (resourceType == null || !ResourceInfo.TryGetValue(resourceType, out var resourceInfo) || resources[c].Type != resourceInfo.ResourceType)
|
if (contents.Type == null || !info.ResourceTypes.TryGetValue(contents.Type, out var resourceInfo) || resources[c].Type != resourceInfo.ResourceIndex)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (!info.RecalculateResourceDensity)
|
||||||
|
return contents.Density.Clamp(1, resourceInfo.MaxDensity);
|
||||||
|
|
||||||
// Set density based on the number of neighboring resources
|
// Set density based on the number of neighboring resources
|
||||||
var adjacent = 0;
|
var adjacent = 0;
|
||||||
for (var u = -1; u < 2; u++)
|
for (var u = -1; u < 2; u++)
|
||||||
@@ -144,7 +178,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
for (var v = -1; v < 2; v++)
|
for (var v = -1; v < 2; v++)
|
||||||
{
|
{
|
||||||
var cell = c + new CVec(u, v);
|
var cell = c + new CVec(u, v);
|
||||||
if (resources.Contains(cell) && resources[cell].Type == resourceInfo.ResourceType)
|
if (resources.Contains(cell) && resources[cell].Type == resourceInfo.ResourceIndex)
|
||||||
adjacent++;
|
adjacent++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,23 +186,20 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return Math.Max(int2.Lerp(0, resourceInfo.MaxDensity, adjacent, 9), 1);
|
return Math.Max(int2.Lerp(0, resourceInfo.MaxDensity, adjacent, 9), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AllowResourceAt(string resourceType, CPos cell)
|
protected virtual bool AllowResourceAt(string resourceType, CPos cell)
|
||||||
{
|
{
|
||||||
var mapResources = Map.Resources;
|
if (!Map.Ramp.Contains(cell) || Map.Ramp[cell] != 0)
|
||||||
if (!mapResources.Contains(cell))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
if (!info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Ignore custom terrain types when spawning resources in the editor
|
// Ignore custom terrain types when spawning resources in the editor
|
||||||
var terrainInfo = Map.Rules.TerrainInfo;
|
var terrainInfo = Map.Rules.TerrainInfo;
|
||||||
var terrainType = terrainInfo.TerrainTypes[terrainInfo.GetTerrainInfo(Map.Tiles[cell]).TerrainType].Type;
|
var terrainType = terrainInfo.TerrainTypes[terrainInfo.GetTerrainInfo(Map.Tiles[cell]).TerrainType].Type;
|
||||||
if (!resourceInfo.AllowedTerrainTypes.Contains(terrainType))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// TODO: Check against actors in the EditorActorLayer
|
// TODO: Check against actors in the EditorActorLayer
|
||||||
return resourceInfo.AllowOnRamps || Map.Ramp[cell] == 0;
|
return resourceInfo.AllowedTerrainTypes.Contains(terrainType);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanAddResource(string resourceType, CPos cell, int amount = 1)
|
bool CanAddResource(string resourceType, CPos cell, int amount = 1)
|
||||||
@@ -177,57 +208,57 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (!resources.Contains(cell))
|
if (!resources.Contains(cell))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
if (!info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// The editor allows the user to replace one resource type with another, so treat mismatching resource type as an empty cell
|
// The editor allows the user to replace one resource type with another, so treat mismatching resource type as an empty cell
|
||||||
var content = resources[cell];
|
var content = resources[cell];
|
||||||
if (content.Type != resourceInfo.ResourceType)
|
if (content.Type != resourceInfo.ResourceIndex)
|
||||||
return amount <= resourceInfo.MaxDensity && AllowResourceAt(resourceType, cell);
|
return amount <= resourceInfo.MaxDensity && AllowResourceAt(resourceType, cell);
|
||||||
|
|
||||||
var oldDensity = content.Type == resourceInfo.ResourceType ? content.Index : 0;
|
var oldDensity = content.Type == resourceInfo.ResourceIndex ? content.Index : 0;
|
||||||
return oldDensity + amount <= resourceInfo.MaxDensity;
|
return oldDensity + amount <= resourceInfo.MaxDensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
int AddResource(string resourceType, CPos cell, int amount = 1)
|
protected virtual int AddResource(string resourceType, CPos cell, int amount = 1)
|
||||||
{
|
{
|
||||||
var resources = Map.Resources;
|
var resources = Map.Resources;
|
||||||
if (!resources.Contains(cell))
|
if (!resources.Contains(cell))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
if (!info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// The editor allows the user to replace one resource type with another, so treat mismatching resource type as an empty cell
|
// The editor allows the user to replace one resource type with another, so treat mismatching resource type as an empty cell
|
||||||
var content = resources[cell];
|
var content = resources[cell];
|
||||||
var oldDensity = content.Type == resourceInfo.ResourceType ? content.Index : 0;
|
var oldDensity = content.Type == resourceInfo.ResourceIndex ? content.Index : 0;
|
||||||
var density = (byte)Math.Min(resourceInfo.MaxDensity, oldDensity + amount);
|
var density = (byte)Math.Min(resourceInfo.MaxDensity, oldDensity + amount);
|
||||||
Map.Resources[cell] = new ResourceTile((byte)resourceInfo.ResourceType, density);
|
Map.Resources[cell] = new ResourceTile((byte)resourceInfo.ResourceIndex, density);
|
||||||
|
|
||||||
return density - oldDensity;
|
return density - oldDensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RemoveResource(string resourceType, CPos cell, int amount = 1)
|
protected virtual int RemoveResource(string resourceType, CPos cell, int amount = 1)
|
||||||
{
|
{
|
||||||
var resources = Map.Resources;
|
var resources = Map.Resources;
|
||||||
if (!resources.Contains(cell))
|
if (!resources.Contains(cell))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
if (!info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var content = resources[cell];
|
var content = resources[cell];
|
||||||
if (content.Type == 0 || content.Type != resourceInfo.ResourceType)
|
if (content.Type == 0 || content.Type != resourceInfo.ResourceIndex)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var oldDensity = content.Index;
|
var oldDensity = content.Index;
|
||||||
var density = (byte)Math.Max(0, oldDensity - amount);
|
var density = (byte)Math.Max(0, oldDensity - amount);
|
||||||
resources[cell] = density > 0 ? new ResourceTile((byte)resourceInfo.ResourceType, density) : default;
|
resources[cell] = density > 0 ? new ResourceTile(resourceInfo.ResourceIndex, density) : default;
|
||||||
|
|
||||||
return oldDensity - density;
|
return oldDensity - density;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearResources(CPos cell)
|
protected virtual void ClearResources(CPos cell)
|
||||||
{
|
{
|
||||||
Map.Resources[cell] = default;
|
Map.Resources[cell] = default;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
|
using OpenRA.Primitives;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Traits
|
namespace OpenRA.Mods.Common.Traits
|
||||||
@@ -31,99 +32,158 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Desc("Attach this to the world actor.")]
|
[Desc("Attach this to the world actor.")]
|
||||||
public class ResourceLayerInfo : TraitInfo, IResourceLayerInfo, Requires<ResourceTypeInfo>, Requires<BuildingInfluenceInfo>
|
public class ResourceLayerInfo : TraitInfo, IResourceLayerInfo, Requires<BuildingInfluenceInfo>, IMapPreviewSignatureInfo
|
||||||
{
|
{
|
||||||
public override object Create(ActorInitializer init) { return new ResourceLayer(init.Self); }
|
public class ResourceTypeInfo
|
||||||
|
{
|
||||||
|
[FieldLoader.Require]
|
||||||
|
[Desc("Resource index in the binary map data.")]
|
||||||
|
public readonly byte ResourceIndex = 0;
|
||||||
|
|
||||||
|
[FieldLoader.Require]
|
||||||
|
[Desc("Terrain type used to determine unit movement and minimap colors.")]
|
||||||
|
public readonly string TerrainType = null;
|
||||||
|
|
||||||
|
[FieldLoader.Require]
|
||||||
|
[Desc("Terrain types that this resource can spawn on.")]
|
||||||
|
public readonly HashSet<string> AllowedTerrainTypes = null;
|
||||||
|
|
||||||
|
[Desc("Maximum number of resource units allowed in a single cell.")]
|
||||||
|
public readonly int MaxDensity = 10;
|
||||||
|
|
||||||
|
public ResourceTypeInfo(MiniYaml yaml)
|
||||||
|
{
|
||||||
|
FieldLoader.Load(this, yaml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldLoader.LoadUsing(nameof(LoadResourceTypes))]
|
||||||
|
public readonly Dictionary<string, ResourceTypeInfo> ResourceTypes = null;
|
||||||
|
|
||||||
|
[Desc("Override the density saved in maps with values calculated based on the number of neighbouring resource cells.")]
|
||||||
|
public readonly bool RecalculateResourceDensity = false;
|
||||||
|
|
||||||
|
// Copied to EditorResourceLayerInfo, ResourceRendererInfo
|
||||||
|
protected static object LoadResourceTypes(MiniYaml yaml)
|
||||||
|
{
|
||||||
|
var ret = new Dictionary<string, ResourceTypeInfo>();
|
||||||
|
var resources = yaml.Nodes.FirstOrDefault(n => n.Key == "ResourceTypes");
|
||||||
|
if (resources != null)
|
||||||
|
foreach (var r in resources.Value.Nodes)
|
||||||
|
ret[r.Key] = new ResourceTypeInfo(r.Value);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PopulateMapPreviewSignatureCells(Map map, Dictionary<string, ResourceTypeInfo> resources, List<(MPos, Color)> destinationBuffer)
|
||||||
|
{
|
||||||
|
var terrainInfo = map.Rules.TerrainInfo;
|
||||||
|
var colors = resources.Values.ToDictionary(
|
||||||
|
r => r.ResourceIndex,
|
||||||
|
r => terrainInfo.TerrainTypes[terrainInfo.GetTerrainIndex(r.TerrainType)].Color);
|
||||||
|
|
||||||
|
for (var i = 0; i < map.MapSize.X; i++)
|
||||||
|
{
|
||||||
|
for (var j = 0; j < map.MapSize.Y; j++)
|
||||||
|
{
|
||||||
|
var cell = new MPos(i, j);
|
||||||
|
if (colors.TryGetValue(map.Resources[cell].Type, out var color))
|
||||||
|
destinationBuffer.Add((cell, color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IMapPreviewSignatureInfo.PopulateMapPreviewSignatureCells(Map map, ActorInfo ai, ActorReference s, List<(MPos, Color)> destinationBuffer)
|
||||||
|
{
|
||||||
|
PopulateMapPreviewSignatureCells(map, ResourceTypes, destinationBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Create(ActorInitializer init) { return new ResourceLayer(init.Self, this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ResourceLayer : IResourceLayer, IWorldLoaded
|
public class ResourceLayer : IResourceLayer, IWorldLoaded
|
||||||
{
|
{
|
||||||
|
readonly ResourceLayerInfo info;
|
||||||
readonly World world;
|
readonly World world;
|
||||||
readonly BuildingInfluence buildingInfluence;
|
protected readonly Map Map;
|
||||||
protected readonly Dictionary<string, ResourceTypeInfo> ResourceInfo;
|
protected readonly BuildingInfluence BuildingInfluence;
|
||||||
protected readonly CellLayer<ResourceLayerContents> Content;
|
protected readonly CellLayer<ResourceLayerContents> Content;
|
||||||
|
protected readonly Dictionary<byte, string> ResourceTypesByIndex;
|
||||||
|
|
||||||
int resCells;
|
int resCells;
|
||||||
|
|
||||||
public event Action<CPos, string> CellChanged;
|
public event Action<CPos, string> CellChanged;
|
||||||
|
|
||||||
public ResourceLayer(Actor self)
|
public ResourceLayer(Actor self, ResourceLayerInfo info)
|
||||||
{
|
{
|
||||||
|
this.info = info;
|
||||||
world = self.World;
|
world = self.World;
|
||||||
buildingInfluence = self.Trait<BuildingInfluence>();
|
Map = world.Map;
|
||||||
ResourceInfo = self.TraitsImplementing<ResourceType>()
|
BuildingInfluence = self.Trait<BuildingInfluence>();
|
||||||
.ToDictionary(r => r.Info.Type, r => r.Info);
|
Content = new CellLayer<ResourceLayerContents>(Map);
|
||||||
|
ResourceTypesByIndex = info.ResourceTypes.ToDictionary(
|
||||||
Content = new CellLayer<ResourceLayerContents>(world.Map);
|
kv => kv.Value.ResourceIndex,
|
||||||
|
kv => kv.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetAdjacentCellsWith(string resourceType, CPos cell)
|
protected virtual void WorldLoaded(World w, WorldRenderer wr)
|
||||||
{
|
{
|
||||||
var sum = 0;
|
|
||||||
var directions = CVec.Directions;
|
|
||||||
for (var i = 0; i < directions.Length; i++)
|
|
||||||
{
|
|
||||||
var c = cell + directions[i];
|
|
||||||
if (Content.Contains(c) && Content[c].Type == resourceType)
|
|
||||||
++sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WorldLoaded(World w, WorldRenderer wr)
|
|
||||||
{
|
|
||||||
var resources = w.WorldActor.TraitsImplementing<ResourceType>()
|
|
||||||
.ToDictionary(r => r.Info.ResourceType, r => r.Info.Type);
|
|
||||||
|
|
||||||
foreach (var cell in w.Map.AllCells)
|
foreach (var cell in w.Map.AllCells)
|
||||||
{
|
{
|
||||||
if (!resources.TryGetValue(w.Map.Resources[cell].Type, out var resourceType))
|
var resource = world.Map.Resources[cell];
|
||||||
|
if (!ResourceTypesByIndex.TryGetValue(resource.Type, out var resourceType))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!AllowResourceAt(resourceType, cell))
|
if (!AllowResourceAt(resourceType, cell))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Content[cell] = CreateResourceCell(resourceType, cell);
|
Content[cell] = CreateResourceCell(resourceType, cell, resource.Index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!info.RecalculateResourceDensity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Set initial density based on the number of neighboring resources
|
||||||
foreach (var cell in w.Map.AllCells)
|
foreach (var cell in w.Map.AllCells)
|
||||||
{
|
{
|
||||||
var resourceType = Content[cell].Type;
|
var resource = Content[cell];
|
||||||
if (resourceType != null && ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
if (resource.Type == null || !info.ResourceTypes.TryGetValue(resource.Type, out var resourceInfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var adjacent = 0;
|
||||||
|
var directions = CVec.Directions;
|
||||||
|
for (var i = 0; i < directions.Length; i++)
|
||||||
{
|
{
|
||||||
// Set initial density based on the number of neighboring resources
|
var c = cell + directions[i];
|
||||||
// Adjacent includes the current cell, so is always >= 1
|
if (Content.Contains(c) && Content[c].Type == resource.Type)
|
||||||
var adjacent = GetAdjacentCellsWith(resourceType, cell);
|
++adjacent;
|
||||||
var density = int2.Lerp(0, resourceInfo.MaxDensity, adjacent, 9);
|
|
||||||
Content[cell] = new ResourceLayerContents(Content[cell].Type, Math.Max(density, 1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adjacent includes the current cell, so is always >= 1
|
||||||
|
var density = Math.Max(int2.Lerp(0, resourceInfo.MaxDensity, adjacent, 9), 1);
|
||||||
|
Content[cell] = new ResourceLayerContents(resource.Type, density);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AllowResourceAt(string resourceType, CPos cell)
|
void IWorldLoaded.WorldLoaded(World w, WorldRenderer wr) { WorldLoaded(w, wr); }
|
||||||
|
|
||||||
|
protected virtual bool AllowResourceAt(string resourceType, CPos cell)
|
||||||
{
|
{
|
||||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
if (!Map.Contains(cell) || Map.Ramp[cell] != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!world.Map.Contains(cell))
|
if (resourceType == null || !info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!resourceInfo.AllowedTerrainTypes.Contains(world.Map.GetTerrainInfo(cell).Type))
|
if (!resourceInfo.AllowedTerrainTypes.Contains(Map.GetTerrainInfo(cell).Type))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!resourceInfo.AllowUnderActors && world.ActorMap.AnyActorsAt(cell))
|
return BuildingInfluence.GetBuildingAt(cell) == null;
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!resourceInfo.AllowUnderBuildings && buildingInfluence.GetBuildingAt(cell) != null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return resourceInfo.AllowOnRamps || world.Map.Ramp[cell] == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceLayerContents CreateResourceCell(string resourceType, CPos cell)
|
ResourceLayerContents CreateResourceCell(string resourceType, CPos cell, int density)
|
||||||
{
|
{
|
||||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
if (!info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||||
{
|
{
|
||||||
world.Map.CustomTerrain[cell] = byte.MaxValue;
|
world.Map.CustomTerrain[cell] = byte.MaxValue;
|
||||||
return ResourceLayerContents.Empty;
|
return ResourceLayerContents.Empty;
|
||||||
@@ -132,7 +192,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
world.Map.CustomTerrain[cell] = world.Map.Rules.TerrainInfo.GetTerrainIndex(resourceInfo.TerrainType);
|
world.Map.CustomTerrain[cell] = world.Map.Rules.TerrainInfo.GetTerrainIndex(resourceInfo.TerrainType);
|
||||||
++resCells;
|
++resCells;
|
||||||
|
|
||||||
return new ResourceLayerContents(resourceType, 0);
|
return new ResourceLayerContents(resourceType, density.Clamp(1, resourceInfo.MaxDensity));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanAddResource(string resourceType, CPos cell, int amount = 1)
|
bool CanAddResource(string resourceType, CPos cell, int amount = 1)
|
||||||
@@ -140,7 +200,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (!world.Map.Contains(cell))
|
if (!world.Map.Contains(cell))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
if (resourceType == null || !info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var content = Content[cell];
|
var content = Content[cell];
|
||||||
@@ -158,12 +218,12 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (!Content.Contains(cell))
|
if (!Content.Contains(cell))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
if (resourceType == null || !info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var content = Content[cell];
|
var content = Content[cell];
|
||||||
if (content.Type == null)
|
if (content.Type == null)
|
||||||
content = CreateResourceCell(resourceType, cell);
|
content = CreateResourceCell(resourceType, cell, 0);
|
||||||
|
|
||||||
if (content.Type != resourceType)
|
if (content.Type != resourceType)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -192,7 +252,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (density == 0)
|
if (density == 0)
|
||||||
{
|
{
|
||||||
Content[cell] = ResourceLayerContents.Empty;
|
Content[cell] = ResourceLayerContents.Empty;
|
||||||
world.Map.CustomTerrain[cell] = byte.MaxValue;
|
Map.CustomTerrain[cell] = byte.MaxValue;
|
||||||
--resCells;
|
--resCells;
|
||||||
|
|
||||||
CellChanged?.Invoke(cell, null);
|
CellChanged?.Invoke(cell, null);
|
||||||
@@ -217,7 +277,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Content[cell] = ResourceLayerContents.Empty;
|
Content[cell] = ResourceLayerContents.Empty;
|
||||||
world.Map.CustomTerrain[cell] = byte.MaxValue;
|
Map.CustomTerrain[cell] = byte.MaxValue;
|
||||||
--resCells;
|
--resCells;
|
||||||
|
|
||||||
CellChanged?.Invoke(cell, null);
|
CellChanged?.Invoke(cell, null);
|
||||||
@@ -226,7 +286,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
ResourceLayerContents IResourceLayer.GetResource(CPos cell) { return Content.Contains(cell) ? Content[cell] : default; }
|
ResourceLayerContents IResourceLayer.GetResource(CPos cell) { return Content.Contains(cell) ? Content[cell] : default; }
|
||||||
int IResourceLayer.GetMaxDensity(string resourceType)
|
int IResourceLayer.GetMaxDensity(string resourceType)
|
||||||
{
|
{
|
||||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
if (!info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return resourceInfo.MaxDensity;
|
return resourceInfo.MaxDensity;
|
||||||
|
|||||||
@@ -21,56 +21,97 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
[Desc("Visualizes the state of the `ResourceLayer`.", " Attach this to the world actor.")]
|
[Desc("Visualizes the state of the `ResourceLayer`.", " Attach this to the world actor.")]
|
||||||
public class ResourceRendererInfo : TraitInfo, Requires<IResourceLayerInfo>
|
public class ResourceRendererInfo : TraitInfo, Requires<IResourceLayerInfo>
|
||||||
{
|
{
|
||||||
[FieldLoader.Require]
|
public class ResourceTypeInfo
|
||||||
[Desc("Only render these ResourceType names.")]
|
{
|
||||||
public readonly string[] RenderTypes = null;
|
[Desc("Sequence image that holds the different variants.")]
|
||||||
|
public readonly string Image = "resources";
|
||||||
|
|
||||||
|
[FieldLoader.Require]
|
||||||
|
[SequenceReference(nameof(Image))]
|
||||||
|
[Desc("Randomly chosen image sequences.")]
|
||||||
|
public readonly string[] Sequences = { };
|
||||||
|
|
||||||
|
[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;
|
||||||
|
|
||||||
|
public ResourceTypeInfo(MiniYaml yaml)
|
||||||
|
{
|
||||||
|
FieldLoader.Load(this, yaml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldLoader.LoadUsing(nameof(LoadResourceTypes))]
|
||||||
|
public readonly Dictionary<string, ResourceTypeInfo> ResourceTypes = null;
|
||||||
|
|
||||||
|
// Copied from ResourceLayerInfo
|
||||||
|
protected static object LoadResourceTypes(MiniYaml yaml)
|
||||||
|
{
|
||||||
|
var ret = new Dictionary<string, ResourceTypeInfo>();
|
||||||
|
var resources = yaml.Nodes.FirstOrDefault(n => n.Key == "ResourceTypes");
|
||||||
|
if (resources != null)
|
||||||
|
foreach (var r in resources.Value.Nodes)
|
||||||
|
ret[r.Key] = new ResourceTypeInfo(r.Value);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
public override object Create(ActorInitializer init) { return new ResourceRenderer(init.Self, this); }
|
public override object Create(ActorInitializer init) { return new ResourceRenderer(init.Self, this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ResourceRenderer : IResourceRenderer, IWorldLoaded, IRenderOverlay, ITickRender, INotifyActorDisposing
|
public class ResourceRenderer : IResourceRenderer, IWorldLoaded, IRenderOverlay, ITickRender, INotifyActorDisposing
|
||||||
{
|
{
|
||||||
protected readonly IResourceLayer ResourceLayer;
|
|
||||||
protected readonly CellLayer<RendererCellContents> RenderContent;
|
|
||||||
protected readonly ResourceRendererInfo Info;
|
protected readonly ResourceRendererInfo Info;
|
||||||
protected readonly Dictionary<string, ResourceType> ResourceInfo;
|
protected readonly IResourceLayer ResourceLayer;
|
||||||
|
protected readonly CellLayer<RendererCellContents> RenderContents;
|
||||||
|
protected readonly Dictionary<string, Dictionary<string, ISpriteSequence>> Variants = new Dictionary<string, Dictionary<string, ISpriteSequence>>();
|
||||||
|
protected readonly World World;
|
||||||
|
|
||||||
readonly HashSet<CPos> dirty = new HashSet<CPos>();
|
readonly HashSet<CPos> dirty = new HashSet<CPos>();
|
||||||
readonly Queue<CPos> cleanDirty = new Queue<CPos>();
|
readonly Queue<CPos> cleanDirty = new Queue<CPos>();
|
||||||
TerrainSpriteLayer shadowLayer;
|
TerrainSpriteLayer shadowLayer;
|
||||||
TerrainSpriteLayer spriteLayer;
|
TerrainSpriteLayer spriteLayer;
|
||||||
|
bool disposed;
|
||||||
|
|
||||||
public ResourceRenderer(Actor self, ResourceRendererInfo info)
|
public ResourceRenderer(Actor self, ResourceRendererInfo info)
|
||||||
{
|
{
|
||||||
Info = info;
|
Info = info;
|
||||||
|
World = self.World;
|
||||||
ResourceLayer = self.Trait<IResourceLayer>();
|
ResourceLayer = self.Trait<IResourceLayer>();
|
||||||
ResourceLayer.CellChanged += AddDirtyCell;
|
ResourceLayer.CellChanged += AddDirtyCell;
|
||||||
ResourceInfo = self.TraitsImplementing<ResourceType>()
|
RenderContents = new CellLayer<RendererCellContents>(self.World.Map);
|
||||||
.ToDictionary(r => r.Info.Type, r => r);
|
|
||||||
|
|
||||||
RenderContent = new CellLayer<RendererCellContents>(self.World.Map);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddDirtyCell(CPos cell, string resourceType)
|
void AddDirtyCell(CPos cell, string resourceType)
|
||||||
{
|
{
|
||||||
if (resourceType == null || Info.RenderTypes.Contains(resourceType))
|
if (resourceType == null || Info.ResourceTypes.ContainsKey(resourceType))
|
||||||
dirty.Add(cell);
|
dirty.Add(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IWorldLoaded.WorldLoaded(World w, WorldRenderer wr)
|
protected virtual void WorldLoaded(World w, WorldRenderer wr)
|
||||||
{
|
{
|
||||||
foreach (var resourceType in ResourceInfo.Values)
|
var sequences = w.Map.Rules.Sequences;
|
||||||
|
foreach (var kv in Info.ResourceTypes)
|
||||||
{
|
{
|
||||||
|
var resourceInfo = kv.Value;
|
||||||
|
var resourceVariants = resourceInfo.Sequences
|
||||||
|
.ToDictionary(v => v, v => sequences.GetSequence(resourceInfo.Image, v));
|
||||||
|
Variants.Add(kv.Key, resourceVariants);
|
||||||
|
|
||||||
if (spriteLayer == null)
|
if (spriteLayer == null)
|
||||||
{
|
{
|
||||||
var first = resourceType.Variants.First().Value.GetSprite(0);
|
var first = resourceVariants.First().Value.GetSprite(0);
|
||||||
var emptySprite = new Sprite(first.Sheet, Rectangle.Empty, TextureChannel.Alpha);
|
var emptySprite = new Sprite(first.Sheet, Rectangle.Empty, TextureChannel.Alpha);
|
||||||
spriteLayer = new TerrainSpriteLayer(w, wr, emptySprite, first.BlendMode, wr.World.Type != WorldType.Editor);
|
spriteLayer = new TerrainSpriteLayer(w, wr, emptySprite, first.BlendMode, wr.World.Type != WorldType.Editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shadowLayer == null)
|
if (shadowLayer == null)
|
||||||
{
|
{
|
||||||
var firstWithShadow = resourceType.Variants.Values.FirstOrDefault(v => v.ShadowStart > 0);
|
var firstWithShadow = resourceVariants.Values.FirstOrDefault(v => v.ShadowStart > 0);
|
||||||
if (firstWithShadow != null)
|
if (firstWithShadow != null)
|
||||||
{
|
{
|
||||||
var first = firstWithShadow.GetShadow(0, WAngle.Zero);
|
var first = firstWithShadow.GetShadow(0, WAngle.Zero);
|
||||||
@@ -80,30 +121,36 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
}
|
}
|
||||||
|
|
||||||
// All resources must share a blend mode
|
// All resources must share a blend mode
|
||||||
var sprites = resourceType.Variants.Values.SelectMany(v => Exts.MakeArray(v.Length, x => v.GetSprite(x)));
|
var sprites = resourceVariants.Values.SelectMany(v => Exts.MakeArray(v.Length, x => v.GetSprite(x)));
|
||||||
if (sprites.Any(s => s.BlendMode != spriteLayer.BlendMode))
|
if (sprites.Any(s => s.BlendMode != spriteLayer.BlendMode))
|
||||||
throw new InvalidDataException("Resource sprites specify different blend modes. "
|
throw new InvalidDataException("Resource sprites specify different blend modes. "
|
||||||
+ "Try using different ResourceRenderer traits for resource types that use different blend modes.");
|
+ "Try using different ResourceRenderer traits for resource types that use different blend modes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the RenderContent with the initial map state
|
// Initialize the RenderContent with the initial map state so it is visible
|
||||||
// because the shroud may not be enabled.
|
// through the fog with the Explored Map option enabled
|
||||||
foreach (var cell in w.Map.AllCells)
|
foreach (var cell in w.Map.AllCells)
|
||||||
{
|
{
|
||||||
var type = ResourceLayer.GetResource(cell).Type;
|
var resource = ResourceLayer.GetResource(cell);
|
||||||
if (type != null && Info.RenderTypes.Contains(type))
|
var rendererCellContents = CreateRenderCellContents(wr, resource, cell);
|
||||||
|
if (rendererCellContents.Type != null)
|
||||||
{
|
{
|
||||||
var resourceContent = ResourceLayer.GetResource(cell);
|
RenderContents[cell] = rendererCellContents;
|
||||||
if (!ResourceInfo.TryGetValue(resourceContent.Type, out var resourceType))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var rendererCellContents = new RendererCellContents(ChooseRandomVariant(resourceType), resourceType, resourceContent.Density);
|
|
||||||
RenderContent[cell] = rendererCellContents;
|
|
||||||
UpdateRenderedSprite(cell, rendererCellContents);
|
UpdateRenderedSprite(cell, rendererCellContents);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IWorldLoaded.WorldLoaded(World w, WorldRenderer wr) { WorldLoaded(w, wr); }
|
||||||
|
|
||||||
|
protected RendererCellContents CreateRenderCellContents(WorldRenderer wr, ResourceLayerContents contents, CPos cell)
|
||||||
|
{
|
||||||
|
if (contents.Type != null && contents.Density > 0 && Info.ResourceTypes.TryGetValue(contents.Type, out var resourceInfo))
|
||||||
|
return new RendererCellContents(contents.Type, contents.Density, resourceInfo, ChooseVariant(contents.Type, cell), wr.Palette(resourceInfo.Palette));
|
||||||
|
|
||||||
|
return RendererCellContents.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
protected void UpdateSpriteLayers(CPos cell, ISpriteSequence sequence, int frame, PaletteReference palette)
|
protected void UpdateSpriteLayers(CPos cell, ISpriteSequence sequence, int frame, PaletteReference palette)
|
||||||
{
|
{
|
||||||
// resource.Type is meaningless (and may be null) if resource.Sequence is null
|
// resource.Type is meaningless (and may be null) if resource.Sequence is null
|
||||||
@@ -132,27 +179,21 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (!ResourceLayer.IsVisible(cell))
|
if (!ResourceLayer.IsVisible(cell))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var resourceContent = ResourceLayer.GetResource(cell);
|
var rendererCellContents = RendererCellContents.Empty;
|
||||||
if (resourceContent.Density > 0)
|
var contents = ResourceLayer.GetResource(cell);
|
||||||
|
if (contents.Density > 0)
|
||||||
{
|
{
|
||||||
var cellContents = RenderContent[cell];
|
rendererCellContents = RenderContents[cell];
|
||||||
var resourceData = ResourceInfo[resourceContent.Type];
|
|
||||||
var variant = cellContents.Variant;
|
|
||||||
if (cellContents.Variant == null || cellContents.Type.Info.Type != resourceContent.Type)
|
|
||||||
variant = ChooseRandomVariant(resourceData);
|
|
||||||
|
|
||||||
var rendererCellContents = new RendererCellContents(variant, resourceData, resourceContent.Density);
|
// Contents are the same, so just update the density
|
||||||
RenderContent[cell] = rendererCellContents;
|
if (rendererCellContents.Type == contents.Type)
|
||||||
|
rendererCellContents = new RendererCellContents(rendererCellContents, contents.Density);
|
||||||
UpdateRenderedSprite(cell, rendererCellContents);
|
else
|
||||||
}
|
rendererCellContents = CreateRenderCellContents(wr, contents, cell);
|
||||||
else
|
|
||||||
{
|
|
||||||
var rendererCellContents = RendererCellContents.Empty;
|
|
||||||
RenderContent[cell] = rendererCellContents;
|
|
||||||
UpdateRenderedSprite(cell, rendererCellContents);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RenderContents[cell] = rendererCellContents;
|
||||||
|
UpdateRenderedSprite(cell, rendererCellContents);
|
||||||
cleanDirty.Enqueue(cell);
|
cleanDirty.Enqueue(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,28 +203,17 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
protected virtual void UpdateRenderedSprite(CPos cell, RendererCellContents content)
|
protected virtual void UpdateRenderedSprite(CPos cell, RendererCellContents content)
|
||||||
{
|
{
|
||||||
var density = content.Density;
|
|
||||||
var type = content.Type;
|
|
||||||
if (content.Density > 0)
|
if (content.Density > 0)
|
||||||
{
|
{
|
||||||
// The call chain for this method (that starts with AddDirtyCell()) guarantees
|
var maxDensity = ResourceLayer.GetMaxDensity(content.Type);
|
||||||
// that the new content type would still be suitable for this renderer,
|
var frame = int2.Lerp(0, content.Sequence.Length - 1, content.Density, maxDensity);
|
||||||
// but that is a bit too fragile to rely on in case the code starts changing.
|
UpdateSpriteLayers(cell, content.Sequence, frame, content.Palette);
|
||||||
if (!Info.RenderTypes.Contains(type.Info.Type))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var sprites = type.Variants[content.Variant];
|
|
||||||
var maxDensity = type.Info.MaxDensity;
|
|
||||||
var frame = int2.Lerp(0, sprites.Length - 1, density, maxDensity);
|
|
||||||
|
|
||||||
UpdateSpriteLayers(cell, sprites, frame, type.Palette);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
UpdateSpriteLayers(cell, null, 0, null);
|
UpdateSpriteLayers(cell, null, 0, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool disposed;
|
protected virtual void Disposing(Actor self)
|
||||||
void INotifyActorDisposing.Disposing(Actor self)
|
|
||||||
{
|
{
|
||||||
if (disposed)
|
if (disposed)
|
||||||
return;
|
return;
|
||||||
@@ -196,16 +226,18 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
disposed = true;
|
disposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual string ChooseRandomVariant(ResourceType t)
|
void INotifyActorDisposing.Disposing(Actor self) { Disposing(self); }
|
||||||
|
|
||||||
|
protected virtual ISpriteSequence ChooseVariant(string resourceType, CPos cell)
|
||||||
{
|
{
|
||||||
return t.Variants.Keys.Random(Game.CosmeticRandom);
|
return Variants[resourceType].Values.Random(World.LocalRandom);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual string GetRenderedResourceType(CPos cell) { return RenderContent[cell].Type.Info.Type; }
|
protected virtual string GetRenderedResourceType(CPos cell) { return RenderContents[cell].Type; }
|
||||||
|
|
||||||
protected virtual string GetRenderedResourceTooltip(CPos cell) { return RenderContent[cell].Type?.Info.Name; }
|
protected virtual string GetRenderedResourceTooltip(CPos cell) { return RenderContents[cell].Info?.Name; }
|
||||||
|
|
||||||
IEnumerable<string> IResourceRenderer.ResourceTypes => ResourceInfo.Keys;
|
IEnumerable<string> IResourceRenderer.ResourceTypes => Info.ResourceTypes.Keys;
|
||||||
|
|
||||||
string IResourceRenderer.GetRenderedResourceType(CPos cell) { return GetRenderedResourceType(cell); }
|
string IResourceRenderer.GetRenderedResourceType(CPos cell) { return GetRenderedResourceType(cell); }
|
||||||
|
|
||||||
@@ -213,13 +245,16 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
IEnumerable<IRenderable> IResourceRenderer.RenderUIPreview(WorldRenderer wr, string resourceType, int2 origin, float scale)
|
IEnumerable<IRenderable> IResourceRenderer.RenderUIPreview(WorldRenderer wr, string resourceType, int2 origin, float scale)
|
||||||
{
|
{
|
||||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
if (!Variants.TryGetValue(resourceType, out var variant))
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
var sequence = resourceInfo.Variants.First().Value;
|
if (!Info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
var sequence = variant.First().Value;
|
||||||
var sprite = sequence.GetSprite(sequence.Length - 1);
|
var sprite = sequence.GetSprite(sequence.Length - 1);
|
||||||
var shadow = sequence.GetShadow(sequence.Length - 1, WAngle.Zero);
|
var shadow = sequence.GetShadow(sequence.Length - 1, WAngle.Zero);
|
||||||
var palette = resourceInfo.Palette;
|
var palette = wr.Palette(resourceInfo.Palette);
|
||||||
|
|
||||||
if (shadow != null)
|
if (shadow != null)
|
||||||
yield return new UISpriteRenderable(shadow, WPos.Zero, origin, 0, palette, scale);
|
yield return new UISpriteRenderable(shadow, WPos.Zero, origin, 0, palette, scale);
|
||||||
@@ -229,14 +264,17 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
IEnumerable<IRenderable> IResourceRenderer.RenderPreview(WorldRenderer wr, string resourceType, WPos origin)
|
IEnumerable<IRenderable> IResourceRenderer.RenderPreview(WorldRenderer wr, string resourceType, WPos origin)
|
||||||
{
|
{
|
||||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
if (!Variants.TryGetValue(resourceType, out var variant))
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
var sequence = resourceInfo.Variants.First().Value;
|
if (!Info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
var sequence = variant.First().Value;
|
||||||
var sprite = sequence.GetSprite(sequence.Length - 1);
|
var sprite = sequence.GetSprite(sequence.Length - 1);
|
||||||
var shadow = sequence.GetShadow(sequence.Length - 1, WAngle.Zero);
|
var shadow = sequence.GetShadow(sequence.Length - 1, WAngle.Zero);
|
||||||
var alpha = sequence.GetAlpha(sequence.Length - 1);
|
var alpha = sequence.GetAlpha(sequence.Length - 1);
|
||||||
var palette = resourceInfo.Palette;
|
var palette = wr.Palette(resourceInfo.Palette);
|
||||||
var tintModifiers = sequence.IgnoreWorldTint ? TintModifiers.IgnoreWorldTint : TintModifiers.None;
|
var tintModifiers = sequence.IgnoreWorldTint ? TintModifiers.IgnoreWorldTint : TintModifiers.None;
|
||||||
|
|
||||||
if (shadow != null)
|
if (shadow != null)
|
||||||
@@ -247,17 +285,30 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public readonly struct RendererCellContents
|
public readonly struct RendererCellContents
|
||||||
{
|
{
|
||||||
public readonly string Variant;
|
public readonly string Type;
|
||||||
public readonly ResourceType Type;
|
public readonly ResourceRendererInfo.ResourceTypeInfo Info;
|
||||||
|
public readonly ISpriteSequence Sequence;
|
||||||
|
public readonly PaletteReference Palette;
|
||||||
public readonly int Density;
|
public readonly int Density;
|
||||||
|
|
||||||
public static readonly RendererCellContents Empty = default;
|
public static readonly RendererCellContents Empty = default;
|
||||||
|
|
||||||
public RendererCellContents(string variant, ResourceType type, int density)
|
public RendererCellContents(string resourceType, int density, ResourceRendererInfo.ResourceTypeInfo info, ISpriteSequence sequence, PaletteReference palette)
|
||||||
{
|
{
|
||||||
Variant = variant;
|
Type = resourceType;
|
||||||
Type = type;
|
|
||||||
Density = density;
|
Density = density;
|
||||||
|
Info = info;
|
||||||
|
Sequence = sequence;
|
||||||
|
Palette = palette;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RendererCellContents(RendererCellContents contents, int density)
|
||||||
|
{
|
||||||
|
Type = contents.Type;
|
||||||
|
Density = density;
|
||||||
|
Info = contents.Info;
|
||||||
|
Sequence = contents.Sequence;
|
||||||
|
Palette = contents.Palette;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,107 +0,0 @@
|
|||||||
#region Copyright & License Information
|
|
||||||
/*
|
|
||||||
* Copyright 2007-2020 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 OpenRA.Graphics;
|
|
||||||
using OpenRA.Primitives;
|
|
||||||
using OpenRA.Traits;
|
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Traits
|
|
||||||
{
|
|
||||||
public class ResourceTypeInfo : TraitInfo, IMapPreviewSignatureInfo
|
|
||||||
{
|
|
||||||
[Desc("Sequence image that holds the different variants.")]
|
|
||||||
public readonly string Image = "resources";
|
|
||||||
|
|
||||||
[FieldLoader.Require]
|
|
||||||
[SequenceReference(nameof(Image))]
|
|
||||||
[Desc("Randomly chosen image sequences.")]
|
|
||||||
public readonly string[] Sequences = { };
|
|
||||||
|
|
||||||
[PaletteReference]
|
|
||||||
[Desc("Palette used for rendering the resource sprites.")]
|
|
||||||
public readonly string Palette = TileSet.TerrainPaletteInternalName;
|
|
||||||
|
|
||||||
[Desc("Resource index used in the binary map data.")]
|
|
||||||
public readonly int ResourceType = 1;
|
|
||||||
|
|
||||||
[Desc("Credit value of a single resource unit.")]
|
|
||||||
public readonly int ValuePerUnit = 0;
|
|
||||||
|
|
||||||
[Desc("Maximum number of resource units allowed in a single cell.")]
|
|
||||||
public readonly int MaxDensity = 10;
|
|
||||||
|
|
||||||
[FieldLoader.Require]
|
|
||||||
[Desc("Resource identifier used by other traits.")]
|
|
||||||
public readonly string Type = null;
|
|
||||||
|
|
||||||
[FieldLoader.Require]
|
|
||||||
[Desc("Resource name used by tooltips.")]
|
|
||||||
public readonly string Name = null;
|
|
||||||
|
|
||||||
[FieldLoader.Require]
|
|
||||||
[Desc("Terrain type used to determine unit movement and minimap colors.")]
|
|
||||||
public readonly string TerrainType = null;
|
|
||||||
|
|
||||||
[Desc("Terrain types that this resource can spawn on.")]
|
|
||||||
public readonly HashSet<string> AllowedTerrainTypes = new HashSet<string>();
|
|
||||||
|
|
||||||
[Desc("Allow resource to spawn under Mobile actors.")]
|
|
||||||
public readonly bool AllowUnderActors = false;
|
|
||||||
|
|
||||||
[Desc("Allow resource to spawn under Buildings.")]
|
|
||||||
public readonly bool AllowUnderBuildings = false;
|
|
||||||
|
|
||||||
[Desc("Allow resource to spawn on ramp tiles.")]
|
|
||||||
public readonly bool AllowOnRamps = false;
|
|
||||||
|
|
||||||
void IMapPreviewSignatureInfo.PopulateMapPreviewSignatureCells(Map map, ActorInfo ai, ActorReference s, List<(MPos, Color)> destinationBuffer)
|
|
||||||
{
|
|
||||||
var terrainInfo = map.Rules.TerrainInfo;
|
|
||||||
var color = terrainInfo.TerrainTypes[terrainInfo.GetTerrainIndex(TerrainType)].Color;
|
|
||||||
|
|
||||||
for (var i = 0; i < map.MapSize.X; i++)
|
|
||||||
{
|
|
||||||
for (var j = 0; j < map.MapSize.Y; j++)
|
|
||||||
{
|
|
||||||
var cell = new MPos(i, j);
|
|
||||||
if (map.Resources[cell].Type == ResourceType)
|
|
||||||
destinationBuffer.Add((cell, color));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Create(ActorInitializer init) { return new ResourceType(this, init.World); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ResourceType : IWorldLoaded
|
|
||||||
{
|
|
||||||
public readonly ResourceTypeInfo Info;
|
|
||||||
public PaletteReference Palette { get; private set; }
|
|
||||||
public readonly Dictionary<string, ISpriteSequence> Variants;
|
|
||||||
|
|
||||||
public ResourceType(ResourceTypeInfo info, World world)
|
|
||||||
{
|
|
||||||
Info = info;
|
|
||||||
Variants = new Dictionary<string, ISpriteSequence>();
|
|
||||||
foreach (var v in info.Sequences)
|
|
||||||
{
|
|
||||||
var seq = world.Map.Rules.Sequences.GetSequence(Info.Image, v);
|
|
||||||
Variants.Add(v, seq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WorldLoaded(World w, WorldRenderer wr)
|
|
||||||
{
|
|
||||||
Palette = wr.Palette(Info.Palette);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2020 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;
|
||||||
|
|
||||||
|
namespace OpenRA.Mods.Common.UpdateRules.Rules
|
||||||
|
{
|
||||||
|
public class RemoveResourceType : UpdateRule
|
||||||
|
{
|
||||||
|
public override string Name => "Remove ResourceType definitions.";
|
||||||
|
|
||||||
|
public override string Description =>
|
||||||
|
"The ResourceType trait has been removed, and resource definitions moved to the\n" +
|
||||||
|
"ResourceLayer, EditorResourceLayer, ResourceRenderer, and PlayerResources traits.";
|
||||||
|
|
||||||
|
MiniYaml resourceLayer;
|
||||||
|
MiniYaml resourceRenderer;
|
||||||
|
MiniYaml values;
|
||||||
|
|
||||||
|
public override IEnumerable<string> BeforeUpdate(ModData modData)
|
||||||
|
{
|
||||||
|
resourceLayer = new MiniYaml("");
|
||||||
|
resourceRenderer = new MiniYaml("");
|
||||||
|
values = new MiniYaml("");
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<string> AfterUpdate(ModData modData)
|
||||||
|
{
|
||||||
|
if (resourceLayer.Nodes.Any())
|
||||||
|
yield return "Add the following definitions to your ResourceLayer and EditorResourceLayer definitions:\n\t" +
|
||||||
|
"RecalculateResourceDensity: true\n\t" +
|
||||||
|
resourceLayer.ToLines("ResourceTypes").JoinWith("\n\t");
|
||||||
|
|
||||||
|
if (resourceLayer.Nodes.Any())
|
||||||
|
yield return "Add the following definitions to your ResourceRenderer definition:\n\t" +
|
||||||
|
resourceRenderer.ToLines("ResourceTypes").JoinWith("\n\t");
|
||||||
|
|
||||||
|
if (values.Nodes.Any())
|
||||||
|
yield return "Add the following definition to your ^BasePlayer definition:\n\t" +
|
||||||
|
"PlayerResources:\n\t\t" +
|
||||||
|
values.ToLines("ResourceValues").JoinWith("\n\t\t");
|
||||||
|
|
||||||
|
if (resourceLayer.Nodes.Any())
|
||||||
|
yield return "Support for AllowUnderActors, AllowUnderBuildings, and AllowOnRamps have been removed.\n" +
|
||||||
|
"You must define a custom ResourceLayer subclass if you want to customize the default behaviour.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
|
||||||
|
{
|
||||||
|
foreach (var resourceNode in actorNode.ChildrenMatching("ResourceType"))
|
||||||
|
{
|
||||||
|
var typeNode = resourceNode.LastChildMatching("Type");
|
||||||
|
if (typeNode != null)
|
||||||
|
{
|
||||||
|
var resourceLayerNode = new MiniYamlNode(typeNode.Value.Value, "");
|
||||||
|
resourceLayer.Nodes.Add(resourceLayerNode);
|
||||||
|
|
||||||
|
var resourceRendererNode = new MiniYamlNode(typeNode.Value.Value, "");
|
||||||
|
resourceRenderer.Nodes.Add(resourceRendererNode);
|
||||||
|
|
||||||
|
var indexNode = resourceNode.LastChildMatching("ResourceType");
|
||||||
|
if (indexNode != null)
|
||||||
|
{
|
||||||
|
indexNode.RenameKey("ResourceIndex");
|
||||||
|
resourceLayerNode.AddNode(indexNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
var terrainTypeNode = resourceNode.LastChildMatching("TerrainType");
|
||||||
|
if (terrainTypeNode != null)
|
||||||
|
resourceLayerNode.AddNode(terrainTypeNode);
|
||||||
|
|
||||||
|
var allowedTerrainNode = resourceNode.LastChildMatching("AllowedTerrainTypes");
|
||||||
|
if (allowedTerrainNode != null)
|
||||||
|
resourceLayerNode.AddNode(allowedTerrainNode);
|
||||||
|
|
||||||
|
var maxDensityNode = resourceNode.LastChildMatching("MaxDensity");
|
||||||
|
if (maxDensityNode != null)
|
||||||
|
resourceLayerNode.AddNode(maxDensityNode);
|
||||||
|
|
||||||
|
var valueNode = resourceNode.LastChildMatching("ValuePerUnit");
|
||||||
|
if (valueNode != null)
|
||||||
|
values.Nodes.Add(new MiniYamlNode(typeNode.Value.Value, valueNode.Value.Value));
|
||||||
|
|
||||||
|
var imageNode = resourceNode.LastChildMatching("Image");
|
||||||
|
if (imageNode != null)
|
||||||
|
resourceRendererNode.AddNode(imageNode);
|
||||||
|
|
||||||
|
var sequencesNode = resourceNode.LastChildMatching("Sequences");
|
||||||
|
if (sequencesNode != null)
|
||||||
|
resourceRendererNode.AddNode(sequencesNode);
|
||||||
|
|
||||||
|
var paletteNode = resourceNode.LastChildMatching("Palette");
|
||||||
|
if (paletteNode != null)
|
||||||
|
resourceRendererNode.AddNode(paletteNode);
|
||||||
|
|
||||||
|
var nameNode = resourceNode.LastChildMatching("Name");
|
||||||
|
if (nameNode != null)
|
||||||
|
resourceRendererNode.AddNode(nameNode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
yield return "Unable to process definition:\n" +
|
||||||
|
resourceNode.Value.ToLines(resourceNode.Key).JoinWith("\n") + "\n\n" +
|
||||||
|
"This override has been removed and must be manually reimplemented if still needed.";
|
||||||
|
}
|
||||||
|
|
||||||
|
actorNode.RemoveNodes("ResourceType");
|
||||||
|
|
||||||
|
foreach (var resourceRendererNode in actorNode.ChildrenMatching("ResourceRenderer"))
|
||||||
|
resourceRendererNode.RemoveNodes("RenderTypes");
|
||||||
|
|
||||||
|
foreach (var resourceRendererNode in actorNode.ChildrenMatching("D2kResourceRenderer"))
|
||||||
|
resourceRendererNode.RemoveNodes("RenderTypes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -91,6 +91,7 @@ namespace OpenRA.Mods.Common.UpdateRules
|
|||||||
new RemovePlaceBuildingPalette(),
|
new RemovePlaceBuildingPalette(),
|
||||||
new ReplaceShadowPalette(),
|
new ReplaceShadowPalette(),
|
||||||
new ReplaceResourceValueModifiers(),
|
new ReplaceResourceValueModifiers(),
|
||||||
|
new RemoveResourceType(),
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenRA.Graphics;
|
||||||
using OpenRA.Mods.Common.Traits;
|
using OpenRA.Mods.Common.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.D2k.Traits
|
namespace OpenRA.Mods.D2k.Traits
|
||||||
@@ -94,36 +95,36 @@ namespace OpenRA.Mods.D2k.Traits
|
|||||||
public D2kResourceRenderer(Actor self, D2kResourceRendererInfo info)
|
public D2kResourceRenderer(Actor self, D2kResourceRendererInfo info)
|
||||||
: base(self, info) { }
|
: base(self, info) { }
|
||||||
|
|
||||||
bool CellContains(CPos c, ResourceType t)
|
bool CellContains(CPos cell, string resourceType)
|
||||||
{
|
{
|
||||||
return RenderContent.Contains(c) && RenderContent[c].Type == t;
|
return RenderContents.Contains(cell) && RenderContents[cell].Type == resourceType;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearSides FindClearSides(ResourceType t, CPos p)
|
ClearSides FindClearSides(CPos cell, string resourceType)
|
||||||
{
|
{
|
||||||
var ret = ClearSides.None;
|
var ret = ClearSides.None;
|
||||||
if (!CellContains(p + new CVec(0, -1), t))
|
if (!CellContains(cell + new CVec(0, -1), resourceType))
|
||||||
ret |= ClearSides.Top | ClearSides.TopLeft | ClearSides.TopRight;
|
ret |= ClearSides.Top | ClearSides.TopLeft | ClearSides.TopRight;
|
||||||
|
|
||||||
if (!CellContains(p + new CVec(-1, 0), t))
|
if (!CellContains(cell + new CVec(-1, 0), resourceType))
|
||||||
ret |= ClearSides.Left | ClearSides.TopLeft | ClearSides.BottomLeft;
|
ret |= ClearSides.Left | ClearSides.TopLeft | ClearSides.BottomLeft;
|
||||||
|
|
||||||
if (!CellContains(p + new CVec(1, 0), t))
|
if (!CellContains(cell + new CVec(1, 0), resourceType))
|
||||||
ret |= ClearSides.Right | ClearSides.TopRight | ClearSides.BottomRight;
|
ret |= ClearSides.Right | ClearSides.TopRight | ClearSides.BottomRight;
|
||||||
|
|
||||||
if (!CellContains(p + new CVec(0, 1), t))
|
if (!CellContains(cell + new CVec(0, 1), resourceType))
|
||||||
ret |= ClearSides.Bottom | ClearSides.BottomLeft | ClearSides.BottomRight;
|
ret |= ClearSides.Bottom | ClearSides.BottomLeft | ClearSides.BottomRight;
|
||||||
|
|
||||||
if (!CellContains(p + new CVec(-1, -1), t))
|
if (!CellContains(cell + new CVec(-1, -1), resourceType))
|
||||||
ret |= ClearSides.TopLeft;
|
ret |= ClearSides.TopLeft;
|
||||||
|
|
||||||
if (!CellContains(p + new CVec(1, -1), t))
|
if (!CellContains(cell + new CVec(1, -1), resourceType))
|
||||||
ret |= ClearSides.TopRight;
|
ret |= ClearSides.TopRight;
|
||||||
|
|
||||||
if (!CellContains(p + new CVec(-1, 1), t))
|
if (!CellContains(cell + new CVec(-1, 1), resourceType))
|
||||||
ret |= ClearSides.BottomLeft;
|
ret |= ClearSides.BottomLeft;
|
||||||
|
|
||||||
if (!CellContains(p + new CVec(1, 1), t))
|
if (!CellContains(cell + new CVec(1, 1), resourceType))
|
||||||
ret |= ClearSides.BottomRight;
|
ret |= ClearSides.BottomRight;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -135,39 +136,27 @@ namespace OpenRA.Mods.D2k.Traits
|
|||||||
|
|
||||||
var directions = CVec.Directions;
|
var directions = CVec.Directions;
|
||||||
for (var i = 0; i < directions.Length; i++)
|
for (var i = 0; i < directions.Length; i++)
|
||||||
UpdateRenderedSpriteInner(cell + directions[i]);
|
{
|
||||||
}
|
var neighbour = cell + directions[i];
|
||||||
|
UpdateRenderedSpriteInner(neighbour, RenderContents[neighbour]);
|
||||||
void UpdateRenderedSpriteInner(CPos cell)
|
}
|
||||||
{
|
|
||||||
UpdateRenderedSpriteInner(cell, RenderContent[cell]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateRenderedSpriteInner(CPos cell, RendererCellContents content)
|
void UpdateRenderedSpriteInner(CPos cell, RendererCellContents content)
|
||||||
{
|
{
|
||||||
var density = content.Density;
|
if (content.Density > 0)
|
||||||
var renderType = content.Type;
|
|
||||||
|
|
||||||
if (density > 0 && renderType != null)
|
|
||||||
{
|
{
|
||||||
// The call chain for this method (that starts with AddDirtyCell()) guarantees
|
var clear = FindClearSides(cell, content.Type);
|
||||||
// that the new content type would still be suitable for this renderer,
|
|
||||||
// but that is a bit too fragile to rely on in case the code starts changing.
|
|
||||||
if (!Info.RenderTypes.Contains(renderType.Info.Type))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var clear = FindClearSides(renderType, cell);
|
|
||||||
|
|
||||||
if (clear == ClearSides.None)
|
if (clear == ClearSides.None)
|
||||||
{
|
{
|
||||||
var sprites = renderType.Variants[content.Variant];
|
var maxDensity = ResourceLayer.GetMaxDensity(content.Type);
|
||||||
var frame = density > renderType.Info.MaxDensity / 2 ? 1 : 0;
|
var index = content.Density > maxDensity / 2 ? 1 : 0;
|
||||||
|
UpdateSpriteLayers(cell, content.Sequence, index, content.Palette);
|
||||||
UpdateSpriteLayers(cell, sprites, frame, renderType.Palette);
|
|
||||||
}
|
}
|
||||||
else if (SpriteMap.TryGetValue(clear, out var index))
|
else if (SpriteMap.TryGetValue(clear, out var index))
|
||||||
{
|
{
|
||||||
UpdateSpriteLayers(cell, renderType.Variants.First().Value, index, renderType.Palette);
|
UpdateSpriteLayers(cell, content.Sequence, index, content.Palette);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw new InvalidOperationException("SpriteMap does not contain an index for ClearSides type '{0}'".F(clear));
|
throw new InvalidOperationException("SpriteMap does not contain an index for ClearSides type '{0}'".F(clear));
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
^BasePlayer:
|
^BasePlayer:
|
||||||
AlwaysVisible:
|
AlwaysVisible:
|
||||||
Shroud:
|
Shroud:
|
||||||
|
PlayerResources:
|
||||||
|
ResourceValues:
|
||||||
|
Tiberium: 35
|
||||||
|
BlueTiberium: 60
|
||||||
|
|
||||||
EditorPlayer:
|
EditorPlayer:
|
||||||
Inherits: ^BasePlayer
|
Inherits: ^BasePlayer
|
||||||
|
|||||||
@@ -126,30 +126,16 @@
|
|||||||
Name: Nod
|
Name: Nod
|
||||||
InternalName: nod
|
InternalName: nod
|
||||||
Description: Brotherhood of Nod\nThe Brotherhood is a religious cult centered around their leader Kane\nand the alien substance Tiberium. They utilize stealth technology\nand guerilla tactics to defeat those who oppose them.
|
Description: Brotherhood of Nod\nThe Brotherhood is a religious cult centered around their leader Kane\nand the alien substance Tiberium. They utilize stealth technology\nand guerilla tactics to defeat those who oppose them.
|
||||||
ResourceType@green-tib:
|
|
||||||
Type: Tiberium
|
|
||||||
Name: Tiberium
|
|
||||||
ResourceType: 1
|
|
||||||
Palette: staticterrain
|
|
||||||
TerrainType: Tiberium
|
|
||||||
Sequences: ti1,ti2,ti3,ti4,ti5,ti6,ti7,ti8,ti9,ti10,ti11,ti12
|
|
||||||
MaxDensity: 12
|
|
||||||
ValuePerUnit: 35
|
|
||||||
AllowedTerrainTypes: Clear,Road
|
|
||||||
AllowUnderActors: true
|
|
||||||
ResourceType@blue-tib:
|
|
||||||
Type: BlueTiberium
|
|
||||||
Name: Tiberium
|
|
||||||
ResourceType: 2
|
|
||||||
Palette: bluetiberium
|
|
||||||
TerrainType: BlueTiberium
|
|
||||||
Sequences: bti1,bti2,bti3,bti4,bti5,bti6,bti7,bti8,bti9,bti10,bti11,bti12
|
|
||||||
MaxDensity: 12
|
|
||||||
ValuePerUnit: 60
|
|
||||||
AllowedTerrainTypes: Clear,Road
|
|
||||||
AllowUnderActors: true
|
|
||||||
ResourceRenderer:
|
ResourceRenderer:
|
||||||
RenderTypes: Tiberium, BlueTiberium
|
ResourceTypes:
|
||||||
|
Tiberium:
|
||||||
|
Sequences: ti1, ti2, ti3, ti4, ti5, ti6, ti7, ti8, ti9, ti10, ti11, ti12
|
||||||
|
Palette: staticterrain
|
||||||
|
Name: Tiberium
|
||||||
|
BlueTiberium:
|
||||||
|
Sequences: bti1, bti2, bti3, bti4, bti5, bti6, bti7, bti8, bti9, bti10, bti11, bti12
|
||||||
|
Palette: bluetiberium
|
||||||
|
Name: Tiberium
|
||||||
|
|
||||||
World:
|
World:
|
||||||
Inherits: ^BaseWorld
|
Inherits: ^BaseWorld
|
||||||
@@ -178,6 +164,18 @@ World:
|
|||||||
SmokeImage: smoke_m
|
SmokeImage: smoke_m
|
||||||
SmokeSequences: idle
|
SmokeSequences: idle
|
||||||
ResourceLayer:
|
ResourceLayer:
|
||||||
|
RecalculateResourceDensity: true
|
||||||
|
ResourceTypes:
|
||||||
|
Tiberium:
|
||||||
|
ResourceIndex: 1
|
||||||
|
TerrainType: Tiberium
|
||||||
|
AllowedTerrainTypes: Clear, Road
|
||||||
|
MaxDensity: 12
|
||||||
|
BlueTiberium:
|
||||||
|
ResourceIndex: 2
|
||||||
|
TerrainType: BlueTiberium
|
||||||
|
AllowedTerrainTypes: Clear, Road
|
||||||
|
MaxDensity: 12
|
||||||
ResourceClaimLayer:
|
ResourceClaimLayer:
|
||||||
WarheadDebugOverlay:
|
WarheadDebugOverlay:
|
||||||
CustomTerrainDebugOverlay:
|
CustomTerrainDebugOverlay:
|
||||||
@@ -262,6 +260,18 @@ EditorWorld:
|
|||||||
EditorActorLayer:
|
EditorActorLayer:
|
||||||
EditorCursorLayer:
|
EditorCursorLayer:
|
||||||
EditorResourceLayer:
|
EditorResourceLayer:
|
||||||
|
RecalculateResourceDensity: true
|
||||||
|
ResourceTypes:
|
||||||
|
Tiberium:
|
||||||
|
ResourceIndex: 1
|
||||||
|
TerrainType: Tiberium
|
||||||
|
AllowedTerrainTypes: Clear, Road
|
||||||
|
MaxDensity: 12
|
||||||
|
BlueTiberium:
|
||||||
|
ResourceIndex: 2
|
||||||
|
TerrainType: BlueTiberium
|
||||||
|
AllowedTerrainTypes: Clear, Road
|
||||||
|
MaxDensity: 12
|
||||||
EditorSelectionLayer:
|
EditorSelectionLayer:
|
||||||
LoadWidgetAtGameStart:
|
LoadWidgetAtGameStart:
|
||||||
EditorActionManager:
|
EditorActionManager:
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
Player:
|
Player:
|
||||||
-ConquestVictoryConditions:
|
-ConquestVictoryConditions:
|
||||||
-HarvesterInsurance:
|
-HarvesterInsurance:
|
||||||
|
PlayerResources:
|
||||||
|
ResourceValues:
|
||||||
|
Spice: 0
|
||||||
|
|
||||||
World:
|
World:
|
||||||
-CrateSpawner:
|
-CrateSpawner:
|
||||||
-SpawnStartingUnits:
|
-SpawnStartingUnits:
|
||||||
-MapStartingLocations:
|
-MapStartingLocations:
|
||||||
ResourceType@Spice:
|
|
||||||
ValuePerUnit: 0
|
|
||||||
ActorSpawnManager:
|
ActorSpawnManager:
|
||||||
Minimum: 1
|
Minimum: 1
|
||||||
Maximum: 3
|
Maximum: 3
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
^BasePlayer:
|
^BasePlayer:
|
||||||
AlwaysVisible:
|
AlwaysVisible:
|
||||||
Shroud:
|
Shroud:
|
||||||
|
PlayerResources:
|
||||||
|
ResourceValues:
|
||||||
|
Spice: 25
|
||||||
|
|
||||||
EditorPlayer:
|
EditorPlayer:
|
||||||
Inherits: ^BasePlayer
|
Inherits: ^BasePlayer
|
||||||
|
|||||||
@@ -107,19 +107,12 @@
|
|||||||
Name: Fremen
|
Name: Fremen
|
||||||
InternalName: fremen
|
InternalName: fremen
|
||||||
Selectable: false
|
Selectable: false
|
||||||
ResourceType@Spice:
|
|
||||||
Type: Spice
|
|
||||||
Name: Spice
|
|
||||||
ResourceType: 1
|
|
||||||
Palette: d2k
|
|
||||||
TerrainType: Spice
|
|
||||||
Sequences: spicea, spiceb, spicec, spiced
|
|
||||||
MaxDensity: 20
|
|
||||||
ValuePerUnit: 25
|
|
||||||
AllowedTerrainTypes: SpiceSand
|
|
||||||
AllowUnderActors: true
|
|
||||||
D2kResourceRenderer:
|
D2kResourceRenderer:
|
||||||
RenderTypes: Spice
|
ResourceTypes:
|
||||||
|
Spice:
|
||||||
|
Sequences: spicea, spiceb, spicec, spiced
|
||||||
|
Palette: d2k
|
||||||
|
Name: Spice
|
||||||
|
|
||||||
World:
|
World:
|
||||||
Inherits: ^BaseWorld
|
Inherits: ^BaseWorld
|
||||||
@@ -146,6 +139,13 @@ World:
|
|||||||
WarheadDebugOverlay:
|
WarheadDebugOverlay:
|
||||||
BuildableTerrainLayer:
|
BuildableTerrainLayer:
|
||||||
ResourceLayer:
|
ResourceLayer:
|
||||||
|
RecalculateResourceDensity: true
|
||||||
|
ResourceTypes:
|
||||||
|
Spice:
|
||||||
|
ResourceIndex: 1
|
||||||
|
TerrainType: Spice
|
||||||
|
AllowedTerrainTypes: SpiceSand
|
||||||
|
MaxDensity: 20
|
||||||
ResourceClaimLayer:
|
ResourceClaimLayer:
|
||||||
CustomTerrainDebugOverlay:
|
CustomTerrainDebugOverlay:
|
||||||
SmudgeLayer@Rock:
|
SmudgeLayer@Rock:
|
||||||
@@ -241,6 +241,13 @@ EditorWorld:
|
|||||||
EditorActorLayer:
|
EditorActorLayer:
|
||||||
EditorCursorLayer:
|
EditorCursorLayer:
|
||||||
EditorResourceLayer:
|
EditorResourceLayer:
|
||||||
|
RecalculateResourceDensity: true
|
||||||
|
ResourceTypes:
|
||||||
|
Spice:
|
||||||
|
ResourceIndex: 1
|
||||||
|
TerrainType: Spice
|
||||||
|
AllowedTerrainTypes: SpiceSand
|
||||||
|
MaxDensity: 20
|
||||||
EditorSelectionLayer:
|
EditorSelectionLayer:
|
||||||
LoadWidgetAtGameStart:
|
LoadWidgetAtGameStart:
|
||||||
EditorActionManager:
|
EditorActionManager:
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ Player:
|
|||||||
LobbyPrerequisiteCheckbox@GLOBALBOUNTY:
|
LobbyPrerequisiteCheckbox@GLOBALBOUNTY:
|
||||||
Enabled: False
|
Enabled: False
|
||||||
Locked: True
|
Locked: True
|
||||||
|
PlayerResources:
|
||||||
|
ResourceValues:
|
||||||
|
Ore: 0
|
||||||
|
Gems: 0
|
||||||
|
|
||||||
World:
|
World:
|
||||||
-CrateSpawner:
|
-CrateSpawner:
|
||||||
@@ -12,8 +16,6 @@ World:
|
|||||||
BackgroundMusic: intro
|
BackgroundMusic: intro
|
||||||
AllowMuteBackgroundMusic: true
|
AllowMuteBackgroundMusic: true
|
||||||
DisableWorldSounds: true
|
DisableWorldSounds: true
|
||||||
ResourceType@ore:
|
|
||||||
ValuePerUnit: 0
|
|
||||||
LuaScript:
|
LuaScript:
|
||||||
Scripts: desert-shellmap.lua
|
Scripts: desert-shellmap.lua
|
||||||
-StartGameNotification:
|
-StartGameNotification:
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
^BasePlayer:
|
^BasePlayer:
|
||||||
AlwaysVisible:
|
AlwaysVisible:
|
||||||
Shroud:
|
Shroud:
|
||||||
|
PlayerResources:
|
||||||
|
ResourceValues:
|
||||||
|
Ore: 25
|
||||||
|
Gems: 50
|
||||||
|
|
||||||
EditorPlayer:
|
EditorPlayer:
|
||||||
Inherits: ^BasePlayer
|
Inherits: ^BasePlayer
|
||||||
|
|||||||
@@ -148,30 +148,16 @@
|
|||||||
RandomFactionMembers: russia, ukraine
|
RandomFactionMembers: russia, ukraine
|
||||||
Side: Random
|
Side: Random
|
||||||
Description: Random Soviet Country\nA random Soviet country will be chosen when the game starts.
|
Description: Random Soviet Country\nA random Soviet country will be chosen when the game starts.
|
||||||
ResourceType@ore:
|
|
||||||
Type: Ore
|
|
||||||
Name: Valuable Minerals
|
|
||||||
ResourceType: 1
|
|
||||||
TerrainType: Ore
|
|
||||||
Palette: player
|
|
||||||
Sequences: gold01,gold02,gold03,gold04
|
|
||||||
MaxDensity: 12
|
|
||||||
ValuePerUnit: 25
|
|
||||||
AllowedTerrainTypes: Clear,Road
|
|
||||||
AllowUnderActors: true
|
|
||||||
ResourceType@gem:
|
|
||||||
Type: Gems
|
|
||||||
Name: Valuable Minerals
|
|
||||||
ResourceType: 2
|
|
||||||
TerrainType: Gems
|
|
||||||
Palette: player
|
|
||||||
Sequences: gem01,gem02,gem03,gem04
|
|
||||||
MaxDensity: 3
|
|
||||||
ValuePerUnit: 50
|
|
||||||
AllowedTerrainTypes: Clear,Road
|
|
||||||
AllowUnderActors: true
|
|
||||||
ResourceRenderer:
|
ResourceRenderer:
|
||||||
RenderTypes: Ore, Gems
|
ResourceTypes:
|
||||||
|
Ore:
|
||||||
|
Sequences: gold01, gold02, gold03, gold04
|
||||||
|
Palette: player
|
||||||
|
Name: Valuable Minerals
|
||||||
|
Gems:
|
||||||
|
Sequences: gem01, gem02, gem03, gem04
|
||||||
|
Palette: player
|
||||||
|
Name: Valuable Minerals
|
||||||
|
|
||||||
World:
|
World:
|
||||||
Inherits: ^BaseWorld
|
Inherits: ^BaseWorld
|
||||||
@@ -210,6 +196,18 @@ World:
|
|||||||
SmokeImage: smoke_m
|
SmokeImage: smoke_m
|
||||||
SmokeSequences: idle
|
SmokeSequences: idle
|
||||||
ResourceLayer:
|
ResourceLayer:
|
||||||
|
RecalculateResourceDensity: true
|
||||||
|
ResourceTypes:
|
||||||
|
Ore:
|
||||||
|
ResourceIndex: 1
|
||||||
|
TerrainType: Ore
|
||||||
|
AllowedTerrainTypes: Clear, Road
|
||||||
|
MaxDensity: 12
|
||||||
|
Gems:
|
||||||
|
ResourceIndex: 2
|
||||||
|
TerrainType: Gems
|
||||||
|
AllowedTerrainTypes: Clear, Road
|
||||||
|
MaxDensity: 3
|
||||||
ResourceClaimLayer:
|
ResourceClaimLayer:
|
||||||
WarheadDebugOverlay:
|
WarheadDebugOverlay:
|
||||||
SpawnMapActors:
|
SpawnMapActors:
|
||||||
@@ -288,6 +286,18 @@ EditorWorld:
|
|||||||
EditorActorLayer:
|
EditorActorLayer:
|
||||||
EditorCursorLayer:
|
EditorCursorLayer:
|
||||||
EditorResourceLayer:
|
EditorResourceLayer:
|
||||||
|
RecalculateResourceDensity: true
|
||||||
|
ResourceTypes:
|
||||||
|
Ore:
|
||||||
|
ResourceIndex: 1
|
||||||
|
TerrainType: Ore
|
||||||
|
AllowedTerrainTypes: Clear, Road
|
||||||
|
MaxDensity: 12
|
||||||
|
Gems:
|
||||||
|
ResourceIndex: 2
|
||||||
|
TerrainType: Gems
|
||||||
|
AllowedTerrainTypes: Clear, Road
|
||||||
|
MaxDensity: 3
|
||||||
EditorSelectionLayer:
|
EditorSelectionLayer:
|
||||||
LoadWidgetAtGameStart:
|
LoadWidgetAtGameStart:
|
||||||
EditorActionManager:
|
EditorActionManager:
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
Player:
|
Player:
|
||||||
-ConquestVictoryConditions:
|
-ConquestVictoryConditions:
|
||||||
|
PlayerResources:
|
||||||
|
ResourceValues:
|
||||||
|
Tiberium: 0
|
||||||
|
BlueTiberium: 0
|
||||||
|
|
||||||
World:
|
World:
|
||||||
-CrateSpawner:
|
-CrateSpawner:
|
||||||
@@ -8,8 +12,6 @@ World:
|
|||||||
-MapStartingLocations:
|
-MapStartingLocations:
|
||||||
LuaScript:
|
LuaScript:
|
||||||
Scripts: fields-of-green.lua
|
Scripts: fields-of-green.lua
|
||||||
ResourceType@Tiberium:
|
|
||||||
ValuePerUnit: 0
|
|
||||||
MusicPlaylist:
|
MusicPlaylist:
|
||||||
BackgroundMusic: intro
|
BackgroundMusic: intro
|
||||||
DisableWorldSounds: true
|
DisableWorldSounds: true
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
^BasePlayer:
|
^BasePlayer:
|
||||||
AlwaysVisible:
|
AlwaysVisible:
|
||||||
Shroud:
|
Shroud:
|
||||||
|
PlayerResources:
|
||||||
|
ResourceValues:
|
||||||
|
Tiberium: 25
|
||||||
|
BlueTiberium: 40
|
||||||
|
|
||||||
EditorPlayer:
|
EditorPlayer:
|
||||||
Inherits: ^BasePlayer
|
Inherits: ^BasePlayer
|
||||||
|
|||||||
@@ -198,39 +198,6 @@
|
|||||||
Name: Nod
|
Name: Nod
|
||||||
InternalName: nod
|
InternalName: nod
|
||||||
Description: Brotherhood of Nod\nThe Brotherhood is a religious cult centered around their leader Kane\nand the alien substance Tiberium. They utilize stealth technology\nand guerilla tactics to defeat those who oppose them.
|
Description: Brotherhood of Nod\nThe Brotherhood is a religious cult centered around their leader Kane\nand the alien substance Tiberium. They utilize stealth technology\nand guerilla tactics to defeat those who oppose them.
|
||||||
ResourceType@Tiberium:
|
|
||||||
Type: Tiberium
|
|
||||||
Name: Tiberium
|
|
||||||
ResourceType: 1
|
|
||||||
Palette: greentiberium
|
|
||||||
Sequences: tib01, tib02, tib03, tib04, tib05, tib06, tib07, tib08, tib09, tib10, tib11, tib12
|
|
||||||
MaxDensity: 12
|
|
||||||
ValuePerUnit: 25
|
|
||||||
AllowedTerrainTypes: Clear, Rough, DirtRoad
|
|
||||||
AllowUnderActors: true
|
|
||||||
TerrainType: Tiberium
|
|
||||||
ResourceType@BlueTiberium:
|
|
||||||
Type: BlueTiberium
|
|
||||||
Name: Tiberium
|
|
||||||
ResourceType: 2
|
|
||||||
Palette: bluetiberium
|
|
||||||
Sequences: tib01, tib02, tib03, tib04, tib05, tib06, tib07, tib08, tib09, tib10, tib11, tib12
|
|
||||||
MaxDensity: 12
|
|
||||||
ValuePerUnit: 40
|
|
||||||
AllowedTerrainTypes: Clear, Rough, DirtRoad
|
|
||||||
AllowUnderActors: true
|
|
||||||
TerrainType: BlueTiberium
|
|
||||||
ResourceType@Veins:
|
|
||||||
Type: Veins
|
|
||||||
Name: Veins
|
|
||||||
ResourceType: 3
|
|
||||||
Palette: player
|
|
||||||
Sequences: veins
|
|
||||||
MaxDensity: 1
|
|
||||||
ValuePerUnit: 0
|
|
||||||
AllowedTerrainTypes: Clear, Rough, DirtRoad
|
|
||||||
AllowUnderActors: true
|
|
||||||
TerrainType: Veins
|
|
||||||
TerrainGeometryOverlay:
|
TerrainGeometryOverlay:
|
||||||
DebugVisualizations:
|
DebugVisualizations:
|
||||||
ExitsDebugOverlayManager:
|
ExitsDebugOverlayManager:
|
||||||
@@ -238,7 +205,19 @@
|
|||||||
SubterraneanActorLayer:
|
SubterraneanActorLayer:
|
||||||
JumpjetActorLayer:
|
JumpjetActorLayer:
|
||||||
ResourceRenderer:
|
ResourceRenderer:
|
||||||
RenderTypes: Tiberium, BlueTiberium, Veins
|
ResourceTypes:
|
||||||
|
Tiberium:
|
||||||
|
Sequences: tib01, tib02, tib03, tib04, tib05, tib06, tib07, tib08, tib09, tib10, tib11, tib12
|
||||||
|
Palette: greentiberium
|
||||||
|
Name: Tiberium
|
||||||
|
BlueTiberium:
|
||||||
|
Sequences: tib01, tib02, tib03, tib04, tib05, tib06, tib07, tib08, tib09, tib10, tib11, tib12
|
||||||
|
Palette: bluetiberium
|
||||||
|
Name: Tiberium
|
||||||
|
Veins:
|
||||||
|
Sequences: veins
|
||||||
|
Palette: player
|
||||||
|
Name: Veins
|
||||||
|
|
||||||
World:
|
World:
|
||||||
Inherits: ^BaseWorld
|
Inherits: ^BaseWorld
|
||||||
@@ -279,6 +258,22 @@ World:
|
|||||||
Type: LargeCrater
|
Type: LargeCrater
|
||||||
Sequence: largecraters
|
Sequence: largecraters
|
||||||
ResourceLayer:
|
ResourceLayer:
|
||||||
|
ResourceTypes:
|
||||||
|
Tiberium:
|
||||||
|
ResourceIndex: 1
|
||||||
|
TerrainType: Tiberium
|
||||||
|
AllowedTerrainTypes: Clear, Rough, DirtRoad
|
||||||
|
MaxDensity: 12
|
||||||
|
BlueTiberium:
|
||||||
|
ResourceIndex: 2
|
||||||
|
TerrainType: BlueTiberium
|
||||||
|
AllowedTerrainTypes: Clear, Rough, DirtRoad
|
||||||
|
MaxDensity: 12
|
||||||
|
Veins:
|
||||||
|
ResourceIndex: 3
|
||||||
|
TerrainType: Veins
|
||||||
|
AllowedTerrainTypes: Clear, Rough, DirtRoad
|
||||||
|
MaxDensity: 1
|
||||||
BridgeLayer:
|
BridgeLayer:
|
||||||
CustomTerrainDebugOverlay:
|
CustomTerrainDebugOverlay:
|
||||||
ResourceClaimLayer:
|
ResourceClaimLayer:
|
||||||
@@ -377,6 +372,22 @@ EditorWorld:
|
|||||||
EditorActorLayer:
|
EditorActorLayer:
|
||||||
EditorCursorLayer:
|
EditorCursorLayer:
|
||||||
EditorResourceLayer:
|
EditorResourceLayer:
|
||||||
|
ResourceTypes:
|
||||||
|
Tiberium:
|
||||||
|
ResourceIndex: 1
|
||||||
|
TerrainType: Tiberium
|
||||||
|
AllowedTerrainTypes: Clear, Rough, DirtRoad
|
||||||
|
MaxDensity: 12
|
||||||
|
BlueTiberium:
|
||||||
|
ResourceIndex: 2
|
||||||
|
TerrainType: BlueTiberium
|
||||||
|
AllowedTerrainTypes: Clear, Rough, DirtRoad
|
||||||
|
MaxDensity: 12
|
||||||
|
Veins:
|
||||||
|
ResourceIndex: 3
|
||||||
|
TerrainType: Veins
|
||||||
|
AllowedTerrainTypes: Clear, Rough, DirtRoad
|
||||||
|
MaxDensity: 1
|
||||||
EditorSelectionLayer:
|
EditorSelectionLayer:
|
||||||
Palette: ra
|
Palette: ra
|
||||||
FootprintAlpha: 0.7
|
FootprintAlpha: 0.7
|
||||||
|
|||||||
Reference in New Issue
Block a user