diff --git a/OpenRA.Mods.Common/Activities/HarvestResource.cs b/OpenRA.Mods.Common/Activities/HarvestResource.cs index 8ce632d96d..57b274ea72 100644 --- a/OpenRA.Mods.Common/Activities/HarvestResource.cs +++ b/OpenRA.Mods.Common/Activities/HarvestResource.cs @@ -81,14 +81,14 @@ namespace OpenRA.Mods.Common.Activities } } - var resource = resLayer.Harvest(self.Location); - if (resource == null) + var resourceType = resLayer.GetResourceType(self.Location); + if (resourceType == null || resLayer.RemoveResource(resourceType, self.Location) != 1) return true; - harv.AcceptResource(self, resource); + harv.AcceptResource(self, resourceType); foreach (var t in notifyHarvesterActions) - t.Harvested(self, resource); + t.Harvested(self, resourceType); QueueChild(new Wait(harvInfo.BaleLoadDelay)); return false; diff --git a/OpenRA.Mods.Common/Traits/SeedsResource.cs b/OpenRA.Mods.Common/Traits/SeedsResource.cs index ce985bef0d..e7bb894501 100644 --- a/OpenRA.Mods.Common/Traits/SeedsResource.cs +++ b/OpenRA.Mods.Common/Traits/SeedsResource.cs @@ -68,8 +68,8 @@ namespace OpenRA.Mods.Common.Traits (resLayer.GetResourceType(p) == resourceType && resLayer.IsFull(p))) .Cast().FirstOrDefault(); - if (cell != null && resLayer.CanSpawnResourceAt(resourceType, cell.Value)) - resLayer.AddResource(resourceType, cell.Value, 1); + if (cell != null && resLayer.CanAddResource(resourceType, cell.Value)) + resLayer.AddResource(resourceType, cell.Value); } } } diff --git a/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs b/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs index 188a3f4f7f..6c0f954b67 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs @@ -36,6 +36,10 @@ namespace OpenRA.Mods.Common.Traits public event Action CellChanged; ResourceLayerContents IResourceLayer.GetResource(CPos cell) { return Tiles[cell]; } + bool IResourceLayer.CanAddResource(ResourceType resourceType, CPos cell, int amount) { return CanAddResource(resourceType, cell, amount); } + int IResourceLayer.AddResource(ResourceType resourceType, CPos cell, int amount) { return AddResource(resourceType, cell, amount); } + int IResourceLayer.RemoveResource(ResourceType resourceType, CPos cell, int amount) { return RemoveResource(resourceType, cell, amount); } + void IResourceLayer.ClearResources(CPos cell) { ClearResources(cell); } bool IResourceLayer.IsVisible(CPos cell) { return Map.Contains(cell); } public EditorResourceLayer(Actor self) @@ -73,12 +77,7 @@ namespace OpenRA.Mods.Common.Traits var newTerrain = byte.MaxValue; if (Resources.TryGetValue(tile.Type, out var type)) { - newTile = new ResourceLayerContents - { - Type = type, - Density = CalculateCellDensity(type, cell) - }; - + newTile = new ResourceLayerContents(type, CalculateCellDensity(type, cell)); newTerrain = Map.Rules.TerrainInfo.GetTerrainIndex(type.Info.TerrainType); } @@ -104,8 +103,7 @@ namespace OpenRA.Mods.Common.Traits continue; UpdateNetWorth(neighbouringTile.Type, neighbouringTile.Density, neighbouringTile.Type, density); - neighbouringTile.Density = density; - Tiles[neighbouringCell] = neighbouringTile; + Tiles[neighbouringCell] = new ResourceLayerContents(neighbouringTile.Type, density); CellChanged?.Invoke(neighbouringCell, type); } @@ -142,6 +140,74 @@ namespace OpenRA.Mods.Common.Traits return Math.Max(int2.Lerp(0, type.Info.MaxDensity, adjacent, 9), 1); } + bool AllowResourceAt(ResourceType rt, CPos cell) + { + var mapResources = Map.Resources; + if (!mapResources.Contains(cell)) + return false; + + if (!rt.Info.AllowedTerrainTypes.Contains(Map.GetTerrainInfo(cell).Type)) + return false; + + // TODO: Check against actors in the EditorActorLayer + return rt.Info.AllowOnRamps || Map.Ramp[cell] == 0; + } + + bool CanAddResource(ResourceType resourceType, CPos cell, int amount = 1) + { + var resources = Map.Resources; + if (!resources.Contains(cell)) + return false; + + var content = resources[cell]; + if (content.Type == 0) + return amount <= resourceType.Info.MaxDensity && AllowResourceAt(resourceType, cell); + + if (content.Type != resourceType.Info.ResourceType) + return false; + + return content.Index + amount <= resourceType.Info.MaxDensity; + } + + int AddResource(ResourceType resourceType, CPos cell, int amount = 1) + { + var resources = Map.Resources; + if (!resources.Contains(cell)) + return 0; + + var content = resources[cell]; + if (content.Type != 0 && content.Type != resourceType.Info.ResourceType) + return 0; + + var oldDensity = content.Index; + var density = (byte)Math.Min(resourceType.Info.MaxDensity, oldDensity + amount); + Map.Resources[cell] = new ResourceTile((byte)resourceType.Info.ResourceType, density); + + return density - oldDensity; + } + + int RemoveResource(ResourceType resourceType, CPos cell, int amount = 1) + { + var resources = Map.Resources; + if (!resources.Contains(cell)) + return 0; + + var content = resources[cell]; + if (content.Type == 0 || content.Type != resourceType.Info.ResourceType) + return 0; + + var oldDensity = content.Index; + var density = (byte)Math.Max(0, oldDensity - amount); + resources[cell] = density > 0 ? new ResourceTile((byte)resourceType.Info.ResourceType, density) : default; + + return oldDensity - density; + } + + void ClearResources(CPos cell) + { + Map.Resources[cell] = default; + } + void INotifyActorDisposing.Disposing(Actor self) { if (disposed) diff --git a/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs b/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs index 95b075071f..ee093a71b6 100644 --- a/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs @@ -18,9 +18,15 @@ namespace OpenRA.Mods.Common.Traits { public struct ResourceLayerContents { - public static readonly ResourceLayerContents Empty = default(ResourceLayerContents); - public ResourceType Type; - public int Density; + public static readonly ResourceLayerContents Empty = default; + public readonly ResourceType Type; + public readonly int Density; + + public ResourceLayerContents(ResourceType type, int density) + { + Type = type; + Density = density; + } } public interface IResourceLayerInfo : ITraitInfoInterface { } @@ -30,6 +36,10 @@ namespace OpenRA.Mods.Common.Traits { event Action CellChanged; ResourceLayerContents GetResource(CPos cell); + bool CanAddResource(ResourceType resourceType, CPos cell, int amount = 1); + int AddResource(ResourceType resourceType, CPos cell, int amount = 1); + int RemoveResource(ResourceType resourceType, CPos cell, int amount = 1); + void ClearResources(CPos cell); bool IsVisible(CPos cell); } @@ -100,10 +110,7 @@ namespace OpenRA.Mods.Common.Traits // Adjacent includes the current cell, so is always >= 1 var adjacent = GetAdjacentCellsWith(type, cell); var density = int2.Lerp(0, type.Info.MaxDensity, adjacent, 9); - var temp = Content[cell]; - temp.Density = Math.Max(density, 1); - - Content[cell] = temp; + Content[cell] = new ResourceLayerContents(Content[cell].Type, Math.Max(density, 1)); } } } @@ -125,40 +132,94 @@ namespace OpenRA.Mods.Common.Traits return rt.Info.AllowOnRamps || world.Map.Ramp[cell] == 0; } - public bool CanSpawnResourceAt(ResourceType newResourceType, CPos cell) - { - if (!world.Map.Contains(cell)) - return false; - - var currentResourceType = GetResourceType(cell); - return (currentResourceType == newResourceType && !IsFull(cell)) - || (currentResourceType == null && AllowResourceAt(newResourceType, cell)); - } - ResourceLayerContents CreateResourceCell(ResourceType t, CPos cell) { world.Map.CustomTerrain[cell] = world.Map.Rules.TerrainInfo.GetTerrainIndex(t.Info.TerrainType); ++resCells; - return new ResourceLayerContents - { - Type = t - }; + return new ResourceLayerContents(t, 0); } - public void AddResource(ResourceType t, CPos p, int n) + public bool CanAddResource(ResourceType resourceType, CPos cell, int amount = 1) { - var cell = Content[p]; - if (cell.Type == null) - cell = CreateResourceCell(t, p); + if (!world.Map.Contains(cell)) + return false; - if (cell.Type != t) + var content = Content[cell]; + if (content.Type == null) + return amount <= resourceType.Info.MaxDensity && AllowResourceAt(resourceType, cell); + + if (content.Type != resourceType) + return false; + + return content.Density + amount <= resourceType.Info.MaxDensity; + } + + public int AddResource(ResourceType resourceType, CPos cell, int amount = 1) + { + if (!Content.Contains(cell)) + return 0; + + var content = Content[cell]; + if (content.Type == null) + content = CreateResourceCell(resourceType, cell); + + if (content.Type != resourceType) + return 0; + + var oldDensity = content.Density; + var density = Math.Min(content.Type.Info.MaxDensity, oldDensity + amount); + Content[cell] = new ResourceLayerContents(content.Type, density); + + CellChanged?.Invoke(cell, content.Type); + + return density - oldDensity; + } + + public int RemoveResource(ResourceType resourceType, CPos cell, int amount = 1) + { + if (!Content.Contains(cell)) + return 0; + + var content = Content[cell]; + if (content.Type == null || content.Type != resourceType) + return 0; + + var oldDensity = content.Density; + var density = Math.Max(0, oldDensity - amount); + + if (density == 0) + { + Content[cell] = ResourceLayerContents.Empty; + world.Map.CustomTerrain[cell] = byte.MaxValue; + --resCells; + + CellChanged?.Invoke(cell, null); + } + else + { + Content[cell] = new ResourceLayerContents(content.Type, density); + CellChanged?.Invoke(cell, content.Type); + } + + return oldDensity - density; + } + + public void ClearResources(CPos cell) + { + if (!Content.Contains(cell)) return; - cell.Density = Math.Min(cell.Type.Info.MaxDensity, cell.Density + n); - Content[p] = cell; + // Don't break other users of CustomTerrain if there are no resources + var content = Content[cell]; + if (content.Type == null) + return; - CellChanged?.Invoke(p, cell.Type); + Content[cell] = ResourceLayerContents.Empty; + world.Map.CustomTerrain[cell] = byte.MaxValue; + --resCells; + + CellChanged?.Invoke(cell, null); } public bool IsFull(CPos cell) @@ -167,47 +228,16 @@ namespace OpenRA.Mods.Common.Traits return cellContents.Density == cellContents.Type.Info.MaxDensity; } - public ResourceType Harvest(CPos cell) - { - var c = Content[cell]; - if (c.Type == null) - return null; - - if (--c.Density < 0) - { - Content[cell] = ResourceLayerContents.Empty; - world.Map.CustomTerrain[cell] = byte.MaxValue; - --resCells; - } - else - Content[cell] = c; - - CellChanged?.Invoke(cell, c.Type); - - return c.Type; - } - - public void Destroy(CPos cell) - { - // Don't break other users of CustomTerrain if there are no resources - var c = Content[cell]; - if (c.Type == null) - return; - - --resCells; - - // Clear cell - Content[cell] = ResourceLayerContents.Empty; - world.Map.CustomTerrain[cell] = byte.MaxValue; - - CellChanged?.Invoke(cell, c.Type); - } - public ResourceType GetResourceType(CPos cell) { return Content[cell].Type; } public int GetResourceDensity(CPos cell) { return Content[cell].Density; } ResourceLayerContents IResourceLayer.GetResource(CPos cell) { return Content[cell]; } + + bool IResourceLayer.CanAddResource(ResourceType resourceType, CPos cell, int amount) { return CanAddResource(resourceType, cell, amount); } + int IResourceLayer.AddResource(ResourceType resourceType, CPos cell, int amount) { return AddResource(resourceType, cell, amount); } + int IResourceLayer.RemoveResource(ResourceType resourceType, CPos cell, int amount) { return RemoveResource(resourceType, cell, amount); } + void IResourceLayer.ClearResources(CPos cell) { ClearResources(cell); } bool IResourceLayer.IsVisible(CPos cell) { return !world.FogObscures(cell); } } } diff --git a/OpenRA.Mods.Common/Warheads/CreateResourceWarhead.cs b/OpenRA.Mods.Common/Warheads/CreateResourceWarhead.cs index b68786c08c..c067f2a654 100644 --- a/OpenRA.Mods.Common/Warheads/CreateResourceWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/CreateResourceWarhead.cs @@ -59,7 +59,7 @@ namespace OpenRA.Mods.Common.Warheads var resLayer = world.WorldActor.Trait(); foreach (var cell in allCells) { - if (!resLayer.CanSpawnResourceAt(resourceType, cell)) + if (!resLayer.CanAddResource(resourceType, cell)) continue; var splash = world.SharedRandom.Next(1, resourceType.Info.MaxDensity - resLayer.GetResourceDensity(cell)); diff --git a/OpenRA.Mods.Common/Warheads/DestroyResourceWarhead.cs b/OpenRA.Mods.Common/Warheads/DestroyResourceWarhead.cs index a2af79a314..bf60f30a64 100644 --- a/OpenRA.Mods.Common/Warheads/DestroyResourceWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/DestroyResourceWarhead.cs @@ -41,7 +41,7 @@ namespace OpenRA.Mods.Common.Warheads // Destroy all resources in the selected tiles foreach (var cell in allCells) - resLayer.Destroy(cell); + resLayer.ClearResources(cell); } } }