From 410cd1c7b137cdf3110a9e47493a49baeaace767 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 6 Dec 2013 17:56:46 +1300 Subject: [PATCH] Reorganise ResourceLayer implementation. --- OpenRA.Editor/RenderUtils.cs | 2 +- OpenRA.Editor/ResourceTool.cs | 2 +- OpenRA.Game/Traits/World/ResourceLayer.cs | 187 +++++++++++---------- OpenRA.Game/Traits/World/ResourceType.cs | 31 +++- OpenRA.Mods.Cnc/PoisonedByTiberium.cs | 2 +- OpenRA.Mods.RA/Activities/FindResources.cs | 2 +- OpenRA.Mods.RA/Harvester.cs | 8 +- OpenRA.Mods.RA/SeedsResource.cs | 4 +- mods/cnc/rules/system.yaml | 8 +- mods/cnc/sequences/misc.yaml | 52 +++++- mods/d2k/rules/system.yaml | 4 +- mods/d2k/sequences/misc.yaml | 6 +- mods/ra/rules/system.yaml | 8 +- mods/ra/sequences/misc.yaml | 20 ++- mods/ts/rules/system.yaml | 11 ++ mods/ts/sequences/misc.yaml | 7 +- 16 files changed, 238 insertions(+), 116 deletions(-) diff --git a/OpenRA.Editor/RenderUtils.cs b/OpenRA.Editor/RenderUtils.cs index 2d29595e8b..1fd5835221 100644 --- a/OpenRA.Editor/RenderUtils.cs +++ b/OpenRA.Editor/RenderUtils.cs @@ -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 diff --git a/OpenRA.Editor/ResourceTool.cs b/OpenRA.Editor/ResourceTool.cs index 2b601c8245..ee09cac1a5 100644 --- a/OpenRA.Editor/ResourceTool.cs +++ b/OpenRA.Editor/ResourceTool.cs @@ -27,7 +27,7 @@ namespace OpenRA.Editor = new TileReference { 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, diff --git a/OpenRA.Game/Traits/World/ResourceLayer.cs b/OpenRA.Game/Traits/World/ResourceLayer.cs index 54bbe37a44..8be0bc0446 100644 --- a/OpenRA.Game/Traits/World/ResourceLayer.cs +++ b/OpenRA.Game/Traits/World/ResourceLayer.cs @@ -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 { } + public class ResourceLayerInfo : TraitInfo, Requires { } 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 dirty; - bool hasSetupPalettes; public void Render(WorldRenderer wr) { - if (!hasSetupPalettes) - { - hasSetupPalettes = true; - foreach (var rt in world.WorldActor.TraitsImplementing()) - 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(); - resourceTypes = w.WorldActor.TraitsImplementing().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() + .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(); @@ -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; } } } diff --git a/OpenRA.Game/Traits/World/ResourceType.cs b/OpenRA.Game/Traits/World/ResourceType.cs index 62167b1eda..d477f25c5e 100644 --- a/OpenRA.Game/Traits/World/ResourceType.cs +++ b/OpenRA.Game/Traits/World/ResourceType.cs @@ -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 Variants; + public ResourceType(ResourceTypeInfo info) { - this.info = info; + this.Info = info; + Variants = new Dictionary(); + 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); } } } diff --git a/OpenRA.Mods.Cnc/PoisonedByTiberium.cs b/OpenRA.Mods.Cnc/PoisonedByTiberium.cs index 8792168f5e..da238179fe 100644 --- a/OpenRA.Mods.Cnc/PoisonedByTiberium.cs +++ b/OpenRA.Mods.Cnc/PoisonedByTiberium.cs @@ -35,7 +35,7 @@ namespace OpenRA.Mods.Cnc var rl = self.World.WorldActor.Trait(); 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()]; diff --git a/OpenRA.Mods.RA/Activities/FindResources.cs b/OpenRA.Mods.RA/Activities/FindResources.cs index 4942555a3c..7d71cb5864 100755 --- a/OpenRA.Mods.RA/Activities/FindResources.cs +++ b/OpenRA.Mods.RA/Activities/FindResources.cs @@ -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) { diff --git a/OpenRA.Mods.RA/Harvester.cs b/OpenRA.Mods.RA/Harvester.cs index 692d3bfe81..28b70d5412 100644 --- a/OpenRA.Mods.RA/Harvester.cs +++ b/OpenRA.Mods.RA/Harvester.cs @@ -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().GetRenderedResource(location); var info = self.Info.Traits.Get(); - if (res == null || !info.Resources.Contains(res.info.Name)) + if (res == null || !info.Resources.Contains(res.Info.Name)) return false; cursor = "harvest"; diff --git a/OpenRA.Mods.RA/SeedsResource.cs b/OpenRA.Mods.RA/SeedsResource.cs index 70ab7ad172..f762b21117 100644 --- a/OpenRA.Mods.RA/SeedsResource.cs +++ b/OpenRA.Mods.RA/SeedsResource.cs @@ -36,7 +36,7 @@ namespace OpenRA.Mods.RA var info = self.Info.Traits.Get(); var resourceType = self.World.WorldActor .TraitsImplementing() - .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; } diff --git a/mods/cnc/rules/system.yaml b/mods/cnc/rules/system.yaml index fafbc8dba8..b12ddf9188 100644 --- a/mods/cnc/rules/system.yaml +++ b/mods/cnc/rules/system.yaml @@ -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 diff --git a/mods/cnc/sequences/misc.yaml b/mods/cnc/sequences/misc.yaml index d6a88dc522..06dbc7546a 100644 --- a/mods/cnc/sequences/misc.yaml +++ b/mods/cnc/sequences/misc.yaml @@ -310,4 +310,54 @@ moveflsh: idle: Start: 0 Length: * - Tick: 80 \ No newline at end of file + 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: * \ No newline at end of file diff --git a/mods/d2k/rules/system.yaml b/mods/d2k/rules/system.yaml index 2426db13f3..30ac3a7354 100644 --- a/mods/d2k/rules/system.yaml +++ b/mods/d2k/rules/system.yaml @@ -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 diff --git a/mods/d2k/sequences/misc.yaml b/mods/d2k/sequences/misc.yaml index ceedd4c11b..0b0fc2df3c 100644 --- a/mods/d2k/sequences/misc.yaml +++ b/mods/d2k/sequences/misc.yaml @@ -304,4 +304,8 @@ moveflsh: Start: 3621 Length: 5 Tick: 80 - BlendMode: Subtractive \ No newline at end of file + BlendMode: Subtractive + +resources: + spice: spice1 + Length: * \ No newline at end of file diff --git a/mods/ra/rules/system.yaml b/mods/ra/rules/system.yaml index 2614a478c8..2e3af5b920 100644 --- a/mods/ra/rules/system.yaml +++ b/mods/ra/rules/system.yaml @@ -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 diff --git a/mods/ra/sequences/misc.yaml b/mods/ra/sequences/misc.yaml index 045c9ab0c5..3016ebea10 100644 --- a/mods/ra/sequences/misc.yaml +++ b/mods/ra/sequences/misc.yaml @@ -468,4 +468,22 @@ overlay: target-valid-temperat: Start: 0 target-invalid: - Start: 1 \ No newline at end of file + 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: * \ No newline at end of file diff --git a/mods/ts/rules/system.yaml b/mods/ts/rules/system.yaml index 4974e9196a..cc14e72d5e 100644 --- a/mods/ts/rules/system.yaml +++ b/mods/ts/rules/system.yaml @@ -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: diff --git a/mods/ts/sequences/misc.yaml b/mods/ts/sequences/misc.yaml index e676752648..b14c3c5006 100644 --- a/mods/ts/sequences/misc.yaml +++ b/mods/ts/sequences/misc.yaml @@ -248,4 +248,9 @@ moveflsh: idle: ring Start: 0 Length: * - Tick: 30 \ No newline at end of file + Tick: 30 + +# TODO: placeholder +resources: + fake: shadow + Length: * \ No newline at end of file