Reorganise ResourceLayer implementation.

This commit is contained in:
Paul Chote
2013-12-06 17:56:46 +13:00
parent 0b5b3b22b4
commit 410cd1c7b1
16 changed files with 238 additions and 116 deletions

View File

@@ -77,7 +77,7 @@ namespace OpenRA.Editor
public static ResourceTemplate RenderResourceType(ResourceTypeInfo info, string[] exts, Palette p)
{
var image = info.SpriteNames[0];
var image = info.EditorSprite;
using (var s = FileSystem.OpenWithExts(image, exts))
{
// TODO: Do this properly

View File

@@ -27,7 +27,7 @@ namespace OpenRA.Editor
= new TileReference<byte, byte>
{
Type = (byte)resourceTemplate.Info.ResourceType,
Index = (byte)random.Next(resourceTemplate.Info.SpriteNames.Length)
Index = (byte)random.Next(resourceTemplate.Info.MaxDensity)
};
var ch = new int2(surface.GetBrushLocation().X / Surface.ChunkSize,

View File

@@ -11,31 +11,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
namespace OpenRA.Traits
{
public class ResourceLayerInfo : TraitInfo<ResourceLayer> { }
public class ResourceLayerInfo : TraitInfo<ResourceLayer>, Requires<ResourceTypeInfo> { }
public class ResourceLayer : IRenderOverlay, IWorldLoaded, ITickRender
{
World world;
static readonly CellContents EmptyCell = new CellContents();
ResourceType[] resourceTypes;
CellContents[,] content;
CellContents[,] render;
World world;
protected CellContents[,] content;
protected CellContents[,] render;
List<CPos> dirty;
bool hasSetupPalettes;
public void Render(WorldRenderer wr)
{
if (!hasSetupPalettes)
{
hasSetupPalettes = true;
foreach (var rt in world.WorldActor.TraitsImplementing<ResourceType>())
rt.info.PaletteRef = wr.Palette(rt.info.Palette);
}
var clip = wr.Viewport.CellBounds;
for (var x = clip.Left; x < clip.Right; x++)
{
@@ -46,13 +39,23 @@ namespace OpenRA.Traits
continue;
var c = render[x, y];
if (c.Image != null)
new SpriteRenderable(c.Image[c.Density], pos.CenterPosition,
WVec.Zero, -511, c.Type.info.PaletteRef, 1f, true).Render(wr);
if (c.Sprite != null)
new SpriteRenderable(c.Sprite, pos.CenterPosition,
WVec.Zero, -511, c.Type.Palette, 1f, true).Render(wr);
}
}
}
int GetAdjacentCellsWith(ResourceType t, int i, int j)
{
var sum = 0;
for (var u = -1; u < 2; u++)
for (var v = -1; v < 2; v++)
if (content[i + u, j + v].Type == t)
++sum;
return sum;
}
public void WorldLoaded(World w, WorldRenderer wr)
{
this.world = w;
@@ -60,41 +63,66 @@ namespace OpenRA.Traits
render = new CellContents[w.Map.MapSize.X, w.Map.MapSize.Y];
dirty = new List<CPos>();
resourceTypes = w.WorldActor.TraitsImplementing<ResourceType>().ToArray();
foreach (var rt in resourceTypes)
rt.info.Sprites = rt.info.SpriteNames.Select(a => Game.modData.SpriteLoader.LoadAllSprites(a)).ToArray();
var resources = w.WorldActor.TraitsImplementing<ResourceType>()
.ToDictionary(r => r.Info.ResourceType, r => r);
var map = w.Map;
for (var x = map.Bounds.Left; x < map.Bounds.Right; x++)
for (var y = map.Bounds.Top; y < map.Bounds.Bottom; y++)
{
var type = resourceTypes.FirstOrDefault(
r => r.info.ResourceType == w.Map.MapResources.Value[x, y].Type);
if (type == null)
continue;
if (!AllowResourceAt(type, new CPos(x, y)))
continue;
render[x, y].Type = content[x, y].Type = type;
render[x, y].Image = content[x, y].Image = ChooseContent(type);
}
for (var x = map.Bounds.Left; x < map.Bounds.Right; x++)
{
for (var y = map.Bounds.Top; y < map.Bounds.Bottom; y++)
{
if (content[x, y].Type != null)
var cell = new CPos(x, y);
ResourceType t;
if (!resources.TryGetValue(w.Map.MapResources.Value[x, y].Type, out t))
continue;
if (!AllowResourceAt(t, cell))
continue;
content[x, y] = CreateResourceCell(t, cell);
}
}
// Set initial density based on the number of neighboring resources
for (var x = map.Bounds.Left; x < map.Bounds.Right; x++)
{
for (var y = map.Bounds.Top; y < map.Bounds.Bottom; y++)
{
var type = content[x, y].Type;
if (type != null)
{
render[x, y].Density = content[x, y].Density = GetIdealDensity(x, y);
w.Map.CustomTerrain[x, y] = content[x, y].Type.info.TerrainType;
// Adjacent includes the current cell, so is always >= 1
var adjacent = GetAdjacentCellsWith(type, x, y);
var density = int2.Lerp(0, type.Info.MaxDensity, adjacent, 9);
content[x, y].Density = density;
render[x, y] = content[x, y];
UpdateRenderedSprite(new CPos(x, y));
}
}
}
}
protected virtual void UpdateRenderedSprite(CPos p)
{
var t = render[p.X, p.Y];
if (t.Density > 0)
{
var sprites = t.Type.Variants[t.Variant];
var frame = int2.Lerp(0, sprites.Length - 1, t.Density - 1, t.Type.Info.MaxDensity);
t.Sprite = sprites[frame];
}
else
t.Sprite = null;
render[p.X, p.Y] = t;
}
protected virtual string ChooseRandomVariant(ResourceType t)
{
return t.Variants.Keys.Random(Game.CosmeticRandom);
}
public void TickRender(WorldRenderer wr, Actor self)
{
var remove = new List<CPos>();
@@ -103,6 +131,7 @@ namespace OpenRA.Traits
if (!self.World.FogObscures(c))
{
render[c.X, c.Y] = content[c.X, c.Y];
UpdateRenderedSprite(c);
remove.Add(c);
}
}
@@ -116,62 +145,45 @@ namespace OpenRA.Traits
if (!world.Map.IsInMap(a.X, a.Y))
return false;
if (!rt.info.AllowedTerrainTypes.Contains(world.GetTerrainInfo(a).Type))
if (!rt.Info.AllowedTerrainTypes.Contains(world.GetTerrainInfo(a).Type))
return false;
if (!rt.info.AllowUnderActors && world.ActorMap.AnyUnitsAt(a))
if (!rt.Info.AllowUnderActors && world.ActorMap.AnyUnitsAt(a))
return false;
return true;
}
Sprite[] ChooseContent(ResourceType t)
CellContents CreateResourceCell(ResourceType t, CPos p)
{
return t.info.Sprites[world.SharedRandom.Next(t.info.Sprites.Length)];
}
int GetAdjacentCellsWith(ResourceType t, int i, int j)
{
int sum = 0;
for (var u = -1; u < 2; u++)
for (var v = -1; v < 2; v++)
if (content[i + u, j + v].Type == t)
++sum;
return sum;
}
int GetIdealDensity(int x, int y)
{
return (GetAdjacentCellsWith(content[x, y].Type, x, y) *
(content[x, y].Image.Length - 1)) / 9;
}
public void AddResource(ResourceType t, int i, int j, int n)
{
if (content[i, j].Type == null)
world.Map.CustomTerrain[p.X, p.Y] = t.Info.TerrainType;
return new CellContents
{
content[i, j].Type = t;
content[i, j].Image = ChooseContent(t);
content[i, j].Density = -1;
}
Type = t,
Variant = ChooseRandomVariant(t),
Density = t.Info.MaxDensity,
};
}
if (content[i, j].Type != t)
public void AddResource(ResourceType t, CPos p, int n)
{
var cell = content[p.X, p.Y];
if (cell.Type == null)
cell = CreateResourceCell(t, p);
if (cell.Type != t)
return;
content[i, j].Density = Math.Min(
content[i, j].Image.Length - 1,
content[i, j].Density + n);
cell.Density = Math.Min(cell.Type.Info.MaxDensity, cell.Density + n);
content[p.X, p.Y] = cell;
world.Map.CustomTerrain[i, j] = t.info.TerrainType;
var cell = new CPos(i, j);
if (!dirty.Contains(cell))
dirty.Add(cell);
if (!dirty.Contains(p))
dirty.Add(p);
}
public bool IsFull(int i, int j)
{
return content[i, j].Density == content[i, j].Image.Length - 1;
return content[i, j].Density == content[i, j].Type.Info.MaxDensity;
}
public ResourceType Harvest(CPos p)
@@ -181,11 +193,7 @@ namespace OpenRA.Traits
return null;
if (--content[p.X, p.Y].Density < 0)
{
content[p.X, p.Y].Type = null;
content[p.X, p.Y].Image = null;
world.Map.CustomTerrain[p.X, p.Y] = null;
}
content[p.X, p.Y] = EmptyCell;
if (!dirty.Contains(p))
dirty.Add(p);
@@ -199,10 +207,8 @@ namespace OpenRA.Traits
if (content[p.X, p.Y].Type == null)
return;
content[p.X, p.Y].Type = null;
content[p.X, p.Y].Image = null;
content[p.X, p.Y].Density = 0;
world.Map.CustomTerrain[p.X, p.Y] = null;
// Clear cell
content[p.X, p.Y] = EmptyCell;
if (!dirty.Contains(p))
dirty.Add(p);
@@ -213,17 +219,18 @@ namespace OpenRA.Traits
public int GetResourceDensity(CPos p) { return content[p.X, p.Y].Density; }
public int GetMaxResourceDensity(CPos p)
{
if (content[p.X, p.Y].Image == null)
if (content[p.X, p.Y].Type == null)
return 0;
return content[p.X, p.Y].Image.Length - 1;
return content[p.X, p.Y].Type.Info.MaxDensity;
}
public struct CellContents
{
public ResourceType Type;
public Sprite[] Image;
public int Density;
public string Variant;
public Sprite Sprite;
}
}
}

