From 410cd1c7b137cdf3110a9e47493a49baeaace767 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 6 Dec 2013 17:56:46 +1300 Subject: [PATCH 1/4] 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 From ca2749dcc0d2f954aaa6826b4f00938b27b302a7 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 6 Dec 2013 22:23:17 +1300 Subject: [PATCH 2/4] Support soft edges on D2K spice tiles. Fixes #2247. --- OpenRA.Mods.D2k/D2kResourceLayer.cs | 172 +++++++++++++++++++++++++ OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj | 1 + mods/d2k/rules/system.yaml | 2 +- mods/d2k/sequences/misc.yaml | 6 +- 4 files changed, 178 insertions(+), 3 deletions(-) create mode 100644 OpenRA.Mods.D2k/D2kResourceLayer.cs diff --git a/OpenRA.Mods.D2k/D2kResourceLayer.cs b/OpenRA.Mods.D2k/D2kResourceLayer.cs new file mode 100644 index 0000000000..fbf1f9c427 --- /dev/null +++ b/OpenRA.Mods.D2k/D2kResourceLayer.cs @@ -0,0 +1,172 @@ +#region Copyright & License Information +/* + * Copyright 2007-2013 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. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; + +namespace OpenRA.Traits +{ + public class D2kResourceLayerInfo : TraitInfo { } + + public class D2kResourceLayer : ResourceLayer + { + [Flags] enum ClearSides : byte + { + None = 0x0, + Left = 0x1, + Top = 0x2, + Right = 0x4, + Bottom = 0x8, + + TopLeft = 0x10, + TopRight = 0x20, + BottomLeft = 0x40, + BottomRight = 0x80, + + All = 0xFF + }; + + static readonly Dictionary variants = new Dictionary() + { + { "cleara", new[] { 0, 50 } }, + { "clearb", new[] { 1, 51 } }, + { "clearc", new[] { 43, 52 } }, + { "cleard", new[] { 0, 53 } }, + }; + + static readonly Dictionary spriteMap = new Dictionary() + { + { ClearSides.None, 0 }, + { ClearSides.Left | ClearSides.Top | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 2 }, + { ClearSides.Top | ClearSides.Right | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 3 }, + { ClearSides.Left | ClearSides.Bottom | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 4 }, + { ClearSides.Right | ClearSides.Bottom | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 5 }, + { ClearSides.Left | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 6 }, + { ClearSides.Right | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 7 }, + { ClearSides.Top | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 8 }, + { ClearSides.Bottom | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 9 }, + { ClearSides.Left | ClearSides.Top | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft, 10 }, + { ClearSides.Top | ClearSides.Right | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomRight, 11 }, + { ClearSides.Left | ClearSides.Bottom | ClearSides.TopLeft | ClearSides.BottomLeft | ClearSides.BottomRight, 12 }, + { ClearSides.Right | ClearSides.Bottom | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 13 }, + { ClearSides.Left | ClearSides.Top | ClearSides.Right | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 14 }, + { ClearSides.Left | ClearSides.Right | ClearSides.Bottom | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 15 }, + { ClearSides.Left | ClearSides.Top | ClearSides.Bottom | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 16 }, + { ClearSides.Top | ClearSides.Right | ClearSides.Bottom | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 17 }, + { ClearSides.Top | ClearSides.TopLeft | ClearSides.TopRight, 18 }, + { ClearSides.Right | ClearSides.TopRight | ClearSides.BottomRight, 19 }, + { ClearSides.Left | ClearSides.TopLeft | ClearSides.BottomLeft, 20 }, + { ClearSides.Bottom | ClearSides.BottomLeft | ClearSides.BottomRight, 21 }, + { ClearSides.TopLeft, 22 }, + { ClearSides.TopRight, 23 }, + { ClearSides.BottomLeft, 24 }, + { ClearSides.BottomRight, 25 }, + { ClearSides.Left | ClearSides.TopLeft | ClearSides.BottomLeft | ClearSides.BottomRight, 26 }, + { ClearSides.Right | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 27 }, + { ClearSides.Top | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomRight, 28 }, + { ClearSides.Top | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft, 29 }, + { ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 30 }, + { ClearSides.TopLeft | ClearSides.BottomLeft | ClearSides.BottomRight, 31 }, + { ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomRight, 32 }, + { ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft, 33 }, + { ClearSides.TopRight | ClearSides.BottomRight, 34 }, + { ClearSides.TopLeft | ClearSides.TopRight, 35 }, + { ClearSides.TopRight | ClearSides.BottomLeft, 36 }, + { ClearSides.TopLeft | ClearSides.BottomLeft, 37 }, + { ClearSides.BottomLeft | ClearSides.BottomRight, 38 }, + { ClearSides.TopLeft | ClearSides.BottomRight, 39 }, + { ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 40 }, + { ClearSides.Left | ClearSides.Right | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 41 }, + { ClearSides.Top | ClearSides.Bottom | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 42 }, + { ClearSides.All, 44 }, + { ClearSides.Left | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft, 46 }, + { ClearSides.Right | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomRight, 47 }, + { ClearSides.Bottom | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 48 }, + { ClearSides.Bottom | ClearSides.TopLeft | ClearSides.BottomLeft | ClearSides.BottomRight, 49 }, + }; + + ClearSides FindClearSides(ResourceType t, CPos p) + { + var ret = ClearSides.None; + if (render[p.X, p.Y - 1].Type != t) + ret |= ClearSides.Top | ClearSides.TopLeft | ClearSides.TopRight; + + if (render[p.X - 1, p.Y].Type != t) + ret |= ClearSides.Left | ClearSides.TopLeft | ClearSides.BottomLeft; + + if (render[p.X + 1, p.Y].Type != t) + ret |= ClearSides.Right | ClearSides.TopRight | ClearSides.BottomRight; + + if (render[p.X, p.Y + 1].Type != t) + ret |= ClearSides.Bottom | ClearSides.BottomLeft | ClearSides.BottomRight; + + if (render[p.X - 1, p.Y - 1].Type != t) + ret |= ClearSides.TopLeft; + + if (render[p.X + 1, p.Y - 1].Type != t) + ret |= ClearSides.TopRight; + + if (render[p.X - 1, p.Y + 1].Type != t) + ret |= ClearSides.BottomLeft; + + if (render[p.X + 1, p.Y + 1].Type != t) + ret |= ClearSides.BottomRight; + + return ret; + } + + void UpdateRenderedTileInner(CPos p) + { + var t = render[p.X, p.Y]; + if (t.Density > 0) + { + var clear = FindClearSides(t.Type, p); + int index; + + if (clear == ClearSides.None) + { + var sprites = variants[t.Variant]; + var frame = t.Density > t.Type.Info.MaxDensity / 2 ? 1 : 0; + t.Sprite = t.Type.Variants.First().Value[sprites[frame]]; + } + else if (spriteMap.TryGetValue(clear, out index)) + t.Sprite = t.Type.Variants.First().Value[index]; + else + t.Sprite = null; + } + else + t.Sprite = null; + + render[p.X, p.Y] = t; + } + + protected override void UpdateRenderedSprite(CPos p) + { + // Need to update neighbouring tiles too + UpdateRenderedTileInner(p); + UpdateRenderedTileInner(p + new CVec(-1, -1)); + UpdateRenderedTileInner(p + new CVec(0, -1)); + UpdateRenderedTileInner(p + new CVec(1, -1)); + UpdateRenderedTileInner(p + new CVec(-1, 0)); + UpdateRenderedTileInner(p + new CVec(1, 0)); + UpdateRenderedTileInner(p + new CVec(-1, 1)); + UpdateRenderedTileInner(p + new CVec(0, 1)); + UpdateRenderedTileInner(p + new CVec(1, 1)); + } + + protected override string ChooseRandomVariant(ResourceType t) + { + return variants.Keys.Random(Game.CosmeticRandom); + } + } +} diff --git a/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj b/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj index c659a4fc22..b6fa211885 100644 --- a/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj +++ b/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj @@ -79,6 +79,7 @@ + diff --git a/mods/d2k/rules/system.yaml b/mods/d2k/rules/system.yaml index 30ac3a7354..b5938ef53d 100644 --- a/mods/d2k/rules/system.yaml +++ b/mods/d2k/rules/system.yaml @@ -441,7 +441,7 @@ World: Race: ordos DomainIndex: PathfinderDebugOverlay: - ResourceLayer: + D2kResourceLayer: ResourceClaimLayer: ResourceType@Spice: ResourceType: 1 diff --git a/mods/d2k/sequences/misc.yaml b/mods/d2k/sequences/misc.yaml index 0b0fc2df3c..5774d04056 100644 --- a/mods/d2k/sequences/misc.yaml +++ b/mods/d2k/sequences/misc.yaml @@ -307,5 +307,7 @@ moveflsh: BlendMode: Subtractive resources: - spice: spice1 - Length: * \ No newline at end of file + spice: BLOXBASE + Frames: 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 300, 301, 320, 321 + Length: 54 + Offset: -16,-16 From 9eaf496d72a0d18efc88d574c99fd0b5918e1716 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 6 Dec 2013 22:31:39 +1300 Subject: [PATCH 3/4] Update CHANGELOG. --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 7d6b8c7976..03d3d4215c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -73,6 +73,7 @@ NEW: Fixed infantry sometimes using the wrong animation when standing. Fixed A* debug overlay. Fixed R8 offsets for sprites with embedded palettes. + Implemented proper spice rendering. Engine: Replays are now saved in per-mod and per-version folders. Added password protection support for servers. @@ -125,6 +126,7 @@ NEW: Removed Utility --tmp-png, --r8, --fromd2 commands (use --png instead). Removed Asset Browser file extraction / conversion (use the Utility instead). Unified sprite loading allows any sprite type to be used anywhere: shp can now be used for terrain, and tmp for units. + Harvestable resource definitions (ResourceTypes) have changed, and now specify their artwork using via sequences. 20130915: All mods: From c3e4cb5069e6ca420dc440bf2b4f9b471ab0bc2a Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 6 Dec 2013 23:28:26 +1300 Subject: [PATCH 4/4] Remove spice1.shp. --- mods/d2k/bits/spice1.shp | Bin 21030 -> 0 bytes mods/d2k/rules/system.yaml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 mods/d2k/bits/spice1.shp diff --git a/mods/d2k/bits/spice1.shp b/mods/d2k/bits/spice1.shp deleted file mode 100644 index 82de5738bad2d1ab27d65313c8f824a1551ce81a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21030 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 diff --git a/mods/d2k/rules/system.yaml b/mods/d2k/rules/system.yaml index b5938ef53d..34c6f8bac0 100644 --- a/mods/d2k/rules/system.yaml +++ b/mods/d2k/rules/system.yaml @@ -447,7 +447,7 @@ World: ResourceType: 1 Palette: d2k TerrainType: Spice - EditorSprite: spice1 + EditorSprite: spice0 Variants: spice MaxDensity: 20 ValuePerUnit: 25