Merge branch 'master' of git://github.com/chrisforbes/OpenRA

This commit is contained in:
Caleb Anderson
2010-01-08 00:26:28 -06:00
43 changed files with 827 additions and 323 deletions

View File

@@ -60,10 +60,11 @@
<Compile Include="PackageEntry.cs" />
<Compile Include="Package.cs" />
<Compile Include="Palette.cs" />
<Compile Include="PaletteRemap.cs" />
<Compile Include="PlayerColorRemap.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ShpReader.cs" />
<Compile Include="ShroudPaletteRemap.cs" />
<Compile Include="SingleColorRemap.cs" />
<Compile Include="Terrain.cs" />
<Compile Include="TileReference.cs" />
<Compile Include="TileSet.cs" />

View File

@@ -4,13 +4,12 @@ using System.IO;
namespace OpenRa.FileFormats
{
public class PaletteRemap : IPaletteRemap
public class PlayerColorRemap : IPaletteRemap
{
int offset;
List<Color> remapColors = new List<Color>();
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];
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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<ProvidesRadar>()
&& a.traits.Get<ProvidesRadar>().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<IronCurtain>());
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<IronCurtainOrderGenerator>())
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);
}

View File

@@ -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"); } }

View File

@@ -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));

View File

@@ -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<FlashTarget>().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<Renderable> Render()
{
if (remainingTicks % 2 == 0)
foreach (var r in target.Render())
yield return r.WithPalette(PaletteType.Highlight);
}
}
}

View File

@@ -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<IronCurtainable>();
}
public void Tick()
{
if (a.IsDead || b.GetDamageModifier() > 0)
Game.world.AddFrameEndTask(w => w.Remove(this));
}
public IEnumerable<Renderable> Render()
{
foreach (var r in a.Render())
yield return r.WithPalette(PaletteType.Invuln);
}
}
}

View File

@@ -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<Renderable> Render()

View File

@@ -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<Building>();
anim.PlayRepeating("disabled");
}
public void Tick()
{
if (!b.Disabled || a.IsDead)
Game.world.AddFrameEndTask(w => w.Remove(this));
}
public IEnumerable<Renderable> 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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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 */
}
}

View File

@@ -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"));

View File

@@ -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;

View File

@@ -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; }
}

View File

@@ -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)

View File

@@ -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);

View File

@@ -82,7 +82,10 @@
<Compile Include="Combat.cs" />
<Compile Include="Effects\Corpse.cs" />
<Compile Include="Effects\DelayedAction.cs" />
<Compile Include="Effects\FlashTarget.cs" />
<Compile Include="Effects\InvulnEffect.cs" />
<Compile Include="Effects\MoveFlash.cs" />
<Compile Include="Effects\PowerDownIndicator.cs" />
<Compile Include="Effects\RepairIndicator.cs" />
<Compile Include="Effects\Smoke.cs" />
<Compile Include="Effects\TeslaZap.cs" />
@@ -96,8 +99,10 @@
<Compile Include="GameRules\VoiceInfo.cs" />
<Compile Include="Effects\IEffect.cs" />
<Compile Include="Graphics\Minimap.cs" />
<Compile Include="Orders\ChronoshiftSelfDestinationOrderGenerator.cs" />
<Compile Include="Orders\ChronosphereSelectOrderGenerator.cs" />
<Compile Include="Orders\IOrderSource.cs" />
<Compile Include="Orders\IronCurtainOrderGenerator.cs" />
<Compile Include="Orders\LocalOrderSource.cs" />
<Compile Include="Effects\Missile.cs" />
<Compile Include="Orders\NetworkOrderSource.cs" />
@@ -215,6 +220,8 @@
<Compile Include="Traits\Helicopter.cs" />
<Compile Include="Traits\InvisibleToOthers.cs" />
<Compile Include="Traits\ConstructionYard.cs" />
<Compile Include="Traits\IronCurtain.cs" />
<Compile Include="Traits\IronCurtainable.cs" />
<Compile Include="Traits\MineImmune.cs" />
<Compile Include="Traits\Minelayer.cs" />
<Compile Include="Traits\LimitedAmmo.cs" />
@@ -304,4 +311,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

View File

@@ -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<IMovement>().FirstOrDefault();
return (movement.CanEnterCell(xy)) ? Cursor.Chronoshift : Cursor.MoveBlocked;
}

View File

@@ -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> 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<IMovement>().FirstOrDefault();
return (movement.CanEnterCell(xy)) ? Cursor.Chronoshift : Cursor.MoveBlocked;
}
}
}

View File

@@ -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<IChronoshiftable>().Any()
&& a.traits.WithInterface<Chronoshiftable>().Any()
&& a.Info.Selectable).FirstOrDefault();
var unit = underCursor != null ? underCursor.Info as UnitInfo : null;

View File

@@ -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> Order(int2 xy, MouseInput mi)
{
if (mi.Button == MouseButton.Right)
Game.controller.CancelInputMode();
return OrderInner(xy, mi);
}
IEnumerable<Order> 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<IronCurtainable>()
&& 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<IronCurtain>());
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;
}
}
}

View File

@@ -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))

View File

@@ -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<Unit>();
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<Unit>();
@@ -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<Turreted>() ? self.traits.Get<Turreted>().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;

View File

