diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj
index b427fe0c01..de332af9c3 100644
--- a/OpenRa.Game/OpenRa.Game.csproj
+++ b/OpenRa.Game/OpenRa.Game.csproj
@@ -211,7 +211,6 @@
-
diff --git a/OpenRa.Game/Traits/Crate.cs b/OpenRa.Game/Traits/Crate.cs
index ec91673f9d..fb868f872e 100644
--- a/OpenRa.Game/Traits/Crate.cs
+++ b/OpenRa.Game/Traits/Crate.cs
@@ -6,7 +6,6 @@ using IjwFramework.Types;
/*
* Crates left to implement:
-Armor=10,ARMOR,2.0 ; armor of nearby objects increased (armor multiplier)
Cloak=0,STEALTH2 ; enable cloaking on nearby objects
Darkness=1,EMPULSE ; cloak entire radar map
Explosion=5,NONE,500 ; high explosive baddie (damage per explosion)
diff --git a/OpenRa.Game/Traits/SupportPowers/CrateSpawnPower.cs b/OpenRa.Game/Traits/SupportPowers/CrateSpawnPower.cs
deleted file mode 100644
index c316d042fb..0000000000
--- a/OpenRa.Game/Traits/SupportPowers/CrateSpawnPower.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using OpenRa.Orders;
-
-namespace OpenRa.Traits
-{
- class CrateSpawnPowerInfo : SupportPowerInfo
- {
- public readonly float Duration = 0f;
- public override object Create(Actor self) { return new CrateSpawnPower(self, this); }
- }
-
- class CrateSpawnPower : SupportPower, IResolveOrder
- {
- public CrateSpawnPower(Actor self, CrateSpawnPowerInfo info) : base(self, info) { }
-
- protected override void OnActivate()
- {
- Game.controller.orderGenerator = new SelectTarget();
- }
-
- public void ResolveOrder(Actor self, Order order)
- {
- if (order.OrderString == "SpawnCrate")
- {
- self.World.AddFrameEndTask(
- w => w.CreateActor("crate", order.TargetLocation, self.Owner));
-
- Game.controller.CancelInputMode();
- FinishActivate();
- }
- }
-
- class SelectTarget : IOrderGenerator
- {
- public IEnumerable Order(World world, int2 xy, MouseInput mi)
- {
- if (mi.Button == MouseButton.Right)
- Game.controller.CancelInputMode();
-
- return OrderInner(world, xy, mi);
- }
-
- IEnumerable OrderInner(World world, int2 xy, MouseInput mi)
- {
- if (mi.Button == MouseButton.Left)
- {
- var underCursor = world.FindUnitsAtMouse(mi.Location).FirstOrDefault();
- if (underCursor == null)
- yield return new Order("SpawnCrate", world.LocalPlayer.PlayerActor, xy);
- }
-
- yield break;
- }
-
- public void Tick(World world) { }
-
- public void Render(World world) { }
-
- public string GetCursor(World world, int2 xy, MouseInput mi)
- {
- mi.Button = MouseButton.Left;
- return OrderInner(world, xy, mi).Any()
- ? "ability" : "move-blocked";
- }
- }
- }
-}
diff --git a/OpenRa.Game/Traits/World/CrateSpawner.cs b/OpenRa.Game/Traits/World/CrateSpawner.cs
index 6d9b62690a..b352ace947 100644
--- a/OpenRa.Game/Traits/World/CrateSpawner.cs
+++ b/OpenRa.Game/Traits/World/CrateSpawner.cs
@@ -11,17 +11,49 @@ namespace OpenRa.Traits
public readonly int CrateMaximum = 255; // Maximum number of crates
public readonly int CrateRadius = 3; // Radius of crate effect TODO: This belongs on the crate effect itself
public readonly int CrateRegen = 180; // Average time (seconds) between crate spawn
- public readonly float WaterCrateChance = 0.2f; // Change of generating a water crate instead of a land crate
-
- public object Create(Actor self) { return new CrateSpawner(self); }
+ public readonly float WaterCrateChance = .2f; // Chance of generating a water crate instead of a land crate
+
+ public object Create(Actor self) { return new CrateSpawner(); }
}
- class CrateSpawner
+ // assumption: there is always at least one free water cell, and one free land cell.
+
+ class CrateSpawner : ITick
{
- Actor self;
- public CrateSpawner(Actor self)
+ List crates = new List();
+ int ticks = 0;
+
+ public void Tick(Actor self)
{
- this.self = self;
+ if (--ticks <= 0)
+ {
+ var info = self.Info.Traits.Get();
+ ticks = info.CrateRegen * 25; // todo: randomize
+
+ crates.RemoveAll(c => !c.IsInWorld);
+
+ while (crates.Count < info.CrateMinimum)
+ SpawnCrate(self, info);
+ if (crates.Count < info.CrateMaximum)
+ SpawnCrate(self, info);
+ }
+ }
+
+ void SpawnCrate(Actor self, CrateSpawnerInfo info)
+ {
+ var inWater = Game.SharedRandom.NextDouble() < info.WaterCrateChance;
+ var umt = inWater ? UnitMovementType.Float : UnitMovementType.Wheel;
+
+ for (; ; )
+ {
+ var p = new int2(Game.SharedRandom.Next(128), Game.SharedRandom.Next(128));
+ if (self.World.IsCellBuildable(p, umt))
+ {
+ self.World.AddFrameEndTask(
+ w => crates.Add(w.CreateActor("crate", p, self.Owner)));
+ break;
+ }
+ }
}
}
}
diff --git a/mods/ra/rules.yaml b/mods/ra/rules.yaml
index 01e818f7cf..e94257b5fc 100644
--- a/mods/ra/rules.yaml
+++ b/mods/ra/rules.yaml
@@ -57,13 +57,6 @@ Player:
TechLevel: 5
GivenAuto: no
OneShot: yes
- CrateSpawnPower:
- Image: 4tnkicon
- ChargeTime: 0.001
- Description: Spawn Crate
- LongDesc: Spawns a crate at a target location
- Prerequisites: POWR
- TechLevel: 12
World:
WaterPaletteRotation: