diff --git a/OpenRA.Mods.RA/Crate.cs b/OpenRA.Mods.RA/Crate.cs index 444dbdc146..ef1e3d2cbd 100644 --- a/OpenRA.Mods.RA/Crate.cs +++ b/OpenRA.Mods.RA/Crate.cs @@ -131,6 +131,10 @@ namespace OpenRA.Mods.RA self.World.ActorMap.AddInfluence(self, this); self.World.ActorMap.AddPosition(self, this); self.World.ScreenMap.Add(self); + + var cs = self.World.WorldActor.TraitOrDefault(); + if (cs != null) + cs.IncrementCrates(); } public void RemovedFromWorld(Actor self) @@ -138,6 +142,10 @@ namespace OpenRA.Mods.RA self.World.ActorMap.RemoveInfluence(self, this); self.World.ActorMap.RemovePosition(self, this); self.World.ScreenMap.Remove(self); + + var cs = self.World.WorldActor.TraitOrDefault(); + if (cs != null) + cs.DecrementCrates(); } } } diff --git a/OpenRA.Mods.RA/CrateDrop.cs b/OpenRA.Mods.RA/CrateDrop.cs deleted file mode 100644 index 8540d1b887..0000000000 --- a/OpenRA.Mods.RA/CrateDrop.cs +++ /dev/null @@ -1,117 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2011 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.Mods.RA.Air; -using OpenRA.Mods.RA.Buildings; -using OpenRA.Traits; - -namespace OpenRA.Mods.RA -{ - public class CrateDropInfo : ITraitInfo - { - [Desc("Minimum number of crates")] - public readonly int Minimum = 1; - [Desc("Maximum number of crates")] - public readonly int Maximum = 255; - [Desc("Which terrain types can we drop on?")] - public readonly string[] ValidGround = {"Clear", "Rough", "Road", "Ore", "Beach"}; - [Desc("Which terrain types count as water?")] - public readonly string[] ValidWater = {"Water"}; - [Desc("Average time (seconds) between crate spawn")] - public readonly int SpawnInterval = 180; - [Desc("Chance of generating a water crate instead of a land crate")] - public readonly float WaterChance = .2f; - [ActorReference] - public readonly string CrateActor = "crate"; - [ActorReference] - public readonly string DeliveryAircraft = "badr"; - - public object Create (ActorInitializer init) { return new CrateDrop(this); } - } - - public class CrateDrop : ITick - { - List crates = new List(); - int ticks = 0; - CrateDropInfo Info; - - public CrateDrop(CrateDropInfo info) { Info = info; } - - public void Tick(Actor self) - { - if (!self.World.LobbyInfo.GlobalSettings.Crates) return; - - if (--ticks <= 0) - { - ticks = Info.SpawnInterval * 25; // TODO: randomize - - crates.RemoveAll(x => !x.IsInWorld); // BUG: this removes crates that are cargo of a BADR! - - var toSpawn = Math.Max(0, Info.Minimum - crates.Count) - + (crates.Count < Info.Maximum ? 1 : 0); - - for (var n = 0; n < toSpawn; n++) - SpawnCrate(self); - } - } - - CPos? ChooseDropCell(Actor self, bool inWater, int maxTries) - { - for( var n = 0; n < maxTries; n++ ) - { - var p = self.World.ChooseRandomCell(self.World.SharedRandom); - - // Is this valid terrain? - var terrainType = self.World.GetTerrainType(p); - if (!(inWater ? Info.ValidWater : Info.ValidGround).Contains(terrainType)) continue; - - // Don't drop on any actors - if (self.World.WorldActor.Trait().GetBuildingAt(p) != null) continue; - if (self.World.ActorMap.GetUnitsAt(p).Any()) continue; - - return p; - } - - return null; - } - - void SpawnCrate(Actor self) - { - var inWater = self.World.SharedRandom.NextFloat() < Info.WaterChance; - var pp = ChooseDropCell(self, inWater, 100); - if (pp == null) return; - - var p = pp.Value; - self.World.AddFrameEndTask(w => - { - var crate = w.CreateActor(false, Info.CrateActor, new TypeDictionary { new OwnerInit(w.WorldActor.Owner) }); - crates.Add(crate); - - var startPos = w.ChooseRandomEdgeCell(); - var altitude = Rules.Info[Info.DeliveryAircraft].Traits.Get().CruiseAltitude; - var plane = w.CreateActor(Info.DeliveryAircraft, new TypeDictionary - { - new CenterPositionInit(startPos.CenterPosition + new WVec(WRange.Zero, WRange.Zero, altitude)), - new OwnerInit(w.WorldActor.Owner), - new FacingInit(Util.GetFacing(p - startPos, 0)) - }); - - plane.CancelActivity(); - plane.QueueActivity(new FlyAttack(Target.FromCell(p))); - plane.Trait().SetLZ(p); - plane.Trait().Load(plane, crate); - }); - } - } -} diff --git a/OpenRA.Mods.RA/CrateSpawner.cs b/OpenRA.Mods.RA/CrateSpawner.cs index 137b84f6b5..aafa8371dc 100644 --- a/OpenRA.Mods.RA/CrateSpawner.cs +++ b/OpenRA.Mods.RA/CrateSpawner.cs @@ -12,76 +12,150 @@ using System; using System.Collections.Generic; using System.Linq; using OpenRA.FileFormats; +using OpenRA.Mods.RA.Air; using OpenRA.Mods.RA.Buildings; using OpenRA.Traits; namespace OpenRA.Mods.RA { - public class CrateSpawnerInfo : TraitInfo + public class CrateSpawnerInfo : ITraitInfo { [Desc("Minimum number of crates")] public readonly int Minimum = 1; [Desc("Maximum number of crates")] public readonly int Maximum = 255; - [Desc("Which terrain types can we drop on?")] - public readonly string[] ValidGround = {"Clear", "Rough", "Road", "Ore", "Beach"}; - [Desc("Which terrain types count as water?")] - public readonly string[] ValidWater = {"Water"}; [Desc("Average time (seconds) between crate spawn")] public readonly int SpawnInterval = 180; + [Desc("Which terrain types can we drop on?")] + public readonly string[] ValidGround = { "Clear", "Rough", "Road", "Ore", "Beach" }; + [Desc("Which terrain types count as water?")] + public readonly string[] ValidWater = { "Water" }; [Desc("Chance of generating a water crate instead of a land crate")] public readonly float WaterChance = .2f; + [Desc("If a DeliveryAircraft: is specified, then this actor will deliver crates"), ActorReference] + public readonly string DeliveryAircraft = null; + [Desc("Crate actors to drop"), ActorReference] + public readonly string[] CrateActors = { "crate" }; + [Desc("Chance of each crate actor spawning")] + public readonly int[] CrateActorShares = { 10 }; + + public object Create(ActorInitializer init) { return new CrateSpawner(this, init.self); } } public class CrateSpawner : ITick { - List crates = new List(); + int crates = 0; int ticks = 0; + CrateSpawnerInfo info; + Actor self; + + public CrateSpawner(CrateSpawnerInfo info, Actor self) + { + this.info = info; + this.self = self; + } public void Tick(Actor self) { - if (!self.World.LobbyInfo.GlobalSettings.Crates) return; + if (!self.World.LobbyInfo.GlobalSettings.Crates) + return; if (--ticks <= 0) { - var info = self.Info.Traits.Get(); - ticks = info.SpawnInterval * 25; // TODO: randomize + ticks = info.SpawnInterval * 25; - crates.RemoveAll(x => !x.IsInWorld); - - var toSpawn = Math.Max(0, info.Minimum - crates.Count) - + (crates.Count < info.Maximum ? 1 : 0); + var toSpawn = Math.Max(0, info.Minimum - crates) + + (crates < info.Maximum ? 1 : 0); for (var n = 0; n < toSpawn; n++) - SpawnCrate(self, info); + SpawnCrate(self); } } - void SpawnCrate(Actor self, CrateSpawnerInfo info) + void SpawnCrate(Actor self) { var threshold = 100; var inWater = self.World.SharedRandom.NextFloat() < info.WaterChance; + var pp = ChooseDropCell(self, inWater, threshold); - for (var n = 0; n < threshold; n++ ) + if (pp == null) + return; + + var p = pp.Value; + var crateActor = ChooseCrateActor(); + + self.World.AddFrameEndTask(w => + { + if (info.DeliveryAircraft != null) + { + var crate = w.CreateActor(false, crateActor, new TypeDictionary { new OwnerInit(w.WorldActor.Owner) }); + var startPos = w.ChooseRandomEdgeCell(); + var altitude = Rules.Info[info.DeliveryAircraft].Traits.Get().CruiseAltitude; + var plane = w.CreateActor(info.DeliveryAircraft, new TypeDictionary + { + new CenterPositionInit(startPos.CenterPosition + new WVec(WRange.Zero, WRange.Zero, altitude)), + new OwnerInit(w.WorldActor.Owner), + new FacingInit(Util.GetFacing(p - startPos, 0)) + }); + + plane.CancelActivity(); + plane.QueueActivity(new FlyAttack(Target.FromCell(p))); + plane.Trait().SetLZ(p); + plane.Trait().Load(plane, crate); + } + else + { + w.CreateActor(crateActor, new TypeDictionary { new OwnerInit(w.WorldActor.Owner), new LocationInit(p) }); + } + }); + } + + CPos? ChooseDropCell(Actor self, bool inWater, int maxTries) + { + for (var n = 0; n < maxTries; n++) { var p = self.World.ChooseRandomCell(self.World.SharedRandom); // Is this valid terrain? var terrainType = self.World.GetTerrainType(p); - if (!(inWater ? info.ValidWater : info.ValidGround).Contains(terrainType)) continue; + if (!(inWater ? info.ValidWater : info.ValidGround).Contains(terrainType)) + continue; - // Don't spawn on any actors - if (self.World.WorldActor.Trait().GetBuildingAt(p) != null) continue; - if (self.World.ActorMap.GetUnitsAt(p).Any()) continue; + // Don't drop on any actors + if (self.World.WorldActor.Trait().GetBuildingAt(p) != null + || self.World.ActorMap.GetUnitsAt(p).Any()) + continue; - self.World.AddFrameEndTask( - w => crates.Add(w.CreateActor("crate", new TypeDictionary - { - new LocationInit( p ), - new OwnerInit( self.World.WorldActor.Owner ), - }))); - return; + return p; } + + return null; + } + + string ChooseCrateActor() + { + var crateShares = info.CrateActorShares; + var n = self.World.SharedRandom.Next(crateShares.Sum()); + + var cumulativeShares = 0; + for (var i = 0; i < crateShares.Length; i++) + { + cumulativeShares += crateShares[i]; + if (n <= cumulativeShares) + return info.CrateActors[i]; + } + + return null; + } + + public void IncrementCrates() + { + crates++; + } + + public void DecrementCrates() + { + crates--; } } } diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 94c65c9e66..b8cbf51364 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -195,7 +195,6 @@ - diff --git a/mods/ra/maps/Fort-Lonestar/map.yaml b/mods/ra/maps/Fort-Lonestar/map.yaml index cff38bcc79..d8c62c25eb 100644 --- a/mods/ra/maps/Fort-Lonestar/map.yaml +++ b/mods/ra/maps/Fort-Lonestar/map.yaml @@ -482,7 +482,7 @@ Smudges: Rules: World: - CrateDrop: + CrateSpawner: Maximum: 1 SpawnInterval: 100 -SpawnMPUnits: diff --git a/mods/ra/maps/Survival01/map.yaml b/mods/ra/maps/Survival01/map.yaml index 0e82733edc..cccc87cc08 100644 --- a/mods/ra/maps/Survival01/map.yaml +++ b/mods/ra/maps/Survival01/map.yaml @@ -1192,7 +1192,7 @@ Smudges: Rules: World: - -CrateDrop: + -CrateSpawner: -SpawnMPUnits: -MPStartLocations: Survival01Script: diff --git a/mods/ra/maps/Survival02/map.yaml b/mods/ra/maps/Survival02/map.yaml index 7a5cbf6cde..bc686d4ba8 100644 --- a/mods/ra/maps/Survival02/map.yaml +++ b/mods/ra/maps/Survival02/map.yaml @@ -991,7 +991,7 @@ Smudges: Rules: World: - -CrateDrop: + -CrateSpawner: -SpawnMPUnits: -MPStartLocations: Survival02Script: diff --git a/mods/ra/maps/allies-01-classic/map.yaml b/mods/ra/maps/allies-01-classic/map.yaml index aa803b38a3..be4b5e9d77 100644 --- a/mods/ra/maps/allies-01-classic/map.yaml +++ b/mods/ra/maps/allies-01-classic/map.yaml @@ -575,7 +575,7 @@ Rules: Player: -ConquestVictoryConditions: World: - -CrateDrop: + -CrateSpawner: -SpawnMPUnits: -MPStartLocations: LuaScriptInterface: diff --git a/mods/ra/maps/allies-01/map.yaml b/mods/ra/maps/allies-01/map.yaml index a98bf6a0e0..87b9b7cdc6 100644 --- a/mods/ra/maps/allies-01/map.yaml +++ b/mods/ra/maps/allies-01/map.yaml @@ -359,7 +359,7 @@ Rules: Player: -ConquestVictoryConditions: World: - -CrateDrop: + -CrateSpawner: -SpawnMPUnits: -MPStartLocations: Allies01Script: diff --git a/mods/ra/maps/allies-02-classic/map.yaml b/mods/ra/maps/allies-02-classic/map.yaml index 0dbc5b7a21..bcde5590f4 100644 --- a/mods/ra/maps/allies-02-classic/map.yaml +++ b/mods/ra/maps/allies-02-classic/map.yaml @@ -870,7 +870,7 @@ Rules: Player: -ConquestVictoryConditions: World: - -CrateDrop: + -CrateSpawner: -SpawnMPUnits: -MPStartLocations: LuaScriptInterface: diff --git a/mods/ra/maps/allies-02/map.yaml b/mods/ra/maps/allies-02/map.yaml index 60631ca2f0..e94a0f4adb 100644 --- a/mods/ra/maps/allies-02/map.yaml +++ b/mods/ra/maps/allies-02/map.yaml @@ -1745,7 +1745,7 @@ Rules: Player: -ConquestVictoryConditions: World: - -CrateDrop: + -CrateSpawner: -SpawnMPUnits: -MPStartLocations: Allies02Script: diff --git a/mods/ra/maps/allies-03/map.yaml b/mods/ra/maps/allies-03/map.yaml index 059c781894..ff11e93807 100644 --- a/mods/ra/maps/allies-03/map.yaml +++ b/mods/ra/maps/allies-03/map.yaml @@ -1440,7 +1440,7 @@ Rules: Player: -ConquestVictoryConditions: World: - -CrateDrop: + -CrateSpawner: -SpawnMPUnits: -MPStartLocations: Allies03Script: diff --git a/mods/ra/maps/allies-04/map.yaml b/mods/ra/maps/allies-04/map.yaml index 1d1197688a..251853c2eb 100644 --- a/mods/ra/maps/allies-04/map.yaml +++ b/mods/ra/maps/allies-04/map.yaml @@ -1811,7 +1811,7 @@ Rules: Player: -ConquestVictoryConditions: World: - -CrateDrop: + -CrateSpawner: -SpawnMPUnits: -MPStartLocations: Allies04Script: diff --git a/mods/ra/maps/bomber-john/map.yaml b/mods/ra/maps/bomber-john/map.yaml index a17bee923d..51d1fa22d0 100644 --- a/mods/ra/maps/bomber-john/map.yaml +++ b/mods/ra/maps/bomber-john/map.yaml @@ -769,7 +769,7 @@ Smudges: Rules: World: - -CrateDrop: + -CrateSpawner: -SpawnMPUnits: APWR: Buildable: diff --git a/mods/ra/maps/desert-shellmap/map.yaml b/mods/ra/maps/desert-shellmap/map.yaml index 4f565bea1f..e00b3c7f9c 100644 --- a/mods/ra/maps/desert-shellmap/map.yaml +++ b/mods/ra/maps/desert-shellmap/map.yaml @@ -1319,7 +1319,7 @@ Rules: Player: -ConquestVictoryConditions: World: - -CrateDrop: + -CrateSpawner: -SpawnMPUnits: -MPStartLocations: DesertShellmapScript: diff --git a/mods/ra/maps/drop-zone-battle-of-tikiaki/map.yaml b/mods/ra/maps/drop-zone-battle-of-tikiaki/map.yaml index b302227269..c597f07cac 100644 --- a/mods/ra/maps/drop-zone-battle-of-tikiaki/map.yaml +++ b/mods/ra/maps/drop-zone-battle-of-tikiaki/map.yaml @@ -291,7 +291,7 @@ Smudges: Rules: World: - CrateDrop: + CrateSpawner: Maximum: 3 SpawnInterval: 5 -SpawnMPUnits: diff --git a/mods/ra/maps/drop-zone-w/map.yaml b/mods/ra/maps/drop-zone-w/map.yaml index 43b39c6ffc..29575a7399 100644 --- a/mods/ra/maps/drop-zone-w/map.yaml +++ b/mods/ra/maps/drop-zone-w/map.yaml @@ -204,7 +204,7 @@ Smudges: Rules: World: - CrateDrop: + CrateSpawner: Maximum: 3 SpawnInterval: 5 WaterChance: 1 diff --git a/mods/ra/maps/drop-zone/map.yaml b/mods/ra/maps/drop-zone/map.yaml index f25a7981cd..abfa3d25c6 100644 --- a/mods/ra/maps/drop-zone/map.yaml +++ b/mods/ra/maps/drop-zone/map.yaml @@ -186,7 +186,7 @@ Smudges: Rules: World: - CrateDrop: + CrateSpawner: Maximum: 3 SpawnInterval: 5 -SpawnMPUnits: diff --git a/mods/ra/maps/monster-tank-madness/map.yaml b/mods/ra/maps/monster-tank-madness/map.yaml index 24bd4d3574..cf7e975120 100644 --- a/mods/ra/maps/monster-tank-madness/map.yaml +++ b/mods/ra/maps/monster-tank-madness/map.yaml @@ -2545,7 +2545,7 @@ Rules: Player: -ConquestVictoryConditions: World: - -CrateDrop: + -CrateSpawner: -SpawnMPUnits: -MPStartLocations: MonsterTankMadnessScript: diff --git a/mods/ra/maps/soviet-01-classic/map.yaml b/mods/ra/maps/soviet-01-classic/map.yaml index 468ac21a51..0633bf3777 100644 --- a/mods/ra/maps/soviet-01-classic/map.yaml +++ b/mods/ra/maps/soviet-01-classic/map.yaml @@ -851,7 +851,7 @@ Rules: Player: -ConquestVictoryConditions: World: - -CrateDrop: + -CrateSpawner: -SpawnMPUnits: -MPStartLocations: Soviet01ClassicScript: diff --git a/mods/ra/maps/training-camp/map.yaml b/mods/ra/maps/training-camp/map.yaml index 11d6911f20..50d0fe6715 100644 --- a/mods/ra/maps/training-camp/map.yaml +++ b/mods/ra/maps/training-camp/map.yaml @@ -807,7 +807,7 @@ Smudges: Rules: World: - -CrateDrop: + -CrateSpawner: -SpawnMPUnits: -MPStartLocations: Player: diff --git a/mods/ra/rules/system.yaml b/mods/ra/rules/system.yaml index 36ccda8783..4ca8bad7f9 100644 --- a/mods/ra/rules/system.yaml +++ b/mods/ra/rules/system.yaml @@ -551,7 +551,8 @@ World: ChooseBuildTabOnSelect: BridgeLayer: Bridges: bridge1, bridge2, br1, br2, br3, sbridge1, sbridge2, sbridge3, sbridge4 - CrateDrop: + CrateSpawner: + DeliveryAircraft: badr Minimum: 1 Maximum: 3 SpawnInterval: 120