@@ -26,10 +26,9 @@ namespace OpenRa.Game.Traits
protected override void QueueAttack( Actor self, Order order )
{
var b = self.traits.Get<Building>();
if (b != null && b.InsuffientPower())
if (self.traits.Contains<Building>() && self.traits.Get<Building>().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;

View File

@@ -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<Renderable>
ModifyRender(Actor self, IEnumerable<Renderable> rs)
{
if (!InsuffientPower())
return rs;
List<Renderable> nrs = new List<Renderable>(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)

View File

@@ -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<IMovement>().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));

View File

@@ -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<IMovement>().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<Chronosphere>()).FirstOrDefault();
// Kill cargo
if (Rules.General.ChronoKillCargo && self.traits.Contains<Cargo>())
{
var cargo = self.traits.Get<Cargo>();
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<ChronoshiftPaletteEffect>().DoChronoshift();
// Play chronosphere active anim
var chronosphere = Game.world.Actors.Where(a => a.Owner == order.Subject.Owner && a.traits.Contains<Chronosphere>()).FirstOrDefault();
if (chronosphere != null)
chronosphere.traits.Get<RenderBuilding>().PlayCustomAnim(chronosphere, "active");
}

View File

@@ -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<IMovement>().FirstOrDefault();
var chronosphere = Game.world.Actors.Where(a => a.Owner == order.Subject.Owner && a.traits.Contains<Chronosphere>()).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;
}
}
}

View File

@@ -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) {}
}
}

View File

@@ -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<IronCurtain>()).FirstOrDefault();
if (ironCurtain != null)
ironCurtain.traits.Get<RenderBuilding>().PlayCustomAnim(ironCurtain, "active");
}
}
}
}

View File

@@ -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<TagType> 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<Production>()
&& x.Owner == self.Owner
&& x.traits.Get<Production>().IsPrimary == true
&& (x.Info as BuildingInfo).Produces.Contains(p)))
{
b.traits.Get<Production>().SetPrimaryProducer(b, false);
}
}
isPrimary = true;
Sound.Play("pribldg1.aud");
}
}
}

View File

@@ -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<Production>()
&& producerTypes.Contains(x.Info)
&& x.Owner == self.Owner
&& x.traits.Get<Production>().IsPrimary == true);
foreach (var p in primaryProducers)
{
// Ignore buildings that are disabled
if (p.traits.Contains<Building>() && p.traits.Get<Building>().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 ] );

View File

@@ -19,7 +19,7 @@ namespace OpenRa.Game.Traits
// Check if powered
var b = self.traits.Get<Building>();
if (b != null && b.InsuffientPower())
if (b != null && b.Disabled)
return false;
return true;

View File

@@ -33,6 +33,7 @@ namespace OpenRa.Game.Traits
public Animation Animation;
public Func<float2> OffsetFunc;
public Func<bool> 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 )

View File

@@ -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 )
{

View File

@@ -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<int2> OccupiedCells(); }
interface INotifyAttack { void Attacking(Actor self); }
interface IRenderModifier { IEnumerable<Renderable> ModifyRender(Actor self, IEnumerable<Renderable> 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;

View File

@@ -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

View File

@@ -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

View File

@@ -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]

View File

@@ -74,7 +74,7 @@
<sequence name="die3" start="432" length="8" />
<sequence name="die4" start="440" length="12" />
<sequence name="die5" start="452" length="18" />
<sequence name="die6" start="0" length="14" src="electro" />
<sequence name="die6" start="0" length="14" src="electro" />
</unit>
<unit name="mech">
<sequence name="stand" start="0" length="8" />
@@ -99,6 +99,15 @@
<sequence name="die3" start="209" length="8" />
<sequence name="die4" start="217" length="12" />
<sequence name="die5" start="229" length="18" />
<sequence name="die6" start="0" length="14" src="electro" />
<sequence name="die6" start="0" length="14" src="electro" />
<sequence name="heal" start="56" length="58" />
<sequence name="standup-0" start="114" length="2" />
<sequence name="standup-1" start="116" length="2" />
<sequence name="standup-2" start="118" length="2" />
<sequence name="standup-3" start="120" length="2" />
<sequence name="standup-4" start="122" length="2" />
<sequence name="standup-5" start="124" length="2" />
<sequence name="standup-6" start="126" length="2" />
<sequence name="standup-7" start="128" length="2" />
</unit>
</sequences>

View File

@@ -201,8 +201,10 @@
</unit>
<!-- iron curtain -->
<unit name="iron">
<sequence name="idle" start="0" length="11" />
<sequence name="damaged-idle" start="11" length="11" />
<sequence name="idle" start="0" length="1" />
<sequence name="active" start="0" length="11" />
<sequence name="damaged-idle" start="11" length="1" />
<sequence name="damaged-active" start="11" length="11" />
<sequence name="make" start="0" length="*" src="ironmake" />
</unit>
<!-- chronosphere -->
@@ -404,10 +406,10 @@
<sequence name="ability-minimap" start="214" length="8" />
</cursor>
<cursor src="speed">
<sequence name="powerdown" start="3" length="12" />
<sequence name="powerdown" start="3" length="12" />
</cursor>
<unit name="powerdown">
<sequence name="disabled" start="3" length="1" src="speed" />
<sequence name="disabled" start="3" length="1" src="speed" />
</unit>
<unit name="120mm">
<sequence name="idle" start="0" length="1" />
@@ -1024,4 +1026,14 @@
<sequence name="pressed" start="1" length="1" />
<sequence name="disabled" start="2" length="1" />
</unit>
<unit name="tabs">
<sequence name="left-normal" start="0" length="1" />
<sequence name="left-pressed" start="1" length="1" />
</unit>
<unit name="dd-crnr">
<sequence name="top-left" start="0" length="1" />
<sequence name="top-right" start="1" length="1" />
<sequence name="bottom-left" start="2" length="1" />
<sequence name="bottom-right" start="3" length="1" />
</unit>
</sequences>

113
units.ini
View File

@@ -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