View File

@@ -8,37 +8,54 @@
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
namespace OpenRA.Traits
{
public class ResourceTypeInfo : ITraitInfo
{
public readonly string[] SpriteNames = { };
// Hack: The editor is getting really unmaintanable...
public readonly string EditorSprite;
public readonly string[] Variants = { };
public readonly string Palette = "terrain";
public readonly int ResourceType = 1;
public readonly int ValuePerUnit = 0;
public readonly int MaxDensity = 10;
public readonly string Name = null;
public readonly string TerrainType = "Ore";
public readonly string[] AllowedTerrainTypes = { };
public readonly bool AllowUnderActors = false;
public Sprite[][] Sprites;
public PaletteReference PaletteRef;
public PipType PipColor = PipType.Yellow;
public object Create(ActorInitializer init) { return new ResourceType(this); }
}
public class ResourceType
public class ResourceType : IWorldLoaded
{
public ResourceTypeInfo info;
public readonly ResourceTypeInfo Info;
public PaletteReference Palette { get; private set; }
public readonly Dictionary<string, Sprite[]> Variants;
public ResourceType(ResourceTypeInfo info)
{
this.info = info;
this.Info = info;
Variants = new Dictionary<string, Sprite[]>();
foreach (var v in info.Variants)
{
var seq = SequenceProvider.GetSequence("resources", v);
var sprites = Exts.MakeArray(seq.Length, x => seq.GetSprite(x));
Variants.Add(v, sprites);
}
}
public void WorldLoaded(World w, WorldRenderer wr)
{
Palette = wr.Palette(Info.Palette);
}
}
}

View File

@@ -35,7 +35,7 @@ namespace OpenRA.Mods.Cnc
var rl = self.World.WorldActor.Trait<ResourceLayer>();
var r = rl.GetResource(self.Location);
if (r == null) return;
if (!info.Resources.Contains(r.info.Name)) return;
if (!info.Resources.Contains(r.Info.Name)) return;
var weapon = Rules.Weapons[info.Weapon.ToLowerInvariant()];

View File

@@ -73,7 +73,7 @@ namespace OpenRA.Mods.RA.Activities
if (resType == null) return 1;
// Can the harvester collect this kind of resource?
if (!harvInfo.Resources.Contains(resType.info.Name)) return 1;
if (!harvInfo.Resources.Contains(resType.Info.Name)) return 1;
if (territory != null)
{

View File

@@ -143,8 +143,8 @@ namespace OpenRA.Mods.RA
public void AcceptResource(ResourceType type)
{
if (!contents.ContainsKey(type.info)) contents[type.info] = 1;
else contents[type.info]++;
if (!contents.ContainsKey(type.Info)) contents[type.Info] = 1;
else contents[type.Info]++;
}
public void UnblockRefinery(Actor self)
@@ -357,7 +357,7 @@ namespace OpenRA.Mods.RA
if (resType == null) return 1;
// Can the harvester collect this kind of resource?
if (!harvInfo.Resources.Contains(resType.info.Name)) return 1;
if (!harvInfo.Resources.Contains(resType.Info.Name)) return 1;
// Another harvester has claimed this resource:
if (territory != null)
@@ -436,7 +436,7 @@ namespace OpenRA.Mods.RA
var res = self.World.WorldActor.Trait<ResourceLayer>().GetRenderedResource(location);
var info = self.Info.Traits.Get<HarvesterInfo>();
if (res == null || !info.Resources.Contains(res.info.Name))
if (res == null || !info.Resources.Contains(res.Info.Name))
return false;
cursor = "harvest";

View File

@@ -36,7 +36,7 @@ namespace OpenRA.Mods.RA
var info = self.Info.Traits.Get<SeedsResourceInfo>();
var resourceType = self.World.WorldActor
.TraitsImplementing<ResourceType>()
.FirstOrDefault(t => t.info.Name == info.ResourceType);
.FirstOrDefault(t => t.Info.Name == info.ResourceType);
if (resourceType == null)
throw new InvalidOperationException("No such resource type `{0}`".F(info.ResourceType));
@@ -51,7 +51,7 @@ namespace OpenRA.Mods.RA
if (cell != null && self.World.Map.IsInMap(cell.Value) &&
(resLayer.GetResource(cell.Value) == resourceType
|| (resLayer.GetResource(cell.Value) == null && resLayer.AllowResourceAt(resourceType, cell.Value))))
resLayer.AddResource(resourceType, cell.Value.X, cell.Value.Y, 1);
resLayer.AddResource(resourceType, cell.Value, 1);
ticks = info.Interval;
}

View File

@@ -292,7 +292,9 @@ World:
ResourceType: 1
Palette: staticterrain
TerrainType: Tiberium
SpriteNames: ti1,ti2,ti3,ti4,ti5,ti6,ti7,ti8,ti9,ti10,ti11,ti12
EditorSprite: ti1
Variants: ti1,ti2,ti3,ti4,ti5,ti6,ti7,ti8,ti9,ti10,ti11,ti12
MaxDensity: 12
ValuePerUnit: 35
Name: Tiberium
PipColor: Green
@@ -302,7 +304,9 @@ World:
ResourceType: 2
Palette: staticterrain
TerrainType: BlueTiberium
SpriteNames: bti1,bti2,bti3,bti4,bti5,bti6,bti7,bti8,bti9,bti10,bti11,bti12
EditorSprite: bti1
Variants: bti1,bti2,bti3,bti4,bti5,bti6,bti7,bti8,bti9,bti10,bti11,bti12
MaxDensity: 12
ValuePerUnit: 75
Name: BlueTiberium
PipColor: Blue

View File

@@ -311,3 +311,53 @@ moveflsh:
Start: 0
Length: *
Tick: 80
resources:
ti1: ti1
Length: *
ti2: ti2
Length: *
ti3: ti3
Length: *
ti4: ti4
Length: *
ti5: ti5
Length: *
ti6: ti6
Length: *
ti7: ti7
Length: *
ti8: ti8
Length: *
ti9: ti9
Length: *
ti10: ti10
Length: *
ti11: ti11
Length: *
ti12: ti12
Length: *
bti1: bti1
Length: *
bti2: bti2
Length: *
bti3: bti3
Length: *
bti4: bti4
Length: *
bti5: bti5
Length: *
bti6: bti6
Length: *
bti7: bti7
Length: *
bti8: bti8
Length: *
bti9: bti9
Length: *
bti10: bti10
Length: *
bti11: bti11
Length: *
bti12: bti12
Length: *

View File

@@ -447,7 +447,9 @@ World:
ResourceType: 1
Palette: d2k
TerrainType: Spice
SpriteNames: spice1
EditorSprite: spice1
Variants: spice
MaxDensity: 20
ValuePerUnit: 25
Name: Spice
PipColor: green

View File

@@ -305,3 +305,7 @@ moveflsh:
Length: 5
Tick: 80
BlendMode: Subtractive
resources:
spice: spice1
Length: *

View File

@@ -632,7 +632,9 @@ World:
ResourceType@ore:
ResourceType: 1
Palette: player
SpriteNames: gold01,gold02,gold03,gold04
EditorSprite: gold01
Variants: gold01,gold02,gold03,gold04
MaxDensity: 12
ValuePerUnit: 25
Name: Ore
PipColor: Yellow
@@ -642,7 +644,9 @@ World:
ResourceType@gem:
ResourceType: 2
Palette: player
SpriteNames: gem01,gem02,gem03,gem04
EditorSprite: gem01
Variants: gem01,gem02,gem03,gem04
MaxDensity: 3
ValuePerUnit: 50
Name: Gems
PipColor: Red

View File

@@ -469,3 +469,21 @@ overlay:
Start: 0
target-invalid:
Start: 1
resources:
gold01: gold01
Length: *
gold02: gold02
Length: *
gold03: gold03
Length: *
gold04: gold04
Length: *
gem01: gem01
Length: *
gem02: gem02
Length: *
gem03: gem03
Length: *
gem04: gem04
Length: *

View File

@@ -116,6 +116,17 @@ World:
Race: nod
ResourceLayer:
ResourceClaimLayer:
ResourceType@gem:
ResourceType: 2
Palette: player
EditorSprite: shadow
Variants: fake
ValuePerUnit: 50
Name: Gems
PipColor: Red
AllowedTerrainTypes: Clear,Road
AllowUnderActors: false
TerrainType: Gems
PathfinderDebugOverlay:
SpawnMapActors:
CreateMPPlayers:

View File

@@ -249,3 +249,8 @@ moveflsh:
Start: 0
Length: *
Tick: 30
# TODO: placeholder
resources:
fake: shadow
Length: *