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 RefineryInfo info;
|
||||
readonly Dictionary<string, int> resourceValues;
|
||||
PlayerResources playerResources;
|
||||
IEnumerable<int> resourceValueModifiers;
|
||||
|
||||
@@ -83,8 +82,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
this.info = info;
|
||||
playerResources = self.Owner.PlayerActor.Trait<PlayerResources>();
|
||||
currentDisplayTick = info.TickRate;
|
||||
resourceValues = self.World.WorldActor.TraitsImplementing<ResourceType>()
|
||||
.ToDictionary(r => r.Info.Type, r => r.Info.ValuePerUnit);
|
||||
}
|
||||
|
||||
void INotifyCreated.Created(Actor self)
|
||||
@@ -105,7 +102,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
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;
|
||||
|
||||
var value = Util.ApplyPercentageModifiers(count * resourceValue, resourceValueModifiers);
|
||||
|
||||
@@ -52,6 +52,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[NotificationReference("Sounds")]
|
||||
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)
|
||||
{
|
||||
var startingCash = SelectableCash.ToDictionary(c => c.ToString(), c => "$" + c.ToString());
|
||||
|
||||
@@ -13,22 +13,47 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[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
|
||||
{
|
||||
readonly EditorResourceLayerInfo info;
|
||||
protected readonly Map Map;
|
||||
protected readonly Dictionary<string, ResourceTypeInfo> ResourceInfo;
|
||||
protected readonly Dictionary<int, string> Resources;
|
||||
protected readonly Dictionary<byte, string> ResourceTypesByIndex;
|
||||
protected readonly CellLayer<ResourceLayerContents> Tiles;
|
||||
protected Dictionary<string, int> resourceValues;
|
||||
|
||||
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; }
|
||||
int IResourceLayer.GetMaxDensity(string resourceType)
|
||||
{
|
||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
||||
return 0;
|
||||
|
||||
return resourceInfo.MaxDensity;
|
||||
return info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo) ? resourceInfo.MaxDensity : 0;
|
||||
}
|
||||
|
||||
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.IsEmpty => false;
|
||||
|
||||
public EditorResourceLayer(Actor self)
|
||||
public EditorResourceLayer(Actor self, EditorResourceLayerInfo info)
|
||||
{
|
||||
if (self.World.Type != WorldType.Editor)
|
||||
return;
|
||||
|
||||
this.info = info;
|
||||
Map = self.World.Map;
|
||||
Tiles = new CellLayer<ResourceLayerContents>(Map);
|
||||
ResourceInfo = self.TraitsImplementing<ResourceType>()
|
||||
.ToDictionary(r => r.Info.Type, r => r.Info);
|
||||
Resources = ResourceInfo.Values
|
||||
.ToDictionary(r => r.ResourceType, r => r.Type);
|
||||
ResourceTypesByIndex = info.ResourceTypes.ToDictionary(
|
||||
kv => kv.Value.ResourceIndex,
|
||||
kv => kv.Key);
|
||||
|
||||
Map.Resources.CellEntryChanged += UpdateCell;
|
||||
}
|
||||
@@ -72,6 +94,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (w.Type != WorldType.Editor)
|
||||
return;
|
||||
|
||||
var playerResourcesInfo = w.Map.Rules.Actors["player"].TraitInfoOrDefault<PlayerResourcesInfo>();
|
||||
resourceValues = playerResourcesInfo?.ResourceValues ?? new Dictionary<string, int>();
|
||||
|
||||
foreach (var cell in Map.AllCells)
|
||||
UpdateCell(cell);
|
||||
}
|
||||
@@ -87,9 +112,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
var newTile = ResourceLayerContents.Empty;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -102,7 +127,10 @@ namespace OpenRA.Mods.Common.Traits
|
||||
Map.CustomTerrain[uv] = newTerrain;
|
||||
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)
|
||||
{
|
||||
var neighbouringCell = cell + d;
|
||||
@@ -110,7 +138,10 @@ namespace OpenRA.Mods.Common.Traits
|
||||
continue;
|
||||
|
||||
var neighbouringTile = Tiles[neighbouringCell];
|
||||
var density = CalculateCellDensity(neighbouringTile.Type, neighbouringCell);
|
||||
if (neighbouringTile.Type == null)
|
||||
continue;
|
||||
|
||||
var density = CalculateCellDensity(neighbouringTile, neighbouringCell);
|
||||
if (neighbouringTile.Density == density)
|
||||
continue;
|
||||
|
||||
@@ -124,19 +155,22 @@ namespace OpenRA.Mods.Common.Traits
|
||||
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
|
||||
if (oldResourceType != null && oldDensity > 0 && ResourceInfo.TryGetValue(oldResourceType, out var oldResourceInfo))
|
||||
NetWorth -= (oldDensity + 1) * oldResourceInfo.ValuePerUnit;
|
||||
if (oldResourceType != null && oldDensity > 0 && resourceValues.TryGetValue(oldResourceType, out var oldResourceValue))
|
||||
NetWorth -= (oldDensity + 1) * oldResourceValue;
|
||||
|
||||
if (newResourceType != null && newDensity > 0 && ResourceInfo.TryGetValue(newResourceType, out var newResourceInfo))
|
||||
NetWorth += (newDensity + 1) * newResourceInfo.ValuePerUnit;
|
||||
if (newResourceType != null && newDensity > 0 && resourceValues.TryGetValue(newResourceType, out var newResourceValue))
|
||||
NetWorth += (newDensity + 1) * newResourceValue;
|
||||
}
|
||||
|
||||
public int CalculateCellDensity(string resourceType, CPos c)
|
||||
protected virtual int CalculateCellDensity(ResourceLayerContents contents, CPos c)
|
||||
{
|
||||
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;
|
||||
|
||||
if (!info.RecalculateResourceDensity)
|
||||
return contents.Density.Clamp(1, resourceInfo.MaxDensity);
|
||||
|
||||
// Set density based on the number of neighboring resources
|
||||
var adjacent = 0;
|
||||
for (var u = -1; u < 2; u++)
|
||||
@@ -144,7 +178,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
for (var v = -1; v < 2; 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++;
|
||||
}
|
||||
}
|
||||
@@ -152,23 +186,20 @@ namespace OpenRA.Mods.Common.Traits
|
||||
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 (!mapResources.Contains(cell))
|
||||
if (!Map.Ramp.Contains(cell) || Map.Ramp[cell] != 0)
|
||||
return false;
|
||||
|
||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
||||
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;
|
||||
|
||||
// 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)
|
||||
@@ -177,57 +208,57 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (!resources.Contains(cell))
|
||||
return false;
|
||||
|
||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
||||
if (!info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||
return false;
|
||||
|
||||
// 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];
|
||||
if (content.Type != resourceInfo.ResourceType)
|
||||
if (content.Type != resourceInfo.ResourceIndex)
|
||||
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;
|
||||
}
|
||||
|
||||
int AddResource(string resourceType, CPos cell, int amount = 1)
|
||||
protected virtual int AddResource(string resourceType, CPos cell, int amount = 1)
|
||||
{
|
||||
var resources = Map.Resources;
|
||||
if (!resources.Contains(cell))
|
||||
return 0;
|
||||
|
||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
||||
if (!info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||
return 0;
|
||||
|
||||
// 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 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);
|
||||
Map.Resources[cell] = new ResourceTile((byte)resourceInfo.ResourceType, density);
|
||||
Map.Resources[cell] = new ResourceTile((byte)resourceInfo.ResourceIndex, density);
|
||||
|
||||
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;
|
||||
if (!resources.Contains(cell))
|
||||
return 0;
|
||||
|
||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
||||
if (!info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||
return 0;
|
||||
|
||||
var content = resources[cell];
|
||||
if (content.Type == 0 || content.Type != resourceInfo.ResourceType)
|
||||
if (content.Type == 0 || content.Type != resourceInfo.ResourceIndex)
|
||||
return 0;
|
||||
|
||||
var oldDensity = content.Index;
|
||||
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;
|
||||
}
|
||||
|
||||
void ClearResources(CPos cell)
|
||||
protected virtual void ClearResources(CPos cell)
|
||||
{
|
||||
Map.Resources[cell] = default;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
@@ -31,99 +32,158 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
|
||||
[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
|
||||
{
|
||||
readonly ResourceLayerInfo info;
|
||||
readonly World world;
|
||||
readonly BuildingInfluence buildingInfluence;
|
||||
protected readonly Dictionary<string, ResourceTypeInfo> ResourceInfo;
|
||||
protected readonly Map Map;
|
||||
protected readonly BuildingInfluence BuildingInfluence;
|
||||
protected readonly CellLayer<ResourceLayerContents> Content;
|
||||
protected readonly Dictionary<byte, string> ResourceTypesByIndex;
|
||||
|
||||
int resCells;
|
||||
|
||||
public event Action<CPos, string> CellChanged;
|
||||
|
||||
public ResourceLayer(Actor self)
|
||||
public ResourceLayer(Actor self, ResourceLayerInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
world = self.World;
|
||||
buildingInfluence = self.Trait<BuildingInfluence>();
|
||||
ResourceInfo = self.TraitsImplementing<ResourceType>()
|
||||
.ToDictionary(r => r.Info.Type, r => r.Info);
|
||||
|
||||
Content = new CellLayer<ResourceLayerContents>(world.Map);
|
||||
Map = world.Map;
|
||||
BuildingInfluence = self.Trait<BuildingInfluence>();
|
||||
Content = new CellLayer<ResourceLayerContents>(Map);
|
||||
ResourceTypesByIndex = info.ResourceTypes.ToDictionary(
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
if (!AllowResourceAt(resourceType, cell))
|
||||
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)
|
||||
{
|
||||
var resourceType = Content[cell].Type;
|
||||
if (resourceType != null && ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
||||
var resource = Content[cell];
|
||||
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
|
||||
// Adjacent includes the current cell, so is always >= 1
|
||||
var adjacent = GetAdjacentCellsWith(resourceType, cell);
|
||||
var density = int2.Lerp(0, resourceInfo.MaxDensity, adjacent, 9);
|
||||
Content[cell] = new ResourceLayerContents(Content[cell].Type, Math.Max(density, 1));
|
||||
var c = cell + directions[i];
|
||||
if (Content.Contains(c) && Content[c].Type == resource.Type)
|
||||
++adjacent;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
if (!world.Map.Contains(cell))
|
||||
if (resourceType == null || !info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||
return false;
|
||||
|
||||
if (!resourceInfo.AllowedTerrainTypes.Contains(world.Map.GetTerrainInfo(cell).Type))
|
||||
if (!resourceInfo.AllowedTerrainTypes.Contains(Map.GetTerrainInfo(cell).Type))
|
||||
return false;
|
||||
|
||||
if (!resourceInfo.AllowUnderActors && world.ActorMap.AnyActorsAt(cell))
|
||||
return false;
|
||||
|
||||
if (!resourceInfo.AllowUnderBuildings && buildingInfluence.GetBuildingAt(cell) != null)
|
||||
return false;
|
||||
|
||||
return resourceInfo.AllowOnRamps || world.Map.Ramp[cell] == 0;
|
||||
return BuildingInfluence.GetBuildingAt(cell) == null;
|
||||
}
|
||||
|
||||
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;
|
||||
return ResourceLayerContents.Empty;
|
||||
@@ -132,7 +192,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
world.Map.CustomTerrain[cell] = world.Map.Rules.TerrainInfo.GetTerrainIndex(resourceInfo.TerrainType);
|
||||
++resCells;
|
||||
|
||||
return new ResourceLayerContents(resourceType, 0);
|
||||
return new ResourceLayerContents(resourceType, density.Clamp(1, resourceInfo.MaxDensity));
|
||||
}
|
||||
|
||||
bool CanAddResource(string resourceType, CPos cell, int amount = 1)
|
||||
@@ -140,7 +200,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (!world.Map.Contains(cell))
|
||||
return false;
|
||||
|
||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
||||
if (resourceType == null || !info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||
return false;
|
||||
|
||||
var content = Content[cell];
|
||||
@@ -158,12 +218,12 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (!Content.Contains(cell))
|
||||
return 0;
|
||||
|
||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
||||
if (resourceType == null || !info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||
return 0;
|
||||
|
||||
var content = Content[cell];
|
||||
if (content.Type == null)
|
||||
content = CreateResourceCell(resourceType, cell);
|
||||
content = CreateResourceCell(resourceType, cell, 0);
|
||||
|
||||
if (content.Type != resourceType)
|
||||
return 0;
|
||||
@@ -192,7 +252,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (density == 0)
|
||||
{
|
||||
Content[cell] = ResourceLayerContents.Empty;
|
||||
world.Map.CustomTerrain[cell] = byte.MaxValue;
|
||||
Map.CustomTerrain[cell] = byte.MaxValue;
|
||||
--resCells;
|
||||
|
||||
CellChanged?.Invoke(cell, null);
|
||||
@@ -217,7 +277,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return;
|
||||
|
||||
Content[cell] = ResourceLayerContents.Empty;
|
||||
world.Map.CustomTerrain[cell] = byte.MaxValue;
|
||||
Map.CustomTerrain[cell] = byte.MaxValue;
|
||||
--resCells;
|
||||
|
||||
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; }
|
||||
int IResourceLayer.GetMaxDensity(string resourceType)
|
||||
{
|
||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
||||
if (!info.ResourceTypes.TryGetValue(resourceType, out var resourceInfo))
|
||||
return 0;
|
||||
|
||||
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.")]
|
||||
public class ResourceRendererInfo : TraitInfo, Requires<IResourceLayerInfo>
|
||||
{
|
||||
[FieldLoader.Require]
|
||||
[Desc("Only render these ResourceType names.")]
|
||||
public readonly string[] RenderTypes = null;
|
||||
public class ResourceTypeInfo
|
||||
{
|
||||
[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 class ResourceRenderer : IResourceRenderer, IWorldLoaded, IRenderOverlay, ITickRender, INotifyActorDisposing
|
||||
{
|
||||
protected readonly IResourceLayer ResourceLayer;
|
||||
protected readonly CellLayer<RendererCellContents> RenderContent;
|
||||
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 Queue<CPos> cleanDirty = new Queue<CPos>();
|
||||
TerrainSpriteLayer shadowLayer;
|
||||
TerrainSpriteLayer spriteLayer;
|
||||
bool disposed;
|
||||
|
||||
public ResourceRenderer(Actor self, ResourceRendererInfo info)
|
||||
{
|
||||
Info = info;
|
||||
World = self.World;
|
||||
ResourceLayer = self.Trait<IResourceLayer>();
|
||||
ResourceLayer.CellChanged += AddDirtyCell;
|
||||
ResourceInfo = self.TraitsImplementing<ResourceType>()
|
||||
.ToDictionary(r => r.Info.Type, r => r);
|
||||
|
||||
RenderContent = new CellLayer<RendererCellContents>(self.World.Map);
|
||||
RenderContents = new CellLayer<RendererCellContents>(self.World.Map);
|
||||
}
|
||||
|
||||
void AddDirtyCell(CPos cell, string resourceType)
|
||||
{
|
||||
if (resourceType == null || Info.RenderTypes.Contains(resourceType))
|
||||
if (resourceType == null || Info.ResourceTypes.ContainsKey(resourceType))
|
||||
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)
|
||||
{
|
||||
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);
|
||||
spriteLayer = new TerrainSpriteLayer(w, wr, emptySprite, first.BlendMode, wr.World.Type != WorldType.Editor);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var first = firstWithShadow.GetShadow(0, WAngle.Zero);
|
||||
@@ -80,30 +121,36 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
|
||||
// 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))
|
||||
throw new InvalidDataException("Resource sprites specify different blend modes. "
|
||||
+ "Try using different ResourceRenderer traits for resource types that use different blend modes.");
|
||||
}
|
||||
|
||||
// Initialize the RenderContent with the initial map state
|
||||
// because the shroud may not be enabled.
|
||||
// Initialize the RenderContent 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 type = ResourceLayer.GetResource(cell).Type;
|
||||
if (type != null && Info.RenderTypes.Contains(type))
|
||||
var resource = ResourceLayer.GetResource(cell);
|
||||
var rendererCellContents = CreateRenderCellContents(wr, resource, cell);
|
||||
if (rendererCellContents.Type != null)
|
||||
{
|
||||
var resourceContent = ResourceLayer.GetResource(cell);
|
||||
if (!ResourceInfo.TryGetValue(resourceContent.Type, out var resourceType))
|
||||
continue;
|
||||
|
||||
var rendererCellContents = new RendererCellContents(ChooseRandomVariant(resourceType), resourceType, resourceContent.Density);
|
||||
RenderContent[cell] = rendererCellContents;
|
||||
RenderContents[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)
|
||||
{
|
||||
// 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))
|
||||
continue;
|
||||
|
||||
var resourceContent = ResourceLayer.GetResource(cell);
|
||||
if (resourceContent.Density > 0)
|
||||
var rendererCellContents = RendererCellContents.Empty;
|
||||
var contents = ResourceLayer.GetResource(cell);
|
||||
if (contents.Density > 0)
|
||||
{
|
||||
var cellContents = RenderContent[cell];
|
||||
var resourceData = ResourceInfo[resourceContent.Type];
|
||||
var variant = cellContents.Variant;
|
||||
if (cellContents.Variant == null || cellContents.Type.Info.Type != resourceContent.Type)
|
||||
variant = ChooseRandomVariant(resourceData);
|
||||
rendererCellContents = RenderContents[cell];
|
||||
|
||||
var rendererCellContents = new RendererCellContents(variant, resourceData, resourceContent.Density);
|
||||
RenderContent[cell] = rendererCellContents;
|
||||
|
||||
UpdateRenderedSprite(cell, rendererCellContents);
|
||||
}
|
||||
else
|
||||
{
|
||||
var rendererCellContents = RendererCellContents.Empty;
|
||||
RenderContent[cell] = rendererCellContents;
|
||||
UpdateRenderedSprite(cell, rendererCellContents);
|
||||
// Contents are the same, so just update the density
|
||||
if (rendererCellContents.Type == contents.Type)
|
||||
rendererCellContents = new RendererCellContents(rendererCellContents, contents.Density);
|
||||
else
|
||||
rendererCellContents = CreateRenderCellContents(wr, contents, cell);
|
||||
}
|
||||
|
||||
RenderContents[cell] = rendererCellContents;
|
||||
UpdateRenderedSprite(cell, rendererCellContents);
|
||||
cleanDirty.Enqueue(cell);
|
||||
}
|
||||
|
||||
@@ -162,28 +203,17 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
protected virtual void UpdateRenderedSprite(CPos cell, RendererCellContents content)
|
||||
{
|
||||
var density = content.Density;
|
||||
var type = content.Type;
|
||||
if (content.Density > 0)
|
||||
{
|
||||
// The call chain for this method (that starts with AddDirtyCell()) guarantees
|
||||
// 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(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);
|
||||
var maxDensity = ResourceLayer.GetMaxDensity(content.Type);
|
||||
var frame = int2.Lerp(0, content.Sequence.Length - 1, content.Density, maxDensity);
|
||||
UpdateSpriteLayers(cell, content.Sequence, frame, content.Palette);
|
||||
}
|
||||
else
|
||||
UpdateSpriteLayers(cell, null, 0, null);
|
||||
}
|
||||
|
||||
bool disposed;
|
||||
void INotifyActorDisposing.Disposing(Actor self)
|
||||
protected virtual void Disposing(Actor self)
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
@@ -196,16 +226,18 @@ namespace OpenRA.Mods.Common.Traits
|
||||
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); }
|
||||
|
||||
@@ -213,13 +245,16 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
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;
|
||||
|
||||
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 shadow = sequence.GetShadow(sequence.Length - 1, WAngle.Zero);
|
||||
var palette = resourceInfo.Palette;
|
||||
var palette = wr.Palette(resourceInfo.Palette);
|
||||
|
||||
if (shadow != null)
|
||||
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)
|
||||
{
|
||||
if (!ResourceInfo.TryGetValue(resourceType, out var resourceInfo))
|
||||
if (!Variants.TryGetValue(resourceType, out var variant))
|
||||
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 shadow = sequence.GetShadow(sequence.Length - 1, WAngle.Zero);
|
||||
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;
|
||||
|
||||
if (shadow != null)
|
||||
@@ -247,17 +285,30 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public readonly struct RendererCellContents
|
||||
{
|
||||
public readonly string Variant;
|
||||
public readonly ResourceType Type;
|
||||
public readonly string Type;
|
||||
public readonly ResourceRendererInfo.ResourceTypeInfo Info;
|
||||
public readonly ISpriteSequence Sequence;
|
||||
public readonly PaletteReference Palette;
|
||||
public readonly int Density;
|
||||
|
||||
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 = type;
|
||||
Type = resourceType;
|
||||
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 ReplaceShadowPalette(),
|
||||
new ReplaceResourceValueModifiers(),
|
||||
new RemoveResourceType(),
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
|
||||
namespace OpenRA.Mods.D2k.Traits
|
||||
@@ -94,36 +95,36 @@ namespace OpenRA.Mods.D2k.Traits
|
||||
public D2kResourceRenderer(Actor self, D2kResourceRendererInfo 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;
|
||||
if (!CellContains(p + new CVec(0, -1), t))
|
||||
if (!CellContains(cell + new CVec(0, -1), resourceType))
|
||||
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;
|
||||
|
||||
if (!CellContains(p + new CVec(1, 0), t))
|
||||
if (!CellContains(cell + new CVec(1, 0), resourceType))
|
||||
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;
|
||||
|
||||
if (!CellContains(p + new CVec(-1, -1), t))
|
||||
if (!CellContains(cell + new CVec(-1, -1), resourceType))
|
||||
ret |= ClearSides.TopLeft;
|
||||
|
||||
if (!CellContains(p + new CVec(1, -1), t))
|
||||
if (!CellContains(cell + new CVec(1, -1), resourceType))
|
||||
ret |= ClearSides.TopRight;
|
||||
|
||||
if (!CellContains(p + new CVec(-1, 1), t))
|
||||
if (!CellContains(cell + new CVec(-1, 1), resourceType))
|
||||
ret |= ClearSides.BottomLeft;
|
||||
|
||||
if (!CellContains(p + new CVec(1, 1), t))
|
||||
if (!CellContains(cell + new CVec(1, 1), resourceType))
|
||||
ret |= ClearSides.BottomRight;
|
||||
|
||||
return ret;
|
||||
@@ -135,39 +136,27 @@ namespace OpenRA.Mods.D2k.Traits
|
||||
|
||||
var directions = CVec.Directions;
|
||||
for (var i = 0; i < directions.Length; i++)
|
||||
UpdateRenderedSpriteInner(cell + directions[i]);
|
||||
}
|
||||
|
||||
void UpdateRenderedSpriteInner(CPos cell)
|
||||
{
|
||||
UpdateRenderedSpriteInner(cell, RenderContent[cell]);
|
||||
{
|
||||
var neighbour = cell + directions[i];
|
||||
UpdateRenderedSpriteInner(neighbour, RenderContents[neighbour]);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateRenderedSpriteInner(CPos cell, RendererCellContents content)
|
||||
{
|
||||
var density = content.Density;
|
||||
var renderType = content.Type;
|
||||
|
||||
if (density > 0 && renderType != null)
|
||||
if (content.Density > 0)
|
||||
{
|
||||
// The call chain for this method (that starts with AddDirtyCell()) guarantees
|
||||
// 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);
|
||||
var clear = FindClearSides(cell, content.Type);
|
||||
|
||||
if (clear == ClearSides.None)
|
||||
{
|
||||
var sprites = renderType.Variants[content.Variant];
|
||||
var frame = density > renderType.Info.MaxDensity / 2 ? 1 : 0;
|
||||
|
||||
UpdateSpriteLayers(cell, sprites, frame, renderType.Palette);
|
||||
var maxDensity = ResourceLayer.GetMaxDensity(content.Type);
|
||||
var index = content.Density > maxDensity / 2 ? 1 : 0;
|
||||
UpdateSpriteLayers(cell, content.Sequence, index, content.Palette);
|
||||
}
|
||||
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
|
||||
throw new InvalidOperationException("SpriteMap does not contain an index for ClearSides type '{0}'".F(clear));
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
^BasePlayer:
|
||||
AlwaysVisible:
|
||||
Shroud:
|
||||
PlayerResources:
|
||||
ResourceValues:
|
||||
Tiberium: 35
|
||||
BlueTiberium: 60
|
||||
|
||||
EditorPlayer:
|
||||
Inherits: ^BasePlayer
|
||||
|
||||
@@ -126,30 +126,16 @@
|
||||
Name: 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.
|
||||
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:
|
||||
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:
|
||||
Inherits: ^BaseWorld
|
||||
@@ -178,6 +164,18 @@ World:
|
||||
SmokeImage: smoke_m
|
||||
SmokeSequences: idle
|
||||
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:
|
||||
WarheadDebugOverlay:
|
||||
CustomTerrainDebugOverlay:
|
||||
@@ -262,6 +260,18 @@ EditorWorld:
|
||||
EditorActorLayer:
|
||||
EditorCursorLayer:
|
||||
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:
|
||||
LoadWidgetAtGameStart:
|
||||
EditorActionManager:
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
Player:
|
||||
-ConquestVictoryConditions:
|
||||
-HarvesterInsurance:
|
||||
PlayerResources:
|
||||
ResourceValues:
|
||||
Spice: 0
|
||||
|
||||
World:
|
||||
-CrateSpawner:
|
||||
-SpawnStartingUnits:
|
||||
-MapStartingLocations:
|
||||
ResourceType@Spice:
|
||||
ValuePerUnit: 0
|
||||
ActorSpawnManager:
|
||||
Minimum: 1
|
||||
Maximum: 3
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
^BasePlayer:
|
||||
AlwaysVisible:
|
||||
Shroud:
|
||||
PlayerResources:
|
||||
ResourceValues:
|
||||
Spice: 25
|
||||
|
||||
EditorPlayer:
|
||||
Inherits: ^BasePlayer
|
||||
|
||||
@@ -107,19 +107,12 @@
|
||||
Name: Fremen
|
||||
InternalName: fremen
|
||||
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:
|
||||
RenderTypes: Spice
|
||||
ResourceTypes:
|
||||
Spice:
|
||||
Sequences: spicea, spiceb, spicec, spiced
|
||||
Palette: d2k
|
||||
Name: Spice
|
||||
|
||||
World:
|
||||
Inherits: ^BaseWorld
|
||||
@@ -146,6 +139,13 @@ World:
|
||||
WarheadDebugOverlay:
|
||||
BuildableTerrainLayer:
|
||||
ResourceLayer:
|
||||
RecalculateResourceDensity: true
|
||||
ResourceTypes:
|
||||
Spice:
|
||||
ResourceIndex: 1
|
||||
TerrainType: Spice
|
||||
AllowedTerrainTypes: SpiceSand
|
||||
MaxDensity: 20
|
||||
ResourceClaimLayer:
|
||||
CustomTerrainDebugOverlay:
|
||||
SmudgeLayer@Rock:
|
||||
@@ -241,6 +241,13 @@ EditorWorld:
|
||||
EditorActorLayer:
|
||||
EditorCursorLayer:
|
||||
EditorResourceLayer:
|
||||
RecalculateResourceDensity: true
|
||||
ResourceTypes:
|
||||
Spice:
|
||||
ResourceIndex: 1
|
||||
TerrainType: Spice
|
||||
AllowedTerrainTypes: SpiceSand
|
||||
MaxDensity: 20
|
||||
EditorSelectionLayer:
|
||||
LoadWidgetAtGameStart:
|
||||
EditorActionManager:
|
||||
|
||||
@@ -3,6 +3,10 @@ Player:
|
||||
LobbyPrerequisiteCheckbox@GLOBALBOUNTY:
|
||||
Enabled: False
|
||||
Locked: True
|
||||
PlayerResources:
|
||||
ResourceValues:
|
||||
Ore: 0
|
||||
Gems: 0
|
||||
|
||||
World:
|
||||
-CrateSpawner:
|
||||
@@ -12,8 +16,6 @@ World:
|
||||
BackgroundMusic: intro
|
||||
AllowMuteBackgroundMusic: true
|
||||
DisableWorldSounds: true
|
||||
ResourceType@ore:
|
||||
ValuePerUnit: 0
|
||||
LuaScript:
|
||||
Scripts: desert-shellmap.lua
|
||||
-StartGameNotification:
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
^BasePlayer:
|
||||
AlwaysVisible:
|
||||
Shroud:
|
||||
PlayerResources:
|
||||
ResourceValues:
|
||||
Ore: 25
|
||||
Gems: 50
|
||||
|
||||
EditorPlayer:
|
||||
Inherits: ^BasePlayer
|
||||
|
||||
@@ -148,30 +148,16 @@
|
||||
RandomFactionMembers: russia, ukraine
|
||||
Side: Random
|
||||
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:
|
||||
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:
|
||||
Inherits: ^BaseWorld
|
||||
@@ -210,6 +196,18 @@ World:
|
||||
SmokeImage: smoke_m
|
||||
SmokeSequences: idle
|
||||
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:
|
||||
WarheadDebugOverlay:
|
||||
SpawnMapActors:
|
||||
@@ -288,6 +286,18 @@ EditorWorld:
|
||||
EditorActorLayer:
|
||||
EditorCursorLayer:
|
||||
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:
|
||||
LoadWidgetAtGameStart:
|
||||
EditorActionManager:
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
Player:
|
||||
-ConquestVictoryConditions:
|
||||
PlayerResources:
|
||||
ResourceValues:
|
||||
Tiberium: 0
|
||||
BlueTiberium: 0
|
||||
|
||||
World:
|
||||
-CrateSpawner:
|
||||
@@ -8,8 +12,6 @@ World:
|
||||
-MapStartingLocations:
|
||||
LuaScript:
|
||||
Scripts: fields-of-green.lua
|
||||
ResourceType@Tiberium:
|
||||
ValuePerUnit: 0
|
||||
MusicPlaylist:
|
||||
BackgroundMusic: intro
|
||||
DisableWorldSounds: true
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
^BasePlayer:
|
||||
AlwaysVisible:
|
||||
Shroud:
|
||||
PlayerResources:
|
||||
ResourceValues:
|
||||
Tiberium: 25
|
||||
BlueTiberium: 40
|
||||
|
||||
EditorPlayer:
|
||||
Inherits: ^BasePlayer
|
||||
|
||||
@@ -198,39 +198,6 @@
|
||||
Name: 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.
|
||||
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:
|
||||
DebugVisualizations:
|
||||
ExitsDebugOverlayManager:
|
||||
@@ -238,7 +205,19 @@
|
||||
SubterraneanActorLayer:
|
||||
JumpjetActorLayer:
|
||||
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:
|
||||
Inherits: ^BaseWorld
|
||||
@@ -279,6 +258,22 @@ World:
|
||||
Type: LargeCrater
|
||||
Sequence: largecraters
|
||||
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:
|
||||
CustomTerrainDebugOverlay:
|
||||
ResourceClaimLayer:
|
||||
@@ -377,6 +372,22 @@ EditorWorld:
|
||||
EditorActorLayer:
|
||||
EditorCursorLayer:
|
||||
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:
|
||||
Palette: ra
|
||||
FootprintAlpha: 0.7
|
||||
|
||||
Reference in New Issue
Block a user