diff --git a/OpenRa.FileFormats/OpenRa.FileFormats.csproj b/OpenRa.FileFormats/OpenRa.FileFormats.csproj
index 26f9983855..0016cbc237 100644
--- a/OpenRa.FileFormats/OpenRa.FileFormats.csproj
+++ b/OpenRa.FileFormats/OpenRa.FileFormats.csproj
@@ -60,10 +60,11 @@
-
+
+
diff --git a/OpenRa.FileFormats/PaletteRemap.cs b/OpenRa.FileFormats/PlayerColorRemap.cs
similarity index 53%
rename from OpenRa.FileFormats/PaletteRemap.cs
rename to OpenRa.FileFormats/PlayerColorRemap.cs
index 635c0d5653..164b06f4de 100644
--- a/OpenRa.FileFormats/PaletteRemap.cs
+++ b/OpenRa.FileFormats/PlayerColorRemap.cs
@@ -4,13 +4,12 @@ using System.IO;
namespace OpenRa.FileFormats
{
- public class PaletteRemap : IPaletteRemap
+ public class PlayerColorRemap : IPaletteRemap
{
int offset;
List remapColors = new List();
- Color shadowColor;
- public PaletteRemap(Stream s)
+ public PlayerColorRemap(Stream s)
{
using (BinaryReader reader = new BinaryReader(s))
{
@@ -27,22 +26,12 @@ namespace OpenRa.FileFormats
offset = 80;
}
- public PaletteRemap( Color shadowColor )
- {
- this.shadowColor = shadowColor;
- }
-
public Color GetRemappedColor(Color original, int index)
{
- if (remapColors.Count > 0)
- {
- if (index < offset || index >= offset + remapColors.Count)
- return original;
+ if (index < offset || index >= offset + remapColors.Count)
+ return original;
- return remapColors[index - offset];
- }
-
- return original.A > 0 ? shadowColor : original;
+ return remapColors[index - offset];
}
}
}
diff --git a/OpenRa.FileFormats/SingleColorRemap.cs b/OpenRa.FileFormats/SingleColorRemap.cs
new file mode 100644
index 0000000000..149779d96b
--- /dev/null
+++ b/OpenRa.FileFormats/SingleColorRemap.cs
@@ -0,0 +1,18 @@
+using System.Drawing;
+
+namespace OpenRa.FileFormats
+{
+ public class SingleColorRemap : IPaletteRemap
+ {
+ Color c;
+ public SingleColorRemap(Color c)
+ {
+ this.c = c;
+ }
+
+ public Color GetRemappedColor(Color original, int index)
+ {
+ return original.A > 0 ? c : original;
+ }
+ }
+}
diff --git a/OpenRa.Game/Chrome.cs b/OpenRa.Game/Chrome.cs
index dfa218f8c8..6e69caf944 100644
--- a/OpenRa.Game/Chrome.cs
+++ b/OpenRa.Game/Chrome.cs
@@ -27,6 +27,17 @@ namespace OpenRa.Game
readonly Animation repairButton;
readonly Animation sellButton;
readonly Animation pwrdownButton;
+ readonly Animation optionsButton;
+
+ readonly Sprite optionsTop;
+ readonly Sprite optionsBottom;
+ readonly Sprite optionsLeft;
+ readonly Sprite optionsRight;
+ readonly Sprite optionsTopLeft;
+ readonly Sprite optionsTopRight;
+ readonly Sprite optionsBottomLeft;
+ readonly Sprite optionsBottomRight;
+ readonly Sprite optionsBackground;
readonly SpriteRenderer buildPaletteRenderer;
readonly Animation cantBuild;
@@ -41,7 +52,8 @@ namespace OpenRa.Game
readonly int paletteColumns;
readonly int2 paletteOrigin;
-
+ bool hadRadar = false;
+ bool optionsPressed = false;
const int MinRows = 4;
public Chrome(Renderer r)
@@ -82,6 +94,19 @@ namespace OpenRa.Game
pwrdownButton = new Animation("repair");
pwrdownButton.PlayRepeating("normal");
+ optionsButton = new Animation("tabs");
+ optionsButton.PlayRepeating("left-normal");
+
+ optionsLeft = SpriteSheetBuilder.LoadAllSprites("dd-left")[0];
+ optionsRight = SpriteSheetBuilder.LoadAllSprites("dd-right")[0];
+ optionsTop = SpriteSheetBuilder.LoadAllSprites("dd-top")[0];
+ optionsBottom = SpriteSheetBuilder.LoadAllSprites("dd-botm")[0];
+ optionsTopLeft = SpriteSheetBuilder.LoadAllSprites("dd-crnr")[0];
+ optionsTopRight = SpriteSheetBuilder.LoadAllSprites("dd-crnr")[1];
+ optionsBottomLeft = SpriteSheetBuilder.LoadAllSprites("dd-crnr")[2];
+ optionsBottomRight = SpriteSheetBuilder.LoadAllSprites("dd-crnr")[3];
+ optionsBackground = SpriteSheetBuilder.LoadAllSprites("dd-bkgnd")[Game.CosmeticRandom.Next(4)];
+
blank = SheetBuilder.Add(new Size(64, 48), 16);
sprites = groups
@@ -132,7 +157,7 @@ namespace OpenRa.Game
Game.LocalPlayer.PowerDrained,
Game.LocalPlayer.PowerProvided,
Game.LocalPlayer.IsReady ? "Yes" : "No"
- ), new int2(140, 5), Color.White);
+ ), new int2(140, 15), Color.White);
PerfHistory.Render(renderer, Game.worldRenderer.lineRenderer);
@@ -149,6 +174,7 @@ namespace OpenRa.Game
int paletteHeight = DrawBuildPalette(currentTab);
DrawBuildTabs(paletteHeight);
DrawChat();
+ DrawOptionsMenu();
}
void DrawMinimap()
@@ -156,7 +182,11 @@ namespace OpenRa.Game
var hasRadar = Game.world.Actors.Any(a => a.Owner == Game.LocalPlayer
&& a.traits.Contains()
&& a.traits.Get().IsActive());
-
+
+ if (hasRadar != hadRadar)
+ Sound.Play((hasRadar) ? "radaron2.aud" : "radardn1.aud");
+ hadRadar = hasRadar;
+
if (hasRadar)
Game.minimap.Draw(new float2(Game.viewport.Width - 256, 8));
}
@@ -296,6 +326,21 @@ namespace OpenRa.Game
AddButton(chronoshiftRect, isLmb => HandleChronosphereButton());
}
buildPaletteRenderer.DrawSprite(repairButton.Image, chronoshiftDrawPos, PaletteType.Chrome);
+
+ // Iron Curtain
+ Rectangle curtainRect = new Rectangle(6, 14+50, repairButton.Image.bounds.Width, repairButton.Image.bounds.Height);
+ var curtainDrawPos = Game.viewport.Location + new float2(curtainRect.Location);
+
+ var hasCurtain = Game.world.Actors.Any(a => a.Owner == Game.LocalPlayer && a.traits.Contains());
+
+ if (!hasCurtain)
+ repairButton.ReplaceAnim("disabled");
+ else
+ {
+ //repairButton.ReplaceAnim(Game.controller.orderGenerator is RepairOrderGenerator ? "pressed" : "normal");
+ AddButton(curtainRect, isLmb => HandleIronCurtainButton());
+ }
+ buildPaletteRenderer.DrawSprite(repairButton.Image, curtainDrawPos, PaletteType.Chrome);
// Repair
@@ -339,6 +384,52 @@ namespace OpenRa.Game
buildPaletteRenderer.DrawSprite(pwrdownButton.Image, pwrdownDrawPos, PaletteType.Chrome);
}
buildPaletteRenderer.Flush();
+
+ //Options
+ Rectangle optionsRect = new Rectangle(0 + 40,0, optionsButton.Image.bounds.Width,
+ optionsButton.Image.bounds.Height);
+
+ var optionsDrawPos = Game.viewport.Location + new float2(optionsRect.Location);
+
+ optionsButton.ReplaceAnim(optionsPressed ? "left-pressed" : "left-normal");
+
+ AddButton(optionsRect, isLmb => optionsPressed = !optionsPressed);
+ buildPaletteRenderer.DrawSprite(optionsButton.Image, optionsDrawPos, PaletteType.Chrome);
+ buildPaletteRenderer.Flush();
+
+ renderer.DrawText("Options", new int2(80, -2) , Color.White);
+ }
+
+ void DrawOptionsMenu()
+ {
+ if (optionsPressed){
+ var menuDrawPos = Game.viewport.Location + new float2(Game.viewport.Width/2, Game.viewport.Height/2);
+ var width = optionsTop.bounds.Width + optionsTopLeft.bounds.Width + optionsTopRight.bounds.Width;
+ var height = optionsLeft.bounds.Height + optionsTopLeft.bounds.Height + optionsBottomLeft.bounds.Height;
+ var adjust = 8;
+
+ menuDrawPos = menuDrawPos + new float2(-width/2, -height/2);
+
+ var backgroundDrawPos = menuDrawPos + new float2( (width - optionsBackground.bounds.Width)/2, (height - optionsBackground.bounds.Height)/2);
+
+ //draw background
+ buildPaletteRenderer.DrawSprite(optionsBackground, backgroundDrawPos, PaletteType.Chrome);
+
+ //draw borders
+ buildPaletteRenderer.DrawSprite(optionsTopLeft, menuDrawPos, PaletteType.Chrome);
+ buildPaletteRenderer.DrawSprite(optionsLeft, menuDrawPos + new float2(0, optionsTopLeft.bounds.Height), PaletteType.Chrome);
+ buildPaletteRenderer.DrawSprite(optionsBottomLeft, menuDrawPos + new float2(0, optionsTopLeft.bounds.Height + optionsLeft.bounds.Height), PaletteType.Chrome);
+
+ buildPaletteRenderer.DrawSprite(optionsTop, menuDrawPos + new float2(optionsTopLeft.bounds.Width, 0), PaletteType.Chrome);
+ buildPaletteRenderer.DrawSprite(optionsTopRight, menuDrawPos + new float2(optionsTopLeft.bounds.Width + optionsTop.bounds.Width, 0), PaletteType.Chrome);
+
+ buildPaletteRenderer.DrawSprite(optionsBottom, menuDrawPos + new float2(optionsTopLeft.bounds.Width, optionsTopLeft.bounds.Height + optionsLeft.bounds.Height +adjust), PaletteType.Chrome);
+ buildPaletteRenderer.DrawSprite(optionsBottomRight, menuDrawPos + new float2(optionsBottomLeft.bounds.Width + optionsBottom.bounds.Width, optionsTopLeft.bounds.Height + optionsLeft.bounds.Height), PaletteType.Chrome);
+
+ buildPaletteRenderer.DrawSprite(optionsRight, menuDrawPos + new float2(optionsTopLeft.bounds.Width + optionsTop.bounds.Width + adjust + 1, optionsTopRight.bounds.Height), PaletteType.Chrome);
+
+ buildPaletteRenderer.Flush();
+ }
}
void HandleChronosphereButton()
@@ -347,6 +438,12 @@ namespace OpenRa.Game
Sound.Play("slcttgt1.aud");
}
+ void HandleIronCurtainButton()
+ {
+ if (Game.controller.ToggleInputMode())
+ Sound.Play("slcttgt1.aud");
+ }
+
void DrawChat()
{
var chatpos = new int2(400, Game.viewport.Height - 20);
@@ -606,7 +703,10 @@ namespace OpenRa.Game
p += new int2(0, 15);
if (!Rules.TechTree.CanBuild(info, Game.LocalPlayer, buildings))
{
- var prereqs = info.Prerequisite.Select(a => Rules.UnitInfo[a.ToLowerInvariant()].Description);
+ var prereqs = info.Prerequisite
+ .Select(a => Rules.UnitInfo[a.ToLowerInvariant()])
+ .Where( u => u.Owner.Any( o => o == Game.LocalPlayer.Race ) )
+ .Select( a => a.Description );
renderer.DrawText("Requires {0}".F( string.Join( ", ", prereqs.ToArray() ) ), p.ToInt2(),
Color.White);
}
diff --git a/OpenRa.Game/Cursor.cs b/OpenRa.Game/Cursor.cs
index 54ebacc76d..89b5f12d94 100644
--- a/OpenRa.Game/Cursor.cs
+++ b/OpenRa.Game/Cursor.cs
@@ -25,6 +25,7 @@ namespace OpenRa.Game
public static Cursor DeployBlocked { get { return new Cursor("deploy-blocked"); } }
public static Cursor Chronoshift { get { return new Cursor("chrono-target"); } }
public static Cursor ChronoshiftSelect { get { return new Cursor("chrono-select"); } }
+ public static Cursor Ability { get { return new Cursor("ability"); } }
public static Cursor C4 { get { return new Cursor("c4"); } }
public static Cursor Capture { get { return new Cursor("capture"); } }
public static Cursor Heal { get { return new Cursor("heal"); } }
diff --git a/OpenRa.Game/Effects/Bullet.cs b/OpenRa.Game/Effects/Bullet.cs
index f342a2d76d..5104585dd3 100644
--- a/OpenRa.Game/Effects/Bullet.cs
+++ b/OpenRa.Game/Effects/Bullet.cs
@@ -31,6 +31,8 @@ namespace OpenRa.Game.Effects
FiredBy = firedBy;
Src = src;
Dest = dest;
+ SrcAltitude = srcAltitude;
+ DestAltitude = destAltitude;
VisualDest = Dest + new int2(
Game.CosmeticRandom.Next(-10, 10),
Game.CosmeticRandom.Next(-10, 10));
diff --git a/OpenRa.Game/Effects/FlashTarget.cs b/OpenRa.Game/Effects/FlashTarget.cs
new file mode 100644
index 0000000000..21e0fe8c85
--- /dev/null
+++ b/OpenRa.Game/Effects/FlashTarget.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using OpenRa.Game.Traits;
+using OpenRa.Game.Graphics;
+
+namespace OpenRa.Game.Effects
+{
+ class FlashTarget : IEffect
+ {
+ Actor target;
+ int remainingTicks = 4;
+
+ public FlashTarget(Actor target)
+ {
+ this.target = target;
+ foreach (var e in Game.world.Effects.OfType().Where(a => a.target == target).ToArray())
+ Game.world.Remove(e);
+ }
+
+ public void Tick()
+ {
+ if (--remainingTicks == 0)
+ Game.world.AddFrameEndTask(w => w.Remove(this));
+ }
+
+ public IEnumerable Render()
+ {
+ if (remainingTicks % 2 == 0)
+ foreach (var r in target.Render())
+ yield return r.WithPalette(PaletteType.Highlight);
+ }
+ }
+}
diff --git a/OpenRa.Game/Effects/InvulnEffect.cs b/OpenRa.Game/Effects/InvulnEffect.cs
new file mode 100644
index 0000000000..b7f28a0a49
--- /dev/null
+++ b/OpenRa.Game/Effects/InvulnEffect.cs
@@ -0,0 +1,30 @@
+using System.Collections.Generic;
+using OpenRa.Game.Graphics;
+using OpenRa.Game.Traits;
+
+namespace OpenRa.Game.Effects
+{
+ class InvulnEffect : IEffect
+ {
+ Actor a;
+ IronCurtainable b;
+
+ public InvulnEffect(Actor a)
+ {
+ this.a = a;
+ this.b = a.traits.Get();
+ }
+
+ public void Tick()
+ {
+ if (a.IsDead || b.GetDamageModifier() > 0)
+ Game.world.AddFrameEndTask(w => w.Remove(this));
+ }
+
+ public IEnumerable Render()
+ {
+ foreach (var r in a.Render())
+ yield return r.WithPalette(PaletteType.Invuln);
+ }
+ }
+}
diff --git a/OpenRa.Game/Effects/Missile.cs b/OpenRa.Game/Effects/Missile.cs
index ee5d2ce88a..c0247567d1 100644
--- a/OpenRa.Game/Effects/Missile.cs
+++ b/OpenRa.Game/Effects/Missile.cs
@@ -21,7 +21,7 @@ namespace OpenRa.Game.Effects
int Altitude;
public Missile(string weapon, Player owner, Actor firedBy,
- int2 src, Actor target, int altitude)
+ int2 src, Actor target, int altitude, int facing)
{
Weapon = Rules.WeaponInfo[weapon];
Projectile = Rules.ProjectileInfo[Weapon.Projectile];
@@ -31,9 +31,7 @@ namespace OpenRa.Game.Effects
Target = target;
Pos = src.ToFloat2();
Altitude = altitude;
-
- /* todo: initial facing should be turret facing, or unit facing if we're not turreted */
- Facing = Traits.Util.GetFacing( Target.CenterLocation - src.ToFloat2(), 0 );
+ Facing = facing;
if (Projectile.Image != null && Projectile.Image != "none")
{
@@ -47,7 +45,7 @@ namespace OpenRa.Game.Effects
}
const int MissileCloseEnough = 7;
- const float Scale = .3f;
+ const float Scale = .2f;
public void Tick()
{
@@ -73,14 +71,16 @@ namespace OpenRa.Game.Effects
return;
}
- var move = (Scale * Weapon.Speed / dist.Length) * dist;
+ var speed = Scale * Weapon.Speed * ((targetAltitude > 0 && Weapon.TurboBoost) ? 1.5f : 1f);
+
+ var angle = Facing / 128f * Math.PI;
+ var move = speed * -float2.FromAngle((float)angle);
Pos += move;
if (Projectile.Animates)
Game.world.AddFrameEndTask(w => w.Add(new Smoke((Pos - 1.5f * move - new int2( 0, Altitude )).ToInt2())));
// todo: running out of fuel
- // todo: turbo boost vs aircraft
}
public IEnumerable Render()
diff --git a/OpenRa.Game/Effects/PowerDownIndicator.cs b/OpenRa.Game/Effects/PowerDownIndicator.cs
new file mode 100644
index 0000000000..185366a078
--- /dev/null
+++ b/OpenRa.Game/Effects/PowerDownIndicator.cs
@@ -0,0 +1,36 @@
+using System.Collections.Generic;
+using OpenRa.Game.Graphics;
+using OpenRa.Game.Traits;
+
+namespace OpenRa.Game.Effects
+{
+ class PowerDownIndicator : IEffect
+ {
+ Actor a;
+ Building b;
+ Animation anim = new Animation("powerdown");
+
+ public PowerDownIndicator(Actor a)
+ {
+ this.a = a;
+ this.b = a.traits.Get();
+ anim.PlayRepeating("disabled");
+ }
+
+ public void Tick()
+ {
+ if (!b.Disabled || a.IsDead)
+ Game.world.AddFrameEndTask(w => w.Remove(this));
+ }
+
+ public IEnumerable Render()
+ {
+ foreach (var r in a.Render())
+ yield return r.WithPalette(PaletteType.Disabled);
+
+ if (b.ManuallyDisabled)
+ yield return new Renderable(anim.Image,
+ a.CenterLocation - .5f * anim.Image.size, PaletteType.Chrome);
+ }
+ }
+}
diff --git a/OpenRa.Game/GameRules/AftermathInfo.cs b/OpenRa.Game/GameRules/AftermathInfo.cs
index 96c403d00c..60511c4d5c 100644
--- a/OpenRa.Game/GameRules/AftermathInfo.cs
+++ b/OpenRa.Game/GameRules/AftermathInfo.cs
@@ -3,12 +3,12 @@ namespace OpenRa.Game.GameRules
{
class AftermathInfo
{
- public readonly int MTankDistance;
- public readonly float QuakeUnitDamage;
- public readonly float QuakeBuildingDamage;
- public readonly float QuakeInfantryDamage;
- public readonly int QuakeDelay;
- public readonly int CarrierLaunchDelay;
- public readonly int ChronoTankDuration;
+ public readonly int MTankDistance = 0;
+ public readonly float QuakeUnitDamage = 0f;
+ public readonly float QuakeBuildingDamage = 0f;
+ public readonly float QuakeInfantryDamage = 0f;
+ public readonly int QuakeDelay = 0;
+ public readonly int CarrierLaunchDelay = 0;
+ public readonly int ChronoTankDuration = 0;
}
}
diff --git a/OpenRa.Game/GameRules/GeneralInfo.cs b/OpenRa.Game/GameRules/GeneralInfo.cs
index e351a7be66..cbd26f3d6b 100644
--- a/OpenRa.Game/GameRules/GeneralInfo.cs
+++ b/OpenRa.Game/GameRules/GeneralInfo.cs
@@ -1,126 +1,127 @@
+using System;
namespace OpenRa.Game.GameRules
{
class GeneralInfo
{
/* Crates */
- public readonly int CrateMinimum;
- public readonly int CrateMaximum;
- public readonly float CrateRadius;
- public readonly float CrateRegen;
- public readonly string UnitCrateType; /* =none, if any */
- public readonly float WaterCrateChance;
+ public readonly int CrateMinimum = 0;
+ public readonly int CrateMaximum = 0;
+ public readonly float CrateRadius = 0;
+ public readonly float CrateRegen = 0;
+ public readonly string UnitCrateType = null; /* =none, if any */
+ public readonly float WaterCrateChance = 0;
- public readonly int SoloCrateMoney;
- public readonly string SilverCrate; /* solo play crate contents */
- public readonly string WaterCrate;
- public readonly string WoodCrate;
+ public readonly int SoloCrateMoney = 2000;
+ public readonly string SilverCrate = null; /* solo play crate contents */
+ public readonly string WaterCrate = null;
+ public readonly string WoodCrate = null;
/* Special Weapons */
- public readonly int ChronoDuration;
- public readonly bool ChronoKillCargo;
- public readonly int ChronoTechLevel;
- public readonly int GPSTechLevel;
- public readonly int GapRadius;
- public readonly float GapRegenInterval;
- public readonly float IronCurtain; /* minutes */
- public readonly int ParaTech;
- public readonly int ParabombTech;
- public readonly int RadarJamRadius;
- public readonly int SpyPlaneTech;
- public readonly int BadgerBombCount;
+ public readonly int ChronoDuration = 0;
+ public readonly bool ChronoKillCargo = true;
+ [Obsolete] public readonly int ChronoTechLevel = -1;
+ [Obsolete] public readonly int GPSTechLevel = -1;
+ public readonly int GapRadius = 0;
+ public readonly float GapRegenInterval =0;
+ public readonly float IronCurtain = 0; /* minutes */
+ [Obsolete] public readonly int ParaTech = -1;
+ [Obsolete] public readonly int ParabombTech = -1;
+ public readonly int RadarJamRadius = 1;
+ [Obsolete] public readonly int SpyPlaneTech = -1;
+ public readonly int BadgerBombCount = 1;
/* Chrono Side Effects */
- public readonly float QuakeChance;
- public readonly float QuakeDamage; /* percent */
- public readonly float VortexChance;
- public readonly int VortexDamage;
- public readonly int VortexRange;
- public readonly int VortexSpeed;
+ public readonly float QuakeChance = 0;
+ public readonly float QuakeDamage = 0; /* percent */
+ public readonly float VortexChance = 0;
+ public readonly int VortexDamage = 0;
+ public readonly int VortexRange = 0;
+ public readonly int VortexSpeed = 0;
/* Repair & Refit */
- public readonly float RefundPercent;
- public readonly float ReloadRate;
- public readonly float RepairPercent;
- public readonly float RepairRate;
- public readonly int RepairStep;
- public readonly float URepairPercent;
- public readonly int URepairStep;
+ public readonly float RefundPercent = 0;
+ public readonly float ReloadRate = 0;
+ public readonly float RepairPercent = 0;
+ public readonly float RepairRate = 0;
+ public readonly int RepairStep = 0;
+ public readonly float URepairPercent = 0;
+ public readonly int URepairStep = 0;
/* Combat & Damage */
- public readonly float TurboBoost;
- public readonly int APMineDamage;
- public readonly int AVMineDamage;
- public readonly int AtomDamage;
- public readonly float BallisticScatter;
- public readonly int BridgeStrength;
- public readonly float C4Delay;
- public readonly float Crush;
- public readonly float ExpSpread;
- public readonly int FireSupress;
- public readonly float HomingScatter;
- public readonly int MaxDamage;
- public readonly int MinDamage;
- public readonly bool OreExplosive;
- public readonly bool PlayerAutoCrush;
- public readonly bool PlayerReturnFire;
- public readonly bool PlayerScatter;
- public readonly float ProneDamage;
- public readonly bool TreeTargeting;
- public readonly int Incoming;
+ public readonly float TurboBoost = 1.5f;
+ public readonly int APMineDamage = 0;
+ public readonly int AVMineDamage = 0;
+ public readonly int AtomDamage = 0;
+ public readonly float BallisticScatter = 0;
+ public readonly int BridgeStrength = 0;
+ public readonly float C4Delay = 0;
+ public readonly float Crush = 0;
+ public readonly float ExpSpread = 0;
+ public readonly int FireSupress = 0;
+ public readonly float HomingScatter = 0;
+ public readonly int MaxDamage = 0;
+ public readonly int MinDamage = 0;
+ public readonly bool OreExplosive = false;
+ public readonly bool PlayerAutoCrush = false;
+ public readonly bool PlayerReturnFire = false;
+ public readonly bool PlayerScatter = false;
+ public readonly float ProneDamage = 0;
+ public readonly bool TreeTargeting = false;
+ public readonly int Incoming = 0;
/* Income & Production */
- public readonly int BailCount;
- public readonly float BuildSpeed;
- public readonly float BuildupTime;
- public readonly int GemValue;
- public readonly int GoldValue;
- public readonly float GrowthRate;
- public readonly bool OreGrows;
- public readonly bool OreSpreads;
- public readonly float OreTruckRate;
- public readonly bool SeparateAircraft;
- public readonly float SurvivorRate;
+ public readonly int BailCount = 0;
+ public readonly float BuildSpeed = 0;
+ public readonly float BuildupTime = 0;
+ public readonly int GemValue = 0;
+ public readonly int GoldValue = 0;
+ public readonly float GrowthRate = 0;
+ public readonly bool OreGrows = true;
+ public readonly bool OreSpreads = true;
+ public readonly float OreTruckRate = 0;
+ public readonly bool SeparateAircraft = true;
+ public readonly float SurvivorRate = 0;
/* Audo/Visual Map Controls */
- public readonly bool AllyReveal;
- public readonly float ConditionRed;
- public readonly float ConditionYellow;
- public readonly int DropZoneRadius;
- public readonly bool EnemyHealth;
- public readonly int Gravity;
- public readonly float IdleActionFrequency;
- public readonly float MessageDelay;
- public readonly float MovieTime;
- public readonly bool NamedCivilians;
- public readonly float SavourDelay;
- public readonly int ShroudRate;
- public readonly int SpeakDelay;
- public readonly int TimerWarning;
- public readonly bool FlashLowPower;
+ public readonly bool AllyReveal = true;
+ public readonly float ConditionRed = 0;
+ public readonly float ConditionYellow = 0;
+ public readonly int DropZoneRadius = 0;
+ public readonly bool EnemyHealth = true;
+ public readonly int Gravity = 0;
+ public readonly float IdleActionFrequency = 0;
+ public readonly float MessageDelay = 0;
+ public readonly float MovieTime = 0;
+ public readonly bool NamedCivilians = false;
+ public readonly float SavourDelay = 0;
+ public readonly int ShroudRate = 0;
+ public readonly int SpeakDelay = 0;
+ public readonly int TimerWarning = 0;
+ public readonly bool FlashLowPower = false;
/* Computer & Movement Controls */
- public readonly bool CurleyShuffle;
- public readonly float BaseBias;
- public readonly float BaseDefenseDelay;
- public readonly float CloseEnough;
- public readonly int DamageDelay;
- public readonly int GameSpeeBias;
- public readonly int LZScanRadius;
- public readonly bool MineAware;
- public readonly float Stray;
- public readonly float SubmergeDelay;
- public readonly float SuspendDelay;
- public readonly int SuspendPriority;
- public readonly float TeamDelay;
+ public readonly bool CurleyShuffle = false;
+ public readonly float BaseBias = 0;
+ public readonly float BaseDefenseDelay = 0;
+ public readonly float CloseEnough = 0;
+ public readonly int DamageDelay = 0;
+ public readonly int GameSpeeBias = 0;
+ public readonly int LZScanRadius = 0;
+ public readonly bool MineAware = false;
+ public readonly float Stray = 0;
+ public readonly float SubmergeDelay = 0;
+ public readonly float SuspendDelay = 0;
+ public readonly int SuspendPriority = 0;
+ public readonly float TeamDelay = 0;
/* Misc */
- public readonly bool FineDiffControl;
- public readonly bool MCVUndeploy;
+ [Obsolete]
+ public readonly bool FineDiffControl = false;
+ public readonly bool MCVUndeploy = false;
/* OpenRA-specific */
- public readonly float OreChance; /* chance of spreading to a
- * particular eligible cell */
- public readonly int LowPowerSlowdown; /* build time multiplier */
+ public readonly float OreChance = 0; /* chance of spreading to a particular eligible cell */
+ public readonly int LowPowerSlowdown = 3; /* build time multiplier */
}
}
diff --git a/OpenRa.Game/GameRules/Rules.cs b/OpenRa.Game/GameRules/Rules.cs
index 70a3acdb5e..3a9c135180 100755
--- a/OpenRa.Game/GameRules/Rules.cs
+++ b/OpenRa.Game/GameRules/Rules.cs
@@ -30,18 +30,18 @@ namespace OpenRa.Game
AllRules = new IniFile(
FileSystem.Open( "session.ini" ),
FileSystem.Open( mapFileName ),
+ FileSystem.Open("aftermathUnits.ini"),
+ FileSystem.Open("units.ini"),
FileSystem.Open( "aftrmath.ini" ),
FileSystem.Open( "rules.ini" ),
- FileSystem.Open( "aftermathUnits.ini" ),
- FileSystem.Open( "units.ini" ),
FileSystem.Open("campaignUnits.ini"),
FileSystem.Open("trees.ini"));
else
AllRules = new IniFile(
FileSystem.Open("session.ini"),
FileSystem.Open(mapFileName),
- FileSystem.Open("rules.ini"),
FileSystem.Open("units.ini"),
+ FileSystem.Open("rules.ini"),
FileSystem.Open("campaignUnits.ini"),
FileSystem.Open("trees.ini"));
diff --git a/OpenRa.Game/GameRules/TechTree.cs b/OpenRa.Game/GameRules/TechTree.cs
index 463934796e..af0fd79ce4 100755
--- a/OpenRa.Game/GameRules/TechTree.cs
+++ b/OpenRa.Game/GameRules/TechTree.cs
@@ -35,8 +35,9 @@ namespace OpenRa.Game.GameRules
return false;
foreach( var p in unit.Prerequisite )
- if( playerBuildings[ p ].Count == 0 )
- return false;
+ if (Rules.UnitInfo[p.ToLowerInvariant()].Owner.Any(x => x == player.Race))
+ if( playerBuildings[ p ].Count == 0 )
+ return false;
if( producesIndex[ Rules.UnitCategory[ unit.Name ] ].All( x => playerBuildings[ x.Name ].Count == 0 ) )
return false;
diff --git a/OpenRa.Game/GameRules/UnitInfo.cs b/OpenRa.Game/GameRules/UnitInfo.cs
index 63acbe1870..6054874fe0 100755
--- a/OpenRa.Game/GameRules/UnitInfo.cs
+++ b/OpenRa.Game/GameRules/UnitInfo.cs
@@ -1,4 +1,5 @@
+using System;
namespace OpenRa.Game.GameRules
{
public enum ArmorType
@@ -19,8 +20,8 @@ namespace OpenRa.Game.GameRules
public readonly int Ammo = -1;
public readonly ArmorType Armor = ArmorType.none;
- public readonly bool DoubleOwned = false;
- public readonly bool Cloakable = false;
+ [Obsolete] public readonly bool DoubleOwned = false;
+ [Obsolete] public readonly bool Cloakable = false;
public readonly int Cost = 0;
public readonly bool Crewed = false;
public readonly bool Explodes = false;
@@ -35,7 +36,7 @@ namespace OpenRa.Game.GameRules
public readonly int ROT = 255;
public readonly int Reload = 0;
public readonly bool SelfHealing = false;
- public readonly bool Sensors = false; // no idea what this does
+ [Obsolete] public readonly bool Sensors = false; // no idea what this does
public readonly int Sight = 1;
public readonly int Strength = 1;
public readonly int TechLevel = -1;
@@ -59,6 +60,10 @@ namespace OpenRa.Game.GameRules
public readonly int UnloadFacing = 0;
public readonly UnitMovementType[] PassengerTypes = null;
+ // weapon origins and firing angles within the turrets. 3 values per position.
+ public readonly int[] PrimaryLocalOffset = { };
+ public readonly int[] SecondaryLocalOffset = { };
+
public UnitInfo(string name) { Name = name; }
}
diff --git a/OpenRa.Game/Graphics/HardwarePalette.cs b/OpenRa.Game/Graphics/HardwarePalette.cs
index 2092291dd0..147a639a34 100644
--- a/OpenRa.Game/Graphics/HardwarePalette.cs
+++ b/OpenRa.Game/Graphics/HardwarePalette.cs
@@ -5,8 +5,8 @@ namespace OpenRa.Game.Graphics
{
public enum PaletteType
{
- Gold, Blue, Red, Orange, Teal, Salmon, Green, Gray,
- Shadow, Invuln, Chrome, Shroud,
+ Gold, Blue, Red, Orange, Teal, Salmon, Green, Gray,
+ Shadow, Invuln, Disabled, Highlight, Shroud, Chrome,
};
class HardwarePalette : Sheet
@@ -21,12 +21,14 @@ namespace OpenRa.Game.Graphics
AddPalette(pal);
foreach (string remap in new string[] { "blue", "red", "orange", "teal", "salmon", "green", "gray" })
- AddPalette(new Palette(pal, new PaletteRemap(FileSystem.Open(remap + ".rem"))));
+ AddPalette(new Palette(pal, new PlayerColorRemap(FileSystem.Open(remap + ".rem"))));
- AddPalette(new Palette(pal, new PaletteRemap(Color.FromArgb(140, 0, 0, 0))));
- AddPalette(pal); // iron curtain. todo: remap!
- AddPalette(pal); // chrome (it's like gold, but we're not going to hax it in palettemods)
- AddPalette(new Palette(pal, new ShroudPaletteRemap()));
+ AddPalette(new Palette(pal, new SingleColorRemap(Color.FromArgb(140, 0, 0, 0)))); // Shadow
+ AddPalette(new Palette(pal, new SingleColorRemap(Color.FromArgb(128, 128, 0, 0)))); // Invulnerable (Iron Curtain)
+ AddPalette(new Palette(pal, new SingleColorRemap(Color.FromArgb(180, 0, 0, 0)))); // Disabled / Low power
+ AddPalette(new Palette(pal, new SingleColorRemap(Color.FromArgb(128, 255, 255, 255)))); // Highlight
+ AddPalette(new Palette(pal, new ShroudPaletteRemap())); // Shroud
+ AddPalette(pal); // Chrome (it's like gold, but we're not going to hax it in palettemods)
}
int AddPalette(Palette p)
diff --git a/OpenRa.Game/Graphics/WorldRenderer.cs b/OpenRa.Game/Graphics/WorldRenderer.cs
index 157b9ee2ab..1060f5467a 100644
--- a/OpenRa.Game/Graphics/WorldRenderer.cs
+++ b/OpenRa.Game/Graphics/WorldRenderer.cs
@@ -242,6 +242,9 @@ namespace OpenRa.Game.Graphics
{
foreach (var tag in tags.GetTags())
{
+ if (tag == TagType.None)
+ continue;
+
var tagImages = new Animation("pips");
tagImages.PlayRepeating(tagStrings[(int)tag]);
spriteRenderer.DrawSprite(tagImages.Image, tagxyBase + tagxyOffset, PaletteType.Chrome);
diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj
index 4aa7ac18d0..80e5bc199f 100644
--- a/OpenRa.Game/OpenRa.Game.csproj
+++ b/OpenRa.Game/OpenRa.Game.csproj
@@ -82,7 +82,10 @@
+
+
+
@@ -96,8 +99,10 @@
+
+
@@ -215,6 +220,8 @@
+
+
@@ -304,4 +311,4 @@
-->
-
+
\ No newline at end of file
diff --git a/OpenRa.Game/Orders/ChronoshiftDestinationOrderGenerator.cs b/OpenRa.Game/Orders/ChronoshiftDestinationOrderGenerator.cs
index e85e248752..a2cd5ef75f 100644
--- a/OpenRa.Game/Orders/ChronoshiftDestinationOrderGenerator.cs
+++ b/OpenRa.Game/Orders/ChronoshiftDestinationOrderGenerator.cs
@@ -35,6 +35,9 @@ namespace OpenRa.Game.Orders
public Cursor GetCursor(int2 xy, MouseInput mi)
{
+ if (!Game.LocalPlayer.Shroud.IsExplored(xy))
+ return Cursor.MoveBlocked;
+
var movement = self.traits.WithInterface().FirstOrDefault();
return (movement.CanEnterCell(xy)) ? Cursor.Chronoshift : Cursor.MoveBlocked;
}
diff --git a/OpenRa.Game/Orders/ChronoshiftSelfDestinationOrderGenerator.cs b/OpenRa.Game/Orders/ChronoshiftSelfDestinationOrderGenerator.cs
new file mode 100644
index 0000000000..5c949c1c51
--- /dev/null
+++ b/OpenRa.Game/Orders/ChronoshiftSelfDestinationOrderGenerator.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Drawing;
+using OpenRa.Game.Traits;
+
+namespace OpenRa.Game.Orders
+{
+ class ChronoshiftSelfDestinationOrderGenerator : IOrderGenerator
+ {
+ public readonly Actor self;
+
+ public ChronoshiftSelfDestinationOrderGenerator(Actor self)
+ {
+ this.self = self;
+ }
+
+ public IEnumerable Order(int2 xy, MouseInput mi)
+ {
+ if (mi.Button == MouseButton.Left)
+ {
+ Game.controller.CancelInputMode();
+ yield break;
+ }
+
+ yield return new Order("ChronoshiftSelf", self, null, xy, null);
+ }
+
+ public void Tick() { }
+ public void Render()
+ {
+ Game.worldRenderer.DrawSelectionBox(self, Color.White, true);
+ }
+
+ public Cursor GetCursor(int2 xy, MouseInput mi)
+ {
+ if (!Game.LocalPlayer.Shroud.IsExplored(xy))
+ return Cursor.MoveBlocked;
+
+ var movement = self.traits.WithInterface().FirstOrDefault();
+ return (movement.CanEnterCell(xy)) ? Cursor.Chronoshift : Cursor.MoveBlocked;
+ }
+ }
+}
diff --git a/OpenRa.Game/Orders/ChronosphereSelectOrderGenerator.cs b/OpenRa.Game/Orders/ChronosphereSelectOrderGenerator.cs
index ae7de0551f..4ff3de312d 100644
--- a/OpenRa.Game/Orders/ChronosphereSelectOrderGenerator.cs
+++ b/OpenRa.Game/Orders/ChronosphereSelectOrderGenerator.cs
@@ -24,7 +24,7 @@ namespace OpenRa.Game.Orders
var loc = mi.Location + Game.viewport.Location;
var underCursor = Game.FindUnits(loc, loc)
.Where(a => a.Owner == Game.LocalPlayer
- && a.traits.WithInterface().Any()
+ && a.traits.WithInterface().Any()
&& a.Info.Selectable).FirstOrDefault();
var unit = underCursor != null ? underCursor.Info as UnitInfo : null;
diff --git a/OpenRa.Game/Orders/IronCurtainOrderGenerator.cs b/OpenRa.Game/Orders/IronCurtainOrderGenerator.cs
new file mode 100644
index 0000000000..d73c787f19
--- /dev/null
+++ b/OpenRa.Game/Orders/IronCurtainOrderGenerator.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using OpenRa.Game.GameRules;
+using OpenRa.Game.Traits;
+
+namespace OpenRa.Game.Orders
+{
+ class IronCurtainOrderGenerator : IOrderGenerator
+ {
+ public IEnumerable Order(int2 xy, MouseInput mi)
+ {
+ if (mi.Button == MouseButton.Right)
+ Game.controller.CancelInputMode();
+
+ return OrderInner(xy, mi);
+ }
+
+ IEnumerable OrderInner(int2 xy, MouseInput mi)
+ {
+ if (mi.Button == MouseButton.Left)
+ {
+ var loc = mi.Location + Game.viewport.Location;
+ var underCursor = Game.FindUnits(loc, loc)
+ .Where(a => a.Owner == Game.LocalPlayer
+ && a.traits.Contains()
+ && a.Info.Selectable).FirstOrDefault();
+
+ var unit = underCursor != null ? underCursor.Info as UnitInfo : null;
+
+ if (unit != null)
+ {
+ yield return new Order("IronCurtain", underCursor, null, int2.Zero, null);
+ }
+ }
+ }
+
+ public void Tick()
+ {
+ var hasStructure = Game.world.Actors
+ .Any(a => a.Owner == Game.LocalPlayer && a.traits.Contains());
+
+ if (!hasStructure)
+ Game.controller.CancelInputMode();
+ }
+
+ public void Render() { }
+
+ public Cursor GetCursor(int2 xy, MouseInput mi)
+ {
+ mi.Button = MouseButton.Left;
+ return OrderInner(xy, mi).Any()
+ ? Cursor.Ability : Cursor.MoveBlocked;
+ }
+ }
+}
diff --git a/OpenRa.Game/Shroud.cs b/OpenRa.Game/Shroud.cs
index 8f64f7365c..56cc9efb59 100644
--- a/OpenRa.Game/Shroud.cs
+++ b/OpenRa.Game/Shroud.cs
@@ -14,6 +14,11 @@ namespace OpenRa.Game
Sprite[,] sprites = new Sprite[128, 128];
bool dirty;
+ public bool IsExplored(int2 xy)
+ {
+ return explored[ xy.X, xy.Y ];
+ }
+
public void Explore(Actor a)
{
foreach (var t in Game.FindTilesInCircle((1f / Game.CellSize * a.CenterLocation).ToInt2(), a.Info.Sight))
diff --git a/OpenRa.Game/Traits/AttackBase.cs b/OpenRa.Game/Traits/AttackBase.cs
index ebb76cbb6d..411770437b 100644
--- a/OpenRa.Game/Traits/AttackBase.cs
+++ b/OpenRa.Game/Traits/AttackBase.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using IjwFramework.Types;
using OpenRa.Game.Effects;
@@ -72,7 +73,7 @@ namespace OpenRa.Game.Traits
var unit = self.traits.GetOrDefault();
if (self.Info.Primary != null && CheckFire(self, unit, self.Info.Primary, ref primaryFireDelay,
- self.Info.PrimaryOffset, ref primaryBurst))
+ self.Info.PrimaryOffset, ref primaryBurst, self.Info.PrimaryLocalOffset))
{
secondaryFireDelay = Math.Max(4, secondaryFireDelay);
primaryRecoil = 1;
@@ -80,7 +81,7 @@ namespace OpenRa.Game.Traits
}
if (self.Info.Secondary != null && CheckFire(self, unit, self.Info.Secondary, ref secondaryFireDelay,
- self.Info.SecondaryOffset ?? self.Info.PrimaryOffset, ref secondaryBurst))
+ self.Info.SecondaryOffset ?? self.Info.PrimaryOffset, ref secondaryBurst, self.Info.SecondaryLocalOffset))
{
if (self.Info.SecondaryOffset != null) secondaryRecoil = 1;
else primaryRecoil = 1;
@@ -88,7 +89,7 @@ namespace OpenRa.Game.Traits
}
}
- bool CheckFire(Actor self, Unit unit, string weaponName, ref int fireDelay, int[] offset, ref int burst)
+ bool CheckFire(Actor self, Unit unit, string weaponName, ref int fireDelay, int[] offset, ref int burst, int[] localOffset)
{
if (fireDelay > 0) return false;
@@ -101,6 +102,17 @@ namespace OpenRa.Game.Traits
if (!Combat.WeaponValidForTarget(weapon, target)) return false;
+ var numOffsets = (localOffset.Length + 2) / 3;
+ if (numOffsets == 0) numOffsets = 1;
+ var localOffsetForShot = burst % numOffsets;
+ var thisLocalOffset = localOffset.Skip(3 * localOffsetForShot).Take(3).ToArray();
+
+ var fireOffset = new[] {
+ offset.ElementAtOrDefault(0) + thisLocalOffset.ElementAtOrDefault(0),
+ offset.ElementAtOrDefault(1) + thisLocalOffset.ElementAtOrDefault(1),
+ offset.ElementAtOrDefault(2),
+ offset.ElementAtOrDefault(3) };
+
if (--burst > 0)
fireDelay = 5;
else
@@ -109,7 +121,7 @@ namespace OpenRa.Game.Traits
burst = weapon.Burst;
}
- var firePos = self.CenterLocation.ToInt2() + Util.GetTurretPosition(self, unit, offset, 0f).ToInt2();
+ var firePos = self.CenterLocation.ToInt2() + Util.GetTurretPosition(self, unit, fireOffset, 0f).ToInt2();
var thisTarget = target; // closure.
var destUnit = thisTarget.traits.GetOrDefault();
@@ -121,9 +133,14 @@ namespace OpenRa.Game.Traits
if( weapon.RenderAsTesla )
Game.world.Add( new TeslaZap( firePos, thisTarget.CenterLocation.ToInt2() ) );
- if( Rules.ProjectileInfo[ weapon.Projectile ].ROT != 0 )
+ if (Rules.ProjectileInfo[weapon.Projectile].ROT != 0)
+ {
+ var fireFacing = thisLocalOffset.ElementAtOrDefault(2) +
+ (self.traits.Contains() ? self.traits.Get().turretFacing : unit.Facing);
+
Game.world.Add(new Missile(weaponName, self.Owner, self,
- firePos, thisTarget, srcAltitude));
+ firePos, thisTarget, srcAltitude, fireFacing));
+ }
else
Game.world.Add(new Bullet(weaponName, self.Owner, self,
firePos, thisTarget.CenterLocation.ToInt2(), srcAltitude, destAltitude));
@@ -155,6 +172,9 @@ namespace OpenRa.Game.Traits
{
self.CancelActivity();
QueueAttack(self, order);
+
+ if (self.Owner == Game.LocalPlayer)
+ Game.world.AddFrameEndTask(w => w.Add(new FlashTarget(order.TargetActor)));
}
else
target = null;
diff --git a/OpenRa.Game/Traits/AttackTurreted.cs b/OpenRa.Game/Traits/AttackTurreted.cs
index 1d0007e223..9c0f513251 100755
--- a/OpenRa.Game/Traits/AttackTurreted.cs
+++ b/OpenRa.Game/Traits/AttackTurreted.cs
@@ -26,10 +26,9 @@ namespace OpenRa.Game.Traits
protected override void QueueAttack( Actor self, Order order )
{
- var b = self.traits.Get();
- if (b != null && b.InsuffientPower())
+ if (self.traits.Contains() && self.traits.Get().Disabled)
return;
-
+
const int RangeTolerance = 1; /* how far inside our maximum range we should try to sit */
/* todo: choose the appropriate weapon, when only one works against this target */
var weapon = order.Subject.Info.Primary ?? order.Subject.Info.Secondary;
@@ -39,6 +38,7 @@ namespace OpenRa.Game.Traits
Math.Max( 0, (int)Rules.WeaponInfo[ weapon ].Range - RangeTolerance ) ) );
target = order.TargetActor;
+
}
bool buildComplete = false;
diff --git a/OpenRa.Game/Traits/Building.cs b/OpenRa.Game/Traits/Building.cs
index dfb870285c..bd48fc1c0d 100644
--- a/OpenRa.Game/Traits/Building.cs
+++ b/OpenRa.Game/Traits/Building.cs
@@ -9,13 +9,16 @@ using OpenRa.Game.Graphics;
namespace OpenRa.Game.Traits
{
- class Building : INotifyDamage, IOrder, ITick, IRenderModifier
+ class Building : INotifyDamage, IOrder, ITick
{
readonly Actor self;
public readonly BuildingInfo unitInfo;
bool isRepairing = false;
- bool isPoweredDown = false;
-
+ bool manuallyDisabled = false;
+ public bool ManuallyDisabled { get { return manuallyDisabled; } }
+ public bool Disabled { get { return (manuallyDisabled || (unitInfo.Powered && self.Owner.GetPowerState() != PowerState.Normal)); } }
+ bool wasDisabled = false;
+
public Building(Actor self)
{
this.self = self;
@@ -24,14 +27,9 @@ namespace OpenRa.Game.Traits
* ((float2)self.Location + .5f * (float2)unitInfo.Dimensions);
}
- public bool InsuffientPower()
- {
- return (isPoweredDown || (unitInfo.Powered && self.Owner.GetPowerState() != PowerState.Normal));
- }
-
public int GetPowerUsage()
{
- if (isPoweredDown)
+ if (manuallyDisabled)
return 0;
if (unitInfo.Power > 0) /* todo: is this how real-ra scales it? */
@@ -40,32 +38,6 @@ namespace OpenRa.Game.Traits
return unitInfo.Power;
}
- public Animation iconAnim;
- public IEnumerable
- ModifyRender(Actor self, IEnumerable rs)
- {
- if (!InsuffientPower())
- return rs;
-
- List nrs = new List(rs);
- foreach(var r in rs)
- {
- // Need 2 shadows to make it dark enough
- nrs.Add(r.WithPalette(PaletteType.Shadow));
- nrs.Add(r.WithPalette(PaletteType.Shadow));
- }
-
- if (isPoweredDown)
- {
- iconAnim = new Animation("powerdown");
- iconAnim.PlayRepeating("disabled");
- nrs.Add(new Renderable(iconAnim.Image, self.CenterLocation - 0.5f*iconAnim.Image.size, PaletteType.Chrome));
- }
-
-
- return nrs;
- }
-
public void Damaged(Actor self, AttackInfo e)
{
if (e.DamageState == DamageState.Dead)
@@ -92,8 +64,8 @@ namespace OpenRa.Game.Traits
if (order.OrderString == "PowerDown")
{
- isPoweredDown = !isPoweredDown;
- Sound.Play((isPoweredDown) ? "bleep12.aud" : "bleep11.aud");
+ manuallyDisabled = !manuallyDisabled;
+ Sound.Play((manuallyDisabled) ? "bleep12.aud" : "bleep11.aud");
}
}
@@ -101,6 +73,11 @@ namespace OpenRa.Game.Traits
public void Tick(Actor self)
{
+ // If the disabled state has changed since the last frame
+ if (Disabled ^ wasDisabled
+ && (wasDisabled = Disabled)) // Yes, I mean assignment
+ Game.world.AddFrameEndTask(w => w.Add(new PowerDownIndicator(self)));
+
if (!isRepairing) return;
if (remainingTicks == 0)
diff --git a/OpenRa.Game/Traits/ChronoshiftDeploy.cs b/OpenRa.Game/Traits/ChronoshiftDeploy.cs
index 7c4252fc97..089ec5ab54 100644
--- a/OpenRa.Game/Traits/ChronoshiftDeploy.cs
+++ b/OpenRa.Game/Traits/ChronoshiftDeploy.cs
@@ -30,13 +30,17 @@ namespace OpenRa.Game.Traits
{
if (order.OrderString == "Deploy")
{
- Game.controller.orderGenerator = new ChronoshiftDestinationOrderGenerator(self);
+ Game.controller.orderGenerator = new ChronoshiftSelfDestinationOrderGenerator(self);
return;
}
var movement = self.traits.WithInterface().FirstOrDefault();
- if (order.OrderString == "Chronoshift" && movement.CanEnterCell(order.TargetLocation))
+ if (order.OrderString == "ChronoshiftSelf" && movement.CanEnterCell(order.TargetLocation))
{
+ // Cannot chronoshift into unexplored location
+ if (!Game.LocalPlayer.Shroud.IsExplored(order.TargetLocation))
+ return;
+
Game.controller.CancelInputMode();
self.CancelActivity();
self.QueueActivity(new Activities.Teleport(order.TargetLocation));
diff --git a/OpenRa.Game/Traits/Chronoshiftable.cs b/OpenRa.Game/Traits/Chronoshiftable.cs
index bf97b15542..7f86dca0be 100644
--- a/OpenRa.Game/Traits/Chronoshiftable.cs
+++ b/OpenRa.Game/Traits/Chronoshiftable.cs
@@ -6,7 +6,7 @@ using System.Drawing;
namespace OpenRa.Game.Traits
{
- class Chronoshiftable : IOrder, ISpeedModifier, ITick, IChronoshiftable
+ class Chronoshiftable : IOrder, ISpeedModifier, ITick
{
// Return-to-sender logic
int2 chronoshiftOrigin;
@@ -46,12 +46,27 @@ namespace OpenRa.Game.Traits
var movement = self.traits.WithInterface().FirstOrDefault();
if (order.OrderString == "Chronoshift" && movement.CanEnterCell(order.TargetLocation))
{
-
+ // Cannot chronoshift into unexplored location
+ if (!Game.LocalPlayer.Shroud.IsExplored(order.TargetLocation))
+ return;
+
// Set up return-to-sender info
chronoshiftOrigin = self.Location;
chronoshiftReturnTicks = (int)(Rules.General.ChronoDuration * 60 * 25);
-
- // TODO: Kill cargo if Rules.General.ChronoKillCargo says so
+
+ var chronosphere = Game.world.Actors.Where(a => a.Owner == order.Subject.Owner && a.traits.Contains()).FirstOrDefault();
+
+ // Kill cargo
+ if (Rules.General.ChronoKillCargo && self.traits.Contains())
+ {
+ var cargo = self.traits.Get();
+ while (!cargo.IsEmpty(self))
+ {
+ if (chronosphere != null)
+ chronosphere.Owner.Kills++;
+ cargo.Unload(self);
+ }
+ }
// Set up the teleport
Game.controller.CancelInputMode();
@@ -63,7 +78,6 @@ namespace OpenRa.Game.Traits
a.traits.Get().DoChronoshift();
// Play chronosphere active anim
- var chronosphere = Game.world.Actors.Where(a => a.Owner == order.Subject.Owner && a.traits.Contains()).FirstOrDefault();
if (chronosphere != null)
chronosphere.traits.Get().PlayCustomAnim(chronosphere, "active");
}
diff --git a/OpenRa.Game/Traits/DemoTruck.cs b/OpenRa.Game/Traits/DemoTruck.cs
index add4849cff..b84bcdec27 100644
--- a/OpenRa.Game/Traits/DemoTruck.cs
+++ b/OpenRa.Game/Traits/DemoTruck.cs
@@ -6,29 +6,27 @@ using OpenRa.Game.Orders;
namespace OpenRa.Game.Traits
{
- class DemoTruck : IOrder, ISpeedModifier, INotifyDamage, IChronoshiftable
+ class DemoTruck : Chronoshiftable, IOrder, INotifyDamage
{
readonly Actor self;
public DemoTruck(Actor self)
+ : base(self)
{
this.self = self;
}
-
- // Fire primary on Chronoshift
- public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
+
+ public new void ResolveOrder(Actor self, Order order)
{
- return null; // Chronoshift order is issued through Chrome.
- }
-
- public void ResolveOrder(Actor self, Order order)
- {
- if (order.OrderString == "ChronosphereSelect")
- Game.controller.orderGenerator = new ChronoshiftDestinationOrderGenerator(self);
-
+ // Override chronoshifting action to detonate vehicle
var movement = self.traits.WithInterface().FirstOrDefault();
var chronosphere = Game.world.Actors.Where(a => a.Owner == order.Subject.Owner && a.traits.Contains()).FirstOrDefault();
if (order.OrderString == "Chronoshift" && movement.CanEnterCell(order.TargetLocation))
+ {
self.InflictDamage(chronosphere, self.Health, Rules.WarheadInfo["Super"]);
+ return;
+ }
+
+ base.ResolveOrder(self, order);
}
// Fire primary on death
@@ -49,11 +47,5 @@ namespace OpenRa.Game.Traits
w => w.Add(new Bullet(self.Info.Primary, detonatedBy.Owner, detonatedBy,
detonateLocation, detonateLocation, altitude, altitude)));
}
-
- public float GetSpeedModifier()
- {
- // ARGH! You must not do this, it will desync!
- return (Game.controller.orderGenerator is ChronoshiftDestinationOrderGenerator) ? 0f : 1f;
- }
}
}
diff --git a/OpenRa.Game/Traits/IronCurtain.cs b/OpenRa.Game/Traits/IronCurtain.cs
new file mode 100644
index 0000000000..668b469bd1
--- /dev/null
+++ b/OpenRa.Game/Traits/IronCurtain.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OpenRa.Game.Traits
+{
+ class IronCurtain
+ {
+ public IronCurtain(Actor self) {}
+ }
+}
diff --git a/OpenRa.Game/Traits/IronCurtainable.cs b/OpenRa.Game/Traits/IronCurtainable.cs
new file mode 100644
index 0000000000..685075273f
--- /dev/null
+++ b/OpenRa.Game/Traits/IronCurtainable.cs
@@ -0,0 +1,47 @@
+using OpenRa.Game.Traits;
+using OpenRa.Game.Orders;
+using System.Collections.Generic;
+using System.Linq;
+using System.Drawing;
+using OpenRa.Game.Effects;
+using OpenRa.Game.Graphics;
+
+namespace OpenRa.Game.Traits
+{
+ class IronCurtainable: IOrder, IDamageModifier, ITick
+ {
+ int RemainingTicks = 0;
+
+ public IronCurtainable(Actor self) { }
+
+ public void Tick(Actor self)
+ {
+ if (RemainingTicks > 0)
+ RemainingTicks--;
+ }
+ public float GetDamageModifier()
+ {
+ return (RemainingTicks > 0) ? 0.0f : 1.0f;
+ }
+
+ public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
+ {
+ return null; // Chronoshift order is issued through Chrome.
+ }
+
+ public void ResolveOrder(Actor self, Order order)
+ {
+ if (order.OrderString == "IronCurtain")
+ {
+ Game.controller.CancelInputMode();
+ Game.world.AddFrameEndTask(w => w.Add(new InvulnEffect(self)));
+ RemainingTicks = (int)(Rules.General.IronCurtain * 60 * 25);
+ Sound.Play("ironcur9.aud");
+ // Play active anim
+ var ironCurtain = Game.world.Actors.Where(a => a.Owner == order.Subject.Owner && a.traits.Contains()).FirstOrDefault();
+ if (ironCurtain != null)
+ ironCurtain.traits.Get().PlayCustomAnim(ironCurtain, "active");
+ }
+ }
+ }
+}
diff --git a/OpenRa.Game/Traits/Production.cs b/OpenRa.Game/Traits/Production.cs
index c4d1413f39..e24eec3a68 100755
--- a/OpenRa.Game/Traits/Production.cs
+++ b/OpenRa.Game/Traits/Production.cs
@@ -4,8 +4,11 @@ using System.Collections.Generic;
namespace OpenRa.Game.Traits
{
- class Production : IProducer, ITags
+ class Production : IOrder, IProducer, ITags
{
+ bool isPrimary = false;
+ public bool IsPrimary { get { return isPrimary; } }
+
public Production( Actor self ) { }
public virtual int2? CreationLocation( Actor self, UnitInfo producee )
@@ -50,7 +53,45 @@ namespace OpenRa.Game.Traits
public IEnumerable GetTags()
{
- yield return (true) ? TagType.Primary : TagType.None;
+ yield return (isPrimary) ? TagType.Primary : TagType.None;
+ }
+
+ public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
+ {
+ if (mi.Button == MouseButton.Right && underCursor == self)
+ return new Order("Deploy", self, null, int2.Zero, null);
+ return null;
+ }
+
+ public void ResolveOrder(Actor self, Order order)
+ {
+ if (order.OrderString == "Deploy")
+ {
+ SetPrimaryProducer(self, !isPrimary);
+ }
+ }
+
+ public void SetPrimaryProducer(Actor self, bool state)
+ {
+ if (state == false)
+ {
+ isPrimary = false;
+ return;
+ }
+
+ // Cancel existing primaries
+ foreach (var p in (self.Info as BuildingInfo).Produces)
+ {
+ foreach (var b in Game.world.Actors.Where(x => x.traits.Contains()
+ && x.Owner == self.Owner
+ && x.traits.Get().IsPrimary == true
+ && (x.Info as BuildingInfo).Produces.Contains(p)))
+ {
+ b.traits.Get().SetPrimaryProducer(b, false);
+ }
+ }
+ isPrimary = true;
+ Sound.Play("pribldg1.aud");
}
}
}
diff --git a/OpenRa.Game/Traits/ProductionQueue.cs b/OpenRa.Game/Traits/ProductionQueue.cs
index 869277d5c8..824229b64d 100755
--- a/OpenRa.Game/Traits/ProductionQueue.cs
+++ b/OpenRa.Game/Traits/ProductionQueue.cs
@@ -128,12 +128,36 @@ namespace OpenRa.Game.Traits
{
var newUnitType = Rules.UnitInfo[ name ];
var producerTypes = Rules.TechTree.UnitBuiltAt( newUnitType );
-
- // TODO: choose producer based on "primary building"
- var producer = Game.world.Actors
- .Where( x => producerTypes.Contains( x.Info ) && x.Owner == self.Owner )
- .FirstOrDefault();
-
+ Actor producer = null;
+
+ // Prioritise primary structure in build order
+ var primaryProducers = Game.world.Actors
+ .Where(x => x.traits.Contains()
+ && producerTypes.Contains(x.Info)
+ && x.Owner == self.Owner
+ && x.traits.Get().IsPrimary == true);
+
+ foreach (var p in primaryProducers)
+ {
+ // Ignore buildings that are disabled
+ if (p.traits.Contains() && p.traits.Get().Disabled)
+ continue;
+ producer = p;
+ break;
+ }
+
+ // TODO: Be smart about disabled buildings. Units in progress should be paused(?)
+ // Ignore this for now
+
+ // Pick the first available producer
+ if (producer == null)
+ {
+ producer = Game.world.Actors
+ .Where( x => producerTypes.Contains( x.Info ) && x.Owner == self.Owner )
+ .FirstOrDefault();
+ }
+
+ // Something went wrong somewhere...
if( producer == null )
{
CancelProduction( Rules.UnitCategory[ name ] );
diff --git a/OpenRa.Game/Traits/ProvidesRadar.cs b/OpenRa.Game/Traits/ProvidesRadar.cs
index 41c3ced78c..7a4ba5e0c6 100644
--- a/OpenRa.Game/Traits/ProvidesRadar.cs
+++ b/OpenRa.Game/Traits/ProvidesRadar.cs
@@ -19,7 +19,7 @@ namespace OpenRa.Game.Traits
// Check if powered
var b = self.traits.Get();
- if (b != null && b.InsuffientPower())
+ if (b != null && b.Disabled)
return false;
return true;
diff --git a/OpenRa.Game/Traits/RenderSimple.cs b/OpenRa.Game/Traits/RenderSimple.cs
index 48f9ec7837..d5db307c09 100644
--- a/OpenRa.Game/Traits/RenderSimple.cs
+++ b/OpenRa.Game/Traits/RenderSimple.cs
@@ -33,6 +33,7 @@ namespace OpenRa.Game.Traits
public Animation Animation;
public Func OffsetFunc;
public Func DisableFunc;
+ public int ZOffset;
public AnimationWithOffset( Animation a )
: this( a, null, null )
@@ -48,10 +49,9 @@ namespace OpenRa.Game.Traits
public Renderable Image( Actor self )
{
- if( OffsetFunc != null )
- return Util.Centered( self, Animation.Image, self.CenterLocation + OffsetFunc() );
- else
- return Util.Centered( self, Animation.Image, self.CenterLocation );
+ var r = Util.Centered( self, Animation.Image, self.CenterLocation
+ + (OffsetFunc != null ? OffsetFunc() : float2.Zero) );
+ return ZOffset != 0 ? r.WithZOffset(ZOffset) : r;
}
public static implicit operator AnimationWithOffset( Animation a )
diff --git a/OpenRa.Game/Traits/RenderUnitTurreted.cs b/OpenRa.Game/Traits/RenderUnitTurreted.cs
index c06614f4bd..f05caa57f4 100644
--- a/OpenRa.Game/Traits/RenderUnitTurreted.cs
+++ b/OpenRa.Game/Traits/RenderUnitTurreted.cs
@@ -19,16 +19,16 @@ namespace OpenRa.Game.Traits
turretAnim.PlayFacing( "turret", () => turreted.turretFacing );
if( self.Info.PrimaryOffset != null )
- anims.Add( "turret_1", new AnimationWithOffset(
+ anims.Add("turret_1", new AnimationWithOffset(
turretAnim,
- () => Util.GetTurretPosition( self, unit, self.Info.PrimaryOffset, attack.primaryRecoil ),
- null ) );
+ () => Util.GetTurretPosition(self, unit, self.Info.PrimaryOffset, attack.primaryRecoil),
+ null) { ZOffset = 1 });
if( self.Info.SecondaryOffset != null )
- anims.Add( "turret_2", new AnimationWithOffset(
+ anims.Add("turret_2", new AnimationWithOffset(
turretAnim,
- () => Util.GetTurretPosition( self, unit, self.Info.SecondaryOffset, attack.secondaryRecoil ),
- null ) );
+ () => Util.GetTurretPosition(self, unit, self.Info.SecondaryOffset, attack.secondaryRecoil),
+ null) { ZOffset = 1 });
if( self.Info.MuzzleFlash )
{
diff --git a/OpenRa.Game/Traits/TraitsInterfaces.cs b/OpenRa.Game/Traits/TraitsInterfaces.cs
index de5bc49c19..5ee0a485ef 100644
--- a/OpenRa.Game/Traits/TraitsInterfaces.cs
+++ b/OpenRa.Game/Traits/TraitsInterfaces.cs
@@ -22,7 +22,11 @@ namespace OpenRa.Game.Traits
Order IssueOrder( Actor self, int2 xy, MouseInput mi, Actor underCursor );
void ResolveOrder( Actor self, Order order );
}
- interface IProducer { bool Produce( Actor self, UnitInfo producee ); }
+ interface IProducer
+ {
+ bool Produce( Actor self, UnitInfo producee );
+ void SetPrimaryProducer(Actor self, bool isPrimary);
+ }
interface IOccupySpace { IEnumerable OccupiedCells(); }
interface INotifyAttack { void Attacking(Actor self); }
interface IRenderModifier { IEnumerable ModifyRender(Actor self, IEnumerable r); }
@@ -43,7 +47,6 @@ namespace OpenRa.Game.Traits
bool IsCrushableBy(UnitMovementType umt, Player player);
bool IsPathableCrush(UnitMovementType umt, Player player);
}
- interface IChronoshiftable{}
struct Renderable
{
public readonly Sprite Sprite;
diff --git a/aftermathUnits.ini b/aftermathUnits.ini
index d352dc379c..9e07413c2c 100755
--- a/aftermathUnits.ini
+++ b/aftermathUnits.ini
@@ -54,15 +54,16 @@ MECH
[SHOK]
Description=Tesla Trooper
-Traits=Unit, Mobile, AttackBase, RenderInfantry, TakeCover
+Traits=Unit, Mobile, AttackBase, RenderInfantry, TakeCover, Passenger
SquadSize=1
Voice=ShokVoice
[MECH]
Description=Mechanic
-Traits=Unit, Mobile, AttackBase, RenderInfantry, TakeCover, SquishByTank
-SquadSize=1
Voice=MechVoice
+Traits=Unit, Mobile, RenderInfantry, AutoHeal, AttackBase, TakeCover, SquishByTank, Passenger
+LongDesc=Heals nearby vehicles.\n Strong vs Nothing\n Weak vs Everything
+SelectionSize=12,17,0,-9
@@ -81,16 +82,13 @@ RenderAsTesla=true
[TTankZap]
RenderAsTesla=true
-[GoodWrench]
-
-
-
-
-
[WarheadTypes]
Mechanical
[Mechanical]
+; Stop the mechanic from `healing' people with his wrench
+Verses=0%,0%,100%,100%,100%
+Spread=1
[VoiceTypes]
ShokVoice
diff --git a/doc/progress.txt b/doc/progress.txt
index cd612fc2cd..1d3b8f14f0 100644
--- a/doc/progress.txt
+++ b/doc/progress.txt
@@ -8,7 +8,7 @@ E3 Works
E4 Works
E6 Works
E7 Works
-MEDI Works
+MEDI Works
SPY Infiltrate action missing
THF Steal action missing
C1,C2,Einstein,Kosygin Not implemented
@@ -17,16 +17,16 @@ All tracked vehicles
1TNK Works
2TNK Works
3TNK Works
-4TNK Gun, missile origins are wrong
+4TNK Works
Light vehicles
V2RL Works
-APC Works
+APC Works
MNLY Works
-MGG No gap
-MRJ No radar
+MGG No gap
+MRJ No radar
JEEP Works
-MCV Works
+MCV Works
HARV Works
ARTY Works
@@ -48,3 +48,12 @@ DD depth charges don't work
PT depth charges don't work
LST Works
+Aftermath Units:
+STNK Stealth effect looks wrong (turret alpha stacking)
+CTNK Missile origins are wrong
+TTNK Works
+DTRK Attack doesn't work; Scorches wrong
+QTNK No weapon
+MSUB Works
+SHOK Works
+MECH Cannot enter transports
\ No newline at end of file
diff --git a/rules.ini b/rules.ini
index 8c029a573e..34338ddc33 100644
--- a/rules.ini
+++ b/rules.ini
@@ -543,10 +543,9 @@ Armor=none
TechLevel=2
Sight=4
Speed=3
-Owner=allies
+Owner=allies,soviet
Cost=300
Points=10
-DoubleOwned=yes
; Flamethrower
[E4]
@@ -602,7 +601,7 @@ Infiltrate=yes
; Tanya
[E7]
-Prerequisite=atek
+Prerequisite=atek,stek
Primary=Colt45
Secondary=Colt45
Strength=100
@@ -615,7 +614,6 @@ Cost=1200
Points=25
Infiltrate=yes
C4=yes
-DoubleOwned=yes
; field medic
[MEDI]
diff --git a/sequences-aftermath.xml b/sequences-aftermath.xml
index 911a79510a..bd25d095f0 100644
--- a/sequences-aftermath.xml
+++ b/sequences-aftermath.xml
@@ -74,7 +74,7 @@
-
+
@@ -99,6 +99,15 @@
-
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sequences.xml b/sequences.xml
index 9e1f0e0ef5..6a463e969c 100644
--- a/sequences.xml
+++ b/sequences.xml
@@ -201,8 +201,10 @@
-
-
+
+
+
+
@@ -404,10 +406,10 @@
-
+
-
+
@@ -1024,4 +1026,14 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/units.ini b/units.ini
index 38418d8b72..5c39efaabf 100644
--- a/units.ini
+++ b/units.ini
@@ -16,47 +16,50 @@ MNLY.AT
[V2RL]
Description=V2 Rocket
-Traits=Unit, Mobile, AttackBase, RenderUnitReload, AutoTarget, Repairable, Chronoshiftable, Passenger
+Traits=Unit, Mobile, AttackBase, RenderUnitReload, AutoTarget, Repairable, Chronoshiftable, Passenger, IronCurtainable
Voice=VehicleVoice
LongDesc=Long-range rocket artillery.\n Strong vs Infantry, Buildings\n Weak vs Tanks, Aircraft
[1TNK]
Description=Light Tank
-Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger
+Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger, IronCurtainable
Recoil=2
Voice=VehicleVoice
LongDesc=Light Tank, good for scouting.\n Strong vs Light Vehicles\n Weak vs Tanks, Aircraft
[2TNK]
Description=Medium Tank
-Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger
+Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger, IronCurtainable
Recoil=3
Voice=VehicleVoice
LongDesc=Allied Main Battle Tank.\n Strong vs Tanks, Light Vehicles\n Weak vs Infantry, Aircraft
[3TNK]
Description=Heavy Tank
-Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger
+Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger, IronCurtainable
Recoil=3
Voice=VehicleVoice
LongDesc=Soviet Main Battle Tank, with dual cannons\n Strong vs Tanks, Light Vehicles\n Weak vs Infantry, Aircraft
[4TNK]
Description=Mammoth Tank
-Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger
+Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger, IronCurtainable
Voice=VehicleVoice
LongDesc=Big and slow tank, with anti-air capability.\n Strong vs Tanks, Aircraft\n Weak vs Infantry
+PrimaryLocalOffset=-4,-5,0,4,-5,0
+SecondaryLocalOffset=-7,2,25,7,2,-25
+Recoil=4
[ARTY]
Description=Artillery
-Traits=Unit, Mobile, AttackBase, RenderUnit, Explodes, AutoTarget, Repairable, Chronoshiftable, Passenger
+Traits=Unit, Mobile, AttackBase, RenderUnit, Explodes, AutoTarget, Repairable, Chronoshiftable, Passenger, IronCurtainable
Voice=VehicleVoice
LongDesc=Long-range artillery.\n Strong vs Infantry, Buildings\n Weak vs Tanks, Aircraft
[JEEP]
Description=Ranger
-Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger
+Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger, IronCurtainable
PrimaryOffset=0,0,0,-2
MuzzleFlash=yes
Voice=VehicleVoice
LongDesc=Fast scout & anti-infantry vehicle.\n Strong vs Infantry\n Weak vs Tanks, Aircraft
[APC]
Description=Armored Personnel Carrier
-Traits=Unit, Mobile, AttackBase, RenderUnitMuzzleFlash, AutoTarget, Repairable, Chronoshiftable, Cargo, Passenger
+Traits=Unit, Mobile, AttackBase, RenderUnitMuzzleFlash, AutoTarget, Repairable, Chronoshiftable, Cargo, Passenger, IronCurtainable
PrimaryOffset=0,0,0,-4
MuzzleFlash=yes
Voice=VehicleVoice
@@ -67,40 +70,40 @@ PassengerTypes=Foot
;; non-combat vehicles
[MRJ]
Description=Radar Jammer
-Traits=Unit, Mobile, RenderUnitSpinner, Repairable, Chronoshiftable, Passenger
+Traits=Unit, Mobile, RenderUnitSpinner, Repairable, Chronoshiftable, Passenger, IronCurtainable
PrimaryOffset=0,4,0,-6
SelectionPriority=3
Voice=VehicleVoice
LongDesc=Hides nearby units on the enemy's minimap.\n Unarmed
[MGG]
Description=Mobile Gap Generator
-Traits=Unit, Mobile, RenderUnitSpinner, Repairable, Chronoshiftable, Passenger
+Traits=Unit, Mobile, RenderUnitSpinner, Repairable, Chronoshiftable, Passenger, IronCurtainable
PrimaryOffset=0,6,0,-3
SelectionPriority=3
Voice=VehicleVoice
LongDesc=Regenerates Fog of War in a small area \naround the unit.\n Unarmed
[HARV]
Description=Ore Truck
-Traits=Harvester, Unit, Mobile, RenderUnit, Repairable, Chronoshiftable, Passenger
+Traits=Harvester, Unit, Mobile, RenderUnit, Repairable, Chronoshiftable, Passenger, IronCurtainable
SelectionPriority=7
Voice=VehicleVoice
LongDesc=Collects Ore and Gems for processing.\n Unarmed
[MCV]
Description=Mobile Construction Vehicle
-Traits=Unit, Mobile, McvDeploy, RenderUnit, Repairable, Chronoshiftable, Passenger
+Traits=Unit, Mobile, McvDeploy, RenderUnit, Repairable, Chronoshiftable, Passenger, IronCurtainable
SelectionPriority=3
Voice=VehicleVoice
LongDesc=Deploys into another Construction Yard.\n Unarmed
[MNLY.AP]
Description=Minelayer (Anti-Personnel)
-Traits=Unit, Mobile, RenderUnit, Minelayer, MineImmune, Repairable, LimitedAmmo, Chronoshiftable, Passenger
+Traits=Unit, Mobile, RenderUnit, Minelayer, MineImmune, Repairable, LimitedAmmo, Chronoshiftable, Passenger, IronCurtainable
Voice=VehicleVoice
LongDesc=Lays mines to destroy unwary enemy units.\n Unarmed
Primary=MINP ;; temporary hack
[MNLY.AT]
Description=Minelayer (Anti-Tank)
-Traits=Unit, Mobile, RenderUnit, Minelayer, MineImmune, Repairable, LimitedAmmo, Chronoshiftable, Passenger
+Traits=Unit, Mobile, RenderUnit, Minelayer, MineImmune, Repairable, LimitedAmmo, Chronoshiftable, Passenger, IronCurtainable
Voice=VehicleVoice
LongDesc=Lays mines to destroy unwary enemy units.\n Unarmed
Primary=MINV ;; temporary hack
@@ -118,21 +121,21 @@ PT
Description=Submarine
WaterBound=yes
BuiltAt=spen
-Traits=Unit, Mobile, RenderUnit, Submarine, AttackBase, Chronoshiftable
+Traits=Unit, Mobile, RenderUnit, Submarine, AttackBase, Chronoshiftable, IronCurtainable
FireDelay=2
LongDesc=Submerged anti-ship unit armed with \ntorpedoes.\n Strong vs Ships\n Weak vs Everything\n Special Ability: Submerge
[DD]
Description=Destroyer
WaterBound=yes
BuiltAt=syrd
-Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Chronoshiftable
+Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Chronoshiftable, IronCurtainable
PrimaryOffset=0,-8,0,-3
LongDesc=Fast multi-role ship. \n Strong vs Submarines, Aircraft\n Weak vs Infantry, Tanks
[CA]
Description=Cruiser
WaterBound=yes
BuiltAt=syrd
-Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Chronoshiftable
+Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Chronoshiftable, IronCurtainable
PrimaryOffset=0,17,0,-2
SecondaryOffset=0,-17,0,-2
LongDesc=Very slow long-range ship. \n Strong vs Buildings\n Weak vs Ships, Submarines
@@ -140,14 +143,14 @@ Recoil=3
[LST]
Description=Transport
WaterBound=yes
-Traits=Unit, Mobile, RenderUnit, Cargo
+Traits=Unit, Mobile, RenderUnit, Cargo, IronCurtainable
LongDesc=General-purpose naval transport.\nCan carry infantry and tanks.\n Unarmed
PassengerTypes=Foot,Wheel,Track
[PT]
Description=Gunboat
WaterBound=yes
BuiltAt=syrd
-Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Chronoshiftable
+Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Chronoshiftable, IronCurtainable
PrimaryOffset=0,-6,0,-1
LongDesc=Light scout & support ship. \n Strong vs Ships, Submarines\n Weak vs Aircraft
@@ -168,13 +171,13 @@ HIND
[MIG]
Description=Mig Attack Plane
BuiltAt=afld
-Traits=Unit, AttackPlane, Plane, RenderUnit, WithShadow, LimitedAmmo
+Traits=Unit, AttackPlane, Plane, RenderUnit, WithShadow, LimitedAmmo, IronCurtainable
InitialFacing=192
LongDesc=Fast Ground-Attack Plane.\n Strong vs Buildings\n Weak vs Infantry, Light Vehicles
[YAK]
Description=Yak Attack Plane
BuiltAt=afld
-Traits=Unit, AttackPlane, Plane, RenderUnit, WithShadow, LimitedAmmo
+Traits=Unit, AttackPlane, Plane, RenderUnit, WithShadow, LimitedAmmo, IronCurtainable
InitialFacing=192
LongDesc=Anti-Tanks & Anti-Infantry Plane.\n Strong vs Infantry, Tanks\n Weak vs Buildings
[TRAN]
@@ -182,14 +185,14 @@ Description=Transport Helicopter
RotorOffset=0,14,0,-4
RotorOffset2=0,-14,0,-2
BuiltAt=hpad
-Traits=Unit, Helicopter, RenderUnitRotor, WithShadow, Cargo
+Traits=Unit, Helicopter, RenderUnitRotor, WithShadow, Cargo, IronCurtainable
InitialFacing=20
LongDesc=Fast Infantry Transport Helicopter.\n Unarmed
PassengerTypes=Foot
[HELI]
Description=Longbow
BuiltAt=hpad
-Traits=Unit, AttackHeli, Helicopter, RenderUnitRotor, WithShadow, LimitedAmmo
+Traits=Unit, AttackHeli, Helicopter, RenderUnitRotor, WithShadow, LimitedAmmo, IronCurtainable
RotorOffset=0,0,0,-2
PrimaryOffset=-5,0,0,2
SecondaryOffset=5,0,0,2
@@ -198,7 +201,7 @@ LongDesc=Helicopter Gunship with AG Missiles.\n Strong vs Buildings, Tanks\n W
[HIND]
Description=Hind
BuiltAt=hpad
-Traits=Unit, AttackHeli, Helicopter, RenderUnitRotor, WithShadow, LimitedAmmo
+Traits=Unit, AttackHeli, Helicopter, RenderUnitRotor, WithShadow, LimitedAmmo, IronCurtainable
PrimaryOffset=-5,0,0,2
SecondaryOffset=5,0,0,2
InitialFacing=20
@@ -226,21 +229,21 @@ MSLO
[PBOX]
Description=Pillbox
-Traits=Building, Turreted, RenderBuilding, AttackTurreted, AutoTarget
+Traits=Building, Turreted, RenderBuilding, AttackTurreted, AutoTarget, IronCurtainable
Dimensions=1,1
Footprint=x
SelectionPriority=3
LongDesc=Basic defensive structure.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks, Aircraft
[HBOX]
Description=Camo Pillbox
-Traits=Building, Turreted, RenderBuilding, AttackTurreted, AutoTarget
+Traits=Building, Turreted, RenderBuilding, AttackTurreted, AutoTarget, IronCurtainable
Dimensions=1,1
Footprint=x
SelectionPriority=3
LongDesc=Hidden defensive structure.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks, Aircraft
[TSLA]
Description=Tesla Coil
-Traits=Building, Turreted, RenderBuildingCharge, AttackTurreted, AutoTarget
+Traits=Building, Turreted, RenderBuildingCharge, AttackTurreted, AutoTarget, IronCurtainable
Dimensions=1,2
Footprint=_ x
SelectionPriority=3
@@ -248,7 +251,7 @@ FireDelay=8
LongDesc=Advanced base defense. Requires power\nto operate.\n Strong vs Tanks, Infantry\n Weak vs Aircraft
[GUN]
Description=Turret
-Traits=Building, Turreted, RenderBuildingTurreted, AttackTurreted, AutoTarget
+Traits=Building, Turreted, RenderBuildingTurreted, AttackTurreted, AutoTarget, IronCurtainable
Dimensions=1,1
Footprint=x
SelectionPriority=3
@@ -256,7 +259,7 @@ InitialFacing=50
LongDesc=Anti-Armor base defense.\n Strong vs Tanks\n Weak vs Infantry, Aircraft
[AGUN]
Description=AA Gun
-Traits=Building, Turreted, RenderBuildingTurreted, AttackTurreted, AutoTarget
+Traits=Building, Turreted, RenderBuildingTurreted, AttackTurreted, AutoTarget, IronCurtainable
Dimensions=1,2
Footprint=_ x
SelectionPriority=3
@@ -264,14 +267,14 @@ InitialFacing=224
LongDesc=Anti-Air base defense.\n Strong vs Aircraft\n Weak vs Infantry, Tanks
[FTUR]
Description=Flame Turret
-Traits=Turreted, Building, RenderBuilding, AttackTurreted, AutoTarget
+Traits=Turreted, Building, RenderBuilding, AttackTurreted, AutoTarget, IronCurtainable
Dimensions=1,1
Footprint=x
SelectionPriority=3
LongDesc=Anti-Infantry base defense.\n Strong vs Infantry\n Weak vs Aircraft
[SAM]
Description=SAM Site
-Traits=Building, Turreted, RenderBuildingTurreted, AttackTurreted, AutoTarget
+Traits=Building, Turreted, RenderBuildingTurreted, AttackTurreted, AutoTarget, IronCurtainable
Dimensions=2,1
Footprint=xx
SelectionPriority=3
@@ -279,28 +282,28 @@ LongDesc=Anti-Air base defense.\n Strong vs Aircraft\n Weak vs Infantry, Tanks
[MSLO]
Description=Missile Silo
-Traits=Building, RenderBuilding
+Traits=Building, RenderBuilding, IronCurtainable
Dimensions=2,1
Footprint=xx
SelectionPriority=3
LongDesc=Launches a devastating nuclear strike.\n Strong vs Infantry, Buildings\n Weak vs Tanks\n Special Ability: Nuclear Missile
[IRON]
Description=Iron Curtain
-Traits=Building, RenderBuilding
+Traits=Building, RenderBuilding, IronCurtainable, IronCurtain
Dimensions=2,2
Footprint=xx xx
SelectionPriority=3
LongDesc=Makes a group of units invulnerable for a \nshort time.\n Special Ability: Invulnerability
[PDOX]
Description=Chronosphere
-Traits=Building, RenderBuilding, Chronosphere
+Traits=Building, RenderBuilding, Chronosphere, IronCurtainable
Dimensions=2,2
Footprint=xx xx
SelectionPriority=3
-LongDesc=Teleports a group of units from one place \nto another, for a limited time.\n Special Ability: Chronoshift
+LongDesc=Teleports a unit from one place \nto another, for a limited time.\n Special Ability: Chronoshift
[GAP]
Description=Gap Generator
-Traits=Building, RenderBuilding
+Traits=Building, RenderBuilding, IronCurtainable
Dimensions=1,2
Footprint=_ x
SelectionPriority=3
@@ -345,14 +348,14 @@ MINV
; `Produces` is a category of objects that this building can produce.
[ATEK]
Description=Allied Tech Center
-Traits=Building, RenderBuilding
+Traits=Building, RenderBuilding, IronCurtainable
Dimensions=2,2
Footprint=xx xx
SelectionPriority=3
LongDesc=Provides Allied advanced technologies.\n Special Ability: GPS Satellite
[WEAP]
Description=War Factory
-Traits=Building, RenderWarFactory, RenderBuilding, RallyPoint, Production
+Traits=Building, RenderWarFactory, RenderBuilding, RallyPoint, Production, IronCurtainable
Dimensions=3,2
Footprint=xxx xxx
Produces=Vehicle
@@ -361,7 +364,7 @@ SelectionPriority=3
LongDesc=Produces tanks & light vehicles.
[SYRD]
Description=Shipyard
-Traits=Building, RenderBuilding, ProductionSurround
+Traits=Building, RenderBuilding, ProductionSurround, IronCurtainable
Dimensions=3,3
Footprint=xxx xxx xxx
Produces=Ship
@@ -369,7 +372,7 @@ SelectionPriority=3
LongDesc=Produces and repairs ships
[SPEN]
Description=Sub Pen
-Traits=Building, RenderBuilding, ProductionSurround
+Traits=Building, RenderBuilding, ProductionSurround, IronCurtainable
Dimensions=3,3
Footprint=xxx xxx xxx
Produces=Ship
@@ -377,7 +380,7 @@ SelectionPriority=3
LongDesc=Produces and repairs submarines and \ntransports
[FACT]
Description=Construction Yard
-Traits=Building, RenderBuilding, ConstructionYard
+Traits=Building, RenderBuilding, ConstructionYard, IronCurtainable
Dimensions=3,3
Footprint=xxx xxx xxx
Produces=Building,Defense
@@ -385,7 +388,7 @@ SelectionPriority=3
LongDesc=Produces other structures
[PROC]
Description=Ore Refinery
-Traits=Building, RenderBuilding, AcceptsOre, StoresOre
+Traits=Building, RenderBuilding, AcceptsOre, StoresOre, IronCurtainable
Dimensions=3,3
Footprint=_x_ xxx x==
SelectionPriority=3
@@ -393,7 +396,7 @@ OrePips=17
LongDesc=Converts Ore and Gems into money
[SILO]
Description=Silo
-Traits=Building, RenderBuildingOre, StoresOre
+Traits=Building, RenderBuildingOre, StoresOre, IronCurtainable
Dimensions=1,1
Footprint=x
SelectionPriority=3
@@ -402,7 +405,7 @@ LongDesc=Stores excess harvested Ore
[HPAD]
Description=Helipad
-Traits=Building, RenderBuilding, Production, BelowUnits, Reservable
+Traits=Building, RenderBuilding, Production, BelowUnits, Reservable, IronCurtainable
Dimensions=2,2
Footprint=xx xx
Produces=Plane
@@ -411,14 +414,14 @@ SpawnOffset=0,-4
LongDesc=Produces and reloads helicopters
[DOME]
Description=Radar Dome
-Traits=Building, RenderBuilding, ProvidesRadar
+Traits=Building, RenderBuilding, ProvidesRadar, IronCurtainable
Dimensions=2,2
Footprint=xx xx
SelectionPriority=3
LongDesc=Provides an overview of the battlefield.\n Requires power to operate.
[AFLD]
Description=Airstrip
-Traits=Building, RenderBuilding, Production, BelowUnits, Reservable
+Traits=Building, RenderBuilding, Production, BelowUnits, Reservable, IronCurtainable
Dimensions=3,2
Footprint=xxx xxx
Produces=Plane
@@ -426,28 +429,28 @@ SelectionPriority=3
LongDesc=Produces and reloads planes\n Special Ability: Paratroopers\n Special Ability: Spy Plane
[POWR]
Description=Power Plant
-Traits=Building, RenderBuilding
+Traits=Building, RenderBuilding, IronCurtainable
Dimensions=2,2
Footprint=xx xx
SelectionPriority=3
LongDesc=Provides power for other structures
[APWR]
Description=Advanced Power Plant
-Traits=Building, RenderBuilding
+Traits=Building, RenderBuilding, IronCurtainable
Dimensions=3,3
Footprint=___ xxx xxx
SelectionPriority=3
LongDesc=Provides more power, cheaper than the \nstandard Power Plant
[STEK]
Description=Soviet Tech Center
-Traits=Building, RenderBuilding
+Traits=Building, RenderBuilding, IronCurtainable
Dimensions=3,2
Footprint=xxx xxx
SelectionPriority=3
LongDesc=Provides Soviet advanced technologies
[BARR]
Description=Soviet Barracks
-Traits=Building, RenderBuilding, RallyPoint, Production
+Traits=Building, RenderBuilding, RallyPoint, Production, IronCurtainable
Dimensions=2,2
Footprint=xx xx
Produces=Infantry
@@ -456,7 +459,7 @@ SelectionPriority=3
LongDesc=Produces infantry
[TENT]
Description=Allied Barracks
-Traits=Building, RenderBuilding, RallyPoint, Production
+Traits=Building, RenderBuilding, RallyPoint, Production, IronCurtainable
Dimensions=2,2
Footprint=xx xx
Produces=Infantry
@@ -465,7 +468,7 @@ SelectionPriority=3
LongDesc=Produces infantry
[KENN]
Description=Kennel
-Traits=Building, RenderBuilding, RallyPoint, Production
+Traits=Building, RenderBuilding, RallyPoint, Production, IronCurtainable
Dimensions=1,1
Footprint=x
RallyPoint=1,2
@@ -473,21 +476,21 @@ SelectionPriority=3
LongDesc=Produces attack dogs
[FIX]
Description=Service Depot
-Traits=Building, RenderBuilding, BelowUnits, Reservable
+Traits=Building, RenderBuilding, BelowUnits, Reservable, IronCurtainable
Dimensions=3,3
Footprint=_x_ xxx _x_
SelectionPriority=3
LongDesc=Repairs vehicles, reloads minelayers, and \nallows the construction of additional bases.
[FACF]
Description=Fake Construction Yard
-Traits=Building, RenderBuilding, Fake
+Traits=Building, RenderBuilding, Fake, IronCurtainable
Dimensions=3,3
Footprint=xxx xxx xxx
SelectionPriority=3
LongDesc=Looks like a Construction Yard.
[WEAF]
Description=Fake War Factory
-Traits=Building, RenderWarFactory, RenderBuilding, Fake
+Traits=Building, RenderWarFactory, RenderBuilding, Fake, IronCurtainable
Dimensions=3,2
Footprint=xxx xxx
SelectionPriority=3