diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index 9028824c85..62aded8acf 100755 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -1,254 +1,254 @@ -#region Copyright & License Information -/* - * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. - * This file is part of OpenRA. - * - * OpenRA is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OpenRA is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with OpenRA. If not, see . - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using OpenRA.FileFormats; -using OpenRA.GameRules; -using OpenRA.Traits; -using OpenRA.Traits.Activities; - -namespace OpenRA -{ - public class Actor - { - [Sync] - public readonly TypeDictionary traits = new TypeDictionary(); - public readonly ActorInfo Info; - - public readonly World World; - public readonly uint ActorID; - - [Sync] - public int2 Location; - [Sync] - public Player Owner; - [Sync] - public int Health; - IActivity currentActivity; - - public Actor(World world, string name, int2 location, Player owner) - { - World = world; - ActorID = world.NextAID(); - Location = location; - CenterLocation = Traits.Util.CenterOfCell(Location); - Owner = owner; - - if (name != null) - { - if (!Rules.Info.ContainsKey(name.ToLowerInvariant())) - throw new NotImplementedException("No rules definition for unit {0}".F(name.ToLowerInvariant())); - - Info = Rules.Info[name.ToLowerInvariant()]; - Health = this.GetMaxHP(); - - foreach (var trait in Info.TraitsInConstructOrder()) - traits.Add(trait.Create(this)); - } - - Size = Lazy.New(() => - { - var si = Info.Traits.GetOrDefault(); - if (si != null && si.Bounds != null) - return new float2(si.Bounds[0], si.Bounds[1]); - - // auto size from render - var firstSprite = traits.WithInterface().SelectMany(x => x.Render(this)).FirstOrDefault(); - if (firstSprite.Sprite == null) return float2.Zero; - return firstSprite.Sprite.size; - }); - } - - public void Tick() - { - var wasIdle = currentActivity is Idle; - while (currentActivity != null) - { - var a = currentActivity; - currentActivity = a.Tick(this) ?? new Idle(); - - if (a == currentActivity) break; - - if (currentActivity is Idle) - { - if (!wasIdle) - foreach (var ni in traits.WithInterface()) - ni.Idle(this); - - break; - } - } - } - - public bool IsIdle - { - get { return currentActivity == null || currentActivity is Idle; } - } - - public float2 CenterLocation; - - Lazy Size; - - public IEnumerable Render() - { - var mods = traits.WithInterface(); - var sprites = traits.WithInterface().SelectMany(x => x.Render(this)); - return mods.Aggregate(sprites, (m, p) => p.ModifyRender(this, m)); - } - - public Order Order( int2 xy, MouseInput mi ) - { - if (Owner != World.LocalPlayer) - return null; - - if (!World.Map.IsInMap(xy.X, xy.Y)) - return null; - - var underCursor = World.FindUnitsAtMouse(mi.Location) - .Where(a => a.Info.Traits.Contains()) - .OrderByDescending(a => a.Info.Traits.Get().Priority) - .FirstOrDefault(); - - return traits.WithInterface() - .Select( x => x.IssueOrder( this, xy, mi, underCursor ) ) - .FirstOrDefault( x => x != null ); - } - - public RectangleF GetBounds(bool useAltitude) - { - var si = Info.Traits.GetOrDefault(); - - var size = Size.Value; - var loc = CenterLocation - 0.5f * size; - - if (si != null && si.Bounds != null && si.Bounds.Length > 2) - loc += new float2(si.Bounds[2], si.Bounds[3]); - - if (useAltitude) - { - var unit = traits.GetOrDefault(); - if (unit != null) loc -= new float2(0, unit.Altitude); - } - - return new RectangleF(loc.X, loc.Y, size.X, size.Y); - } - - public bool IsDead { get { return Health <= 0; } } - public bool IsInWorld { get; set; } - public bool RemoveOnDeath = true; - - public DamageState GetDamageState() - { - if (Health <= 0) - return DamageState.Dead; - - if (Health < this.GetMaxHP() * World.Defaults.ConditionYellow) - return DamageState.Half; - - return DamageState.Normal; - } - - public void InflictDamage(Actor attacker, int damage, WarheadInfo warhead) - { - if (IsDead) return; /* overkill! don't count extra hits as more kills! */ - - var rawDamage = damage; - var oldState = GetDamageState(); - - /* apply the damage modifiers, if we have any. */ - var modifier = (float)traits.WithInterface() - .Select(t => t.GetDamageModifier()).Product(); - - damage = (int)(damage * modifier); - - Health -= damage; - if (Health <= 0) - { - Health = 0; - - attacker.Owner.Kills++; - Owner.Deaths++; - - if (RemoveOnDeath) - World.AddFrameEndTask(w => w.Remove(this)); - } - - var maxHP = this.GetMaxHP(); - - if (Health > maxHP) Health = maxHP; - - Log.Write("InflictDamage: {0} #{1} -> {2} #{3} raw={4} adj={5} hp={6} mod={7}", - attacker.Info.Name, attacker.ActorID, Info.Name, ActorID, rawDamage, damage, Health, modifier); - - var newState = GetDamageState(); - - foreach (var nd in traits.WithInterface()) - nd.Damaged(this, new AttackInfo - { - Attacker = attacker, - Damage = damage, - DamageState = newState, - DamageStateChanged = newState != oldState, - Warhead = warhead - }); - } - - public void QueueActivity( IActivity nextActivity ) - { - if( currentActivity == null ) - { - currentActivity = nextActivity; - return; - } - var act = currentActivity; - while( act.NextActivity != null ) - { - act = act.NextActivity; - } - act.NextActivity = nextActivity; - } - - public void CancelActivity() - { - if( currentActivity != null ) - currentActivity.Cancel( this ); - } - - // For pathdebug, et al - public IActivity GetCurrentActivity() - { - return currentActivity; - } - - public override int GetHashCode() - { - return (int)ActorID; - } - - public override bool Equals( object obj ) - { - var o = obj as Actor; - return ( o != null && o.ActorID == ActorID ); - } - } -} +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.GameRules; +using OpenRA.Traits; +using OpenRA.Traits.Activities; + +namespace OpenRA +{ + public class Actor + { + [Sync] + public readonly TypeDictionary traits = new TypeDictionary(); + public readonly ActorInfo Info; + + public readonly World World; + public readonly uint ActorID; + + [Sync] + public int2 Location; + [Sync] + public Player Owner; + [Sync] + public int Health; + IActivity currentActivity; + + public Actor(World world, string name, int2 location, Player owner) + { + World = world; + ActorID = world.NextAID(); + Location = location; + CenterLocation = Traits.Util.CenterOfCell(Location); + Owner = owner; + + if (name != null) + { + if (!Rules.Info.ContainsKey(name.ToLowerInvariant())) + throw new NotImplementedException("No rules definition for unit {0}".F(name.ToLowerInvariant())); + + Info = Rules.Info[name.ToLowerInvariant()]; + Health = this.GetMaxHP(); + + foreach (var trait in Info.TraitsInConstructOrder()) + traits.Add(trait.Create(this)); + } + + Size = Lazy.New(() => + { + var si = Info.Traits.GetOrDefault(); + if (si != null && si.Bounds != null) + return new float2(si.Bounds[0], si.Bounds[1]); + + // auto size from render + var firstSprite = traits.WithInterface().SelectMany(x => x.Render(this)).FirstOrDefault(); + if (firstSprite.Sprite == null) return float2.Zero; + return firstSprite.Sprite.size; + }); + } + + public void Tick() + { + var wasIdle = currentActivity is Idle; + while (currentActivity != null) + { + var a = currentActivity; + currentActivity = a.Tick(this) ?? new Idle(); + + if (a == currentActivity) break; + + if (currentActivity is Idle) + { + if (!wasIdle) + foreach (var ni in traits.WithInterface()) + ni.Idle(this); + + break; + } + } + } + + public bool IsIdle + { + get { return currentActivity == null || currentActivity is Idle; } + } + + public float2 CenterLocation; + + OpenRA.FileFormats.Lazy Size; + + public IEnumerable Render() + { + var mods = traits.WithInterface(); + var sprites = traits.WithInterface().SelectMany(x => x.Render(this)); + return mods.Aggregate(sprites, (m, p) => p.ModifyRender(this, m)); + } + + public Order Order( int2 xy, MouseInput mi ) + { + if (Owner != World.LocalPlayer) + return null; + + if (!World.Map.IsInMap(xy.X, xy.Y)) + return null; + + var underCursor = World.FindUnitsAtMouse(mi.Location) + .Where(a => a.Info.Traits.Contains()) + .OrderByDescending(a => a.Info.Traits.Get().Priority) + .FirstOrDefault(); + + return traits.WithInterface() + .Select( x => x.IssueOrder( this, xy, mi, underCursor ) ) + .FirstOrDefault( x => x != null ); + } + + public RectangleF GetBounds(bool useAltitude) + { + var si = Info.Traits.GetOrDefault(); + + var size = Size.Value; + var loc = CenterLocation - 0.5f * size; + + if (si != null && si.Bounds != null && si.Bounds.Length > 2) + loc += new float2(si.Bounds[2], si.Bounds[3]); + + if (useAltitude) + { + var unit = traits.GetOrDefault(); + if (unit != null) loc -= new float2(0, unit.Altitude); + } + + return new RectangleF(loc.X, loc.Y, size.X, size.Y); + } + + public bool IsDead { get { return Health <= 0; } } + public bool IsInWorld { get; set; } + public bool RemoveOnDeath = true; + + public DamageState GetDamageState() + { + if (Health <= 0) + return DamageState.Dead; + + if (Health < this.GetMaxHP() * World.Defaults.ConditionYellow) + return DamageState.Half; + + return DamageState.Normal; + } + + public void InflictDamage(Actor attacker, int damage, WarheadInfo warhead) + { + if (IsDead) return; /* overkill! don't count extra hits as more kills! */ + + var rawDamage = damage; + var oldState = GetDamageState(); + + /* apply the damage modifiers, if we have any. */ + var modifier = (float)traits.WithInterface() + .Select(t => t.GetDamageModifier()).Product(); + + damage = (int)(damage * modifier); + + Health -= damage; + if (Health <= 0) + { + Health = 0; + + attacker.Owner.Kills++; + Owner.Deaths++; + + if (RemoveOnDeath) + World.AddFrameEndTask(w => w.Remove(this)); + } + + var maxHP = this.GetMaxHP(); + + if (Health > maxHP) Health = maxHP; + + Log.Write("InflictDamage: {0} #{1} -> {2} #{3} raw={4} adj={5} hp={6} mod={7}", + attacker.Info.Name, attacker.ActorID, Info.Name, ActorID, rawDamage, damage, Health, modifier); + + var newState = GetDamageState(); + + foreach (var nd in traits.WithInterface()) + nd.Damaged(this, new AttackInfo + { + Attacker = attacker, + Damage = damage, + DamageState = newState, + DamageStateChanged = newState != oldState, + Warhead = warhead + }); + } + + public void QueueActivity( IActivity nextActivity ) + { + if( currentActivity == null ) + { + currentActivity = nextActivity; + return; + } + var act = currentActivity; + while( act.NextActivity != null ) + { + act = act.NextActivity; + } + act.NextActivity = nextActivity; + } + + public void CancelActivity() + { + if( currentActivity != null ) + currentActivity.Cancel( this ); + } + + // For pathdebug, et al + public IActivity GetCurrentActivity() + { + return currentActivity; + } + + public override int GetHashCode() + { + return (int)ActorID; + } + + public override bool Equals( object obj ) + { + var o = obj as Actor; + return ( o != null && o.ActorID == ActorID ); + } + } +} diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 3b3e033594..2430f0577e 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -33,6 +33,8 @@ using OpenRA.Network; using OpenRA.Server; using OpenRA.Support; using OpenRA.Traits; +using OpenRA.Widgets; + using Timer = OpenRA.Support.Timer; using XRandom = OpenRA.Thirdparty.Random; @@ -47,7 +49,7 @@ namespace OpenRA public static Controller controller; internal static Chrome chrome; internal static UserSettings Settings; - + internal static OrderManager orderManager; public static bool skipMakeAnims = true; @@ -69,12 +71,12 @@ namespace OpenRA foreach (var dir in manifest.Folders) FileSystem.Mount(dir); foreach (var pkg in manifest.Packages) FileSystem.Mount(pkg); - + Timer.Time("mount temporary packages: {0}"); } - + public static void LoadModAssemblies(Manifest m) - { + { // All the core namespaces var asms = typeof(Game).Assembly.GetNamespaces() .Select(c => Pair.New(typeof(Game).Assembly, c)) @@ -102,9 +104,9 @@ namespace OpenRA throw new InvalidOperationException("Cannot locate type: {0}".F(classname)); } - - public static Dictionary AvailableMaps; - + + public static Dictionary AvailableMaps; + // TODO: Do this nicer static Dictionary FindMaps(string[] mods) { @@ -118,48 +120,48 @@ namespace OpenRA return paths.Select(p => new MapStub(new Folder(p))).ToDictionary(m => m.Uid); } - + static void ChangeMods() { - Timer.Time( "----ChangeMods" ); + Timer.Time("----ChangeMods"); var manifest = new Manifest(LobbyInfo.GlobalSettings.Mods); - Timer.Time( "manifest: {0}" ); + Timer.Time("manifest: {0}"); LoadModAssemblies(manifest); SheetBuilder.Initialize(renderer); LoadModPackages(manifest); - Timer.Time( "load assemblies, packages: {0}" ); + Timer.Time("load assemblies, packages: {0}"); packageChangePending = false; } - + static void LoadMap(string mapName) { - Timer.Time( "----LoadMap" ); + Timer.Time("----LoadMap"); SheetBuilder.Initialize(renderer); var manifest = new Manifest(LobbyInfo.GlobalSettings.Mods); - Timer.Time( "manifest: {0}" ); - + Timer.Time("manifest: {0}"); + if (!Game.AvailableMaps.ContainsKey(mapName)) throw new InvalidDataException("Cannot find map with Uid {0}".F(mapName)); - - var map = new Map( Game.AvailableMaps[mapName].Package ); - + + var map = new Map(Game.AvailableMaps[mapName].Package); + viewport = new Viewport(clientSize, map.TopLeft, map.BottomRight, renderer); world = null; // trying to access the old world will NRE, rather than silently doing it wrong. ChromeProvider.Initialize(manifest.Chrome); - Timer.Time( "viewport, ChromeProvider: {0}" ); - world = new World(manifest,map); - Timer.Time( "world: {0}" ); - + Timer.Time("viewport, ChromeProvider: {0}"); + world = new World(manifest, map); + Timer.Time("world: {0}"); + SequenceProvider.Initialize(manifest.Sequences); - Timer.Time( "ChromeProv, SeqProv: {0}" ); + Timer.Time("ChromeProv, SeqProv: {0}"); chrome = new Chrome(renderer, manifest); - Timer.Time( "chrome: {0}" ); + Timer.Time("chrome: {0}"); - Timer.Time( "----end LoadMap" ); + Timer.Time("----end LoadMap"); Debug("Map change {0} -> {1}".F(Game.mapName, mapName)); } - + public static void MoveViewport(int2 loc) { viewport.Center(loc); @@ -174,21 +176,21 @@ namespace OpenRA CurrentHost = host; CurrentPort = port; - - orderManager = new OrderManager(new NetworkConnection( host, port ), ChooseReplayFilename()); + + orderManager = new OrderManager(new NetworkConnection(host, port), ChooseReplayFilename()); } static string ChooseReplayFilename() { return DateTime.UtcNow.ToString("OpenRA-yyyy-MM-ddThhmmssZ.rep"); } - + static void JoinLocal() { if (orderManager != null) orderManager.Dispose(); orderManager = new OrderManager(new EchoConnection()); } - + static int lastTime = Environment.TickCount; static void ResetTimer() @@ -242,7 +244,7 @@ namespace OpenRA return sb.ToString(); } - internal static void DumpSyncReport( int frame ) + internal static void DumpSyncReport(int frame) { var f = syncReports.FirstOrDefault(a => a.First == frame); if (f == null) @@ -262,11 +264,11 @@ namespace OpenRA // TODO: Only do this on mod change Timer.Time("----begin maplist"); AvailableMaps = FindMaps(LobbyInfo.GlobalSettings.Mods); - Timer.Time( "maplist: {0}" ); + Timer.Time("maplist: {0}"); ChangeMods(); return; } - + if (mapChangePending) { mapName = LobbyInfo.GlobalSettings.Map; @@ -281,9 +283,9 @@ namespace OpenRA using (new PerfSample("tick_time")) { lastTime += Settings.Timestep; - chrome.Tick( world ); + chrome.Tick(world); - orderManager.TickImmediate( world ); + orderManager.TickImmediate(world); var isNetTick = LocalTick % NetTickScale == 0; @@ -309,7 +311,7 @@ namespace OpenRA using (new PerfSample("render")) { ++RenderFrame; - viewport.DrawRegions( world ); + viewport.DrawRegions(world); } PerfHistory.items["render"].Tick(); @@ -364,7 +366,7 @@ namespace OpenRA if (mapName != LobbyInfo.GlobalSettings.Map) mapChangePending = true; - + if (string.Join(",", oldLobbyInfo.GlobalSettings.Mods) != string.Join(",", LobbyInfo.GlobalSettings.Mods)) { @@ -379,18 +381,18 @@ namespace OpenRA static void LoadShellMap(string map) { - LoadMap(map); + LoadMap(map); world.Queries = new World.AllQueries(world); foreach (var p in world.players.Values) foreach (var q in world.players.Values) p.Stances[q] = ChooseInitialStance(p, q); - + foreach (var gs in world.WorldActor.traits.WithInterface()) gs.GameStarted(world); orderManager.StartGame(); } - + internal static void StartGame() { LoadMap(LobbyInfo.GlobalSettings.Map); @@ -405,13 +407,13 @@ namespace OpenRA foreach (var p in world.players.Values) foreach (var q in world.players.Values) p.Stances[q] = ChooseInitialStance(p, q); - + world.Queries = new World.AllQueries(world); foreach (var gs in world.WorldActor.traits.WithInterface()) gs.GameStarted(world); - viewport.GoToStartLocation( world.LocalPlayer ); + viewport.GoToStartLocation(world.LocalPlayer); orderManager.StartGame(); } @@ -423,7 +425,7 @@ namespace OpenRA var pc = GetClientForPlayer(p); var qc = GetClientForPlayer(q); - return pc.Team != 0 && pc.Team == qc.Team + return pc.Team != 0 && pc.Team == qc.Team ? Stance.Ally : Stance.Enemy; } @@ -441,8 +443,8 @@ namespace OpenRA if (ev == MouseInputEvent.Down) lastPos = new int2(e.Location); - if (ev == MouseInputEvent.Move && - (e.Button == MouseButtons.Middle || + if (ev == MouseInputEvent.Move && + (e.Button == MouseButtons.Middle || e.Button == (MouseButtons.Left | MouseButtons.Right))) { var p = new int2(e.Location); @@ -450,7 +452,7 @@ namespace OpenRA lastPos = p; } - viewport.DispatchMouseInput( world, + viewport.DispatchMouseInput(world, new MouseInput { Button = (MouseButton)(int)e.Button, @@ -459,8 +461,8 @@ namespace OpenRA Modifiers = modifierKeys, }); - if( sync != world.SyncHash() && world == initialWorld ) - throw new InvalidOperationException( "Desync in DispatchMouseInput" ); + if (sync != world.SyncHash() && world == initialWorld) + throw new InvalidOperationException("Desync in DispatchMouseInput"); } internal static bool IsHost @@ -487,11 +489,11 @@ namespace OpenRA { ')', '0' }, }; - public static void HandleKeyPress( KeyPressEventArgs e, Modifiers modifiers ) + public static void HandleKeyPress(KeyPressEventArgs e, Modifiers modifiers) { int sync = world.SyncHash(); - - if( e.KeyChar == '\r' ) + + if (e.KeyChar == '\r') chat.Toggle(); else if (Game.chat.isChatting) chat.TypeChar(e.KeyChar); @@ -503,12 +505,17 @@ namespace OpenRA Game.controller.selection.DoControlGroup(world, c - '0', modifiers); - if (c == 'h') + if (c == 08) Game.controller.GotoNextBase(); + + if (c == 09) + BuildPaletteWidget.TabChange((Control.ModifierKeys & Keys.Shift) == Keys.Shift ? true : false); + + BuildPaletteWidget.DoBuildingHotkey(c, world); } - if( sync != Game.world.SyncHash() ) - throw new InvalidOperationException( "Desync in OnKeyPress" ); + if (sync != Game.world.SyncHash()) + throw new InvalidOperationException("Desync in OnKeyPress"); } public static void HandleModifierKeys(Modifiers mods) @@ -539,15 +546,15 @@ namespace OpenRA throw new InvalidOperationException("Unable to find game root."); Directory.SetCurrentDirectory(".."); } - + LoadUserSettings(settings); LobbyInfo.GlobalSettings.Mods = Settings.InitialMods; - + // Load the default mod to access required files LoadModPackages(new Manifest(LobbyInfo.GlobalSettings.Mods)); - + Renderer.SheetSize = Settings.SheetSize; - + bool windowed = !Game.Settings.Fullscreen; var resolution = GetResolution(settings); renderer = new Renderer(resolution, windowed); @@ -555,21 +562,21 @@ namespace OpenRA controller = new Controller(); clientSize = new int2(resolution); - + Sound.Initialize(); PerfHistory.items["render"].hasNormalTick = false; PerfHistory.items["batches"].hasNormalTick = false; PerfHistory.items["text"].hasNormalTick = false; PerfHistory.items["cursor"].hasNormalTick = false; AvailableMaps = FindMaps(LobbyInfo.GlobalSettings.Mods); - + ChangeMods(); - if( Settings.Replay != "" ) - orderManager = new OrderManager( new ReplayConnection( Settings.Replay ) ); + if (Settings.Replay != "") + orderManager = new OrderManager(new ReplayConnection(Settings.Replay)); else JoinLocal(); - + LoadShellMap(new Manifest(LobbyInfo.GlobalSettings.Mods).ShellmapUid); ResetTimer(); diff --git a/OpenRA.Game/Player.cs b/OpenRA.Game/Player.cs index af4dc78838..f5e8038a55 100644 --- a/OpenRA.Game/Player.cs +++ b/OpenRA.Game/Player.cs @@ -1,198 +1,198 @@ -#region Copyright & License Information -/* - * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. - * This file is part of OpenRA. - * - * OpenRA is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OpenRA is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with OpenRA. If not, see . - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using OpenRA.FileFormats; -using OpenRA.Traits; - -namespace OpenRA -{ - public enum PowerState { Normal, Low, Critical }; - - public class Player - { - public Actor PlayerActor; - public int Kills; - public int Deaths; - - public readonly string Palette; - public readonly Color Color; - - public readonly string PlayerName; - public readonly string InternalName; - public readonly CountryInfo Country; - public readonly int Index; - - public int Cash = 10000; - public int Ore = 0; - public int OreCapacity; - public int DisplayCash = 0; - public int PowerProvided = 0; - public int PowerDrained = 0; - - public ShroudRenderer Shroud; - public World World { get; private set; } - - public static List> PlayerColors( World world ) - { - return world.WorldActor.Info.Traits.WithInterface() - .Where(p => p.Playable) - .Select(p => Tuple.New(p.Name, p.DisplayName, p.Color)) - .ToList(); - } - - public Player( World world, Session.Client client ) - { - World = world; - Shroud = new ShroudRenderer(this, world.Map); - - PlayerActor = world.CreateActor("Player", new int2(int.MaxValue, int.MaxValue), this); - - if (client != null) - { - Index = client.Index; - Palette = PlayerColors(world)[client.PaletteIndex % PlayerColors(world).Count()].a; - Color = PlayerColors(world)[client.PaletteIndex % PlayerColors(world).Count()].c; - PlayerName = client.Name; - InternalName = "Multi{0}".F(client.Index); - } - else - { - Index = -1; - PlayerName = InternalName = "Neutral"; - Palette = "neutral"; - Color = Color.Gray; // HACK HACK - } - - Country = world.GetCountries() - .FirstOrDefault(c => client != null && client.Country == c.Name) - ?? world.GetCountries().Random(world.SharedRandom); - } - - void UpdatePower() - { - var oldBalance = PowerProvided - PowerDrained; - - PowerProvided = 0; - PowerDrained = 0; - - var myBuildings = World.Queries.OwnedBy[this] - .WithTrait(); - - foreach (var a in myBuildings) - { - var p = a.Trait.GetPowerUsage(); - if (p > 0) - PowerProvided += p; - else - PowerDrained -= p; - } - - if (PowerProvided - PowerDrained < 0) - if (PowerProvided - PowerDrained != oldBalance) - GiveAdvice(World.WorldActor.Info.Traits.Get().LowPower); - } - - public float GetSiloFullness() - { - return (float)Ore / OreCapacity; - } - - public PowerState GetPowerState() - { - if (PowerProvided >= PowerDrained) return PowerState.Normal; - if (PowerProvided > PowerDrained / 2) return PowerState.Low; - return PowerState.Critical; - } - - void UpdateOreCapacity() - { - OreCapacity = World.Queries.OwnedBy[this] - .Where(a => a.traits.Contains()) - .Select(a => a.Info.Traits.Get()) - .Sum(b => b.Capacity); - } - - void GiveAdvice(string advice) - { - // todo: store the condition or something. - // repeat after World.Defaults.SpeakDelay, as long as the condition holds. - Sound.PlayToPlayer(this, advice); - } - - public void GiveCash( int num ) { Cash += num; } - public void GiveOre(int num) - { - Ore += num; - - if (Ore > OreCapacity) - Ore = OreCapacity; // trim off the overflow. - - if (Ore > .8 * OreCapacity) - GiveAdvice(World.WorldActor.Info.Traits.Get().SilosNeeded); - } - - public bool TakeCash( int num ) - { - if (Cash + Ore < num) return false; - if (Ore <= num) - { - num -= Ore; - Ore = 0; - Cash -= num; - } - else - Ore -= num; - - return true; - } - - const float displayCashFracPerFrame = .07f; - const int displayCashDeltaPerFrame = 37; - - public void Tick() - { - UpdatePower(); - UpdateOreCapacity(); - - var totalMoney = Cash + Ore; - var diff = Math.Abs(totalMoney - DisplayCash); - var move = Math.Min(Math.Max((int)(diff * displayCashFracPerFrame), - displayCashDeltaPerFrame), diff); - - var eva = World.WorldActor.Info.Traits.Get(); - if (DisplayCash < totalMoney) - { - DisplayCash += move; - Sound.PlayToPlayer(this, eva.CashTickUp); - } - else if (DisplayCash > totalMoney) - { - DisplayCash -= move; - Sound.PlayToPlayer(this, eva.CashTickDown); - } - } - - public Dictionary Stances = new Dictionary(); - } -} +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Traits; + +namespace OpenRA +{ + public enum PowerState { Normal, Low, Critical }; + + public class Player + { + public Actor PlayerActor; + public int Kills; + public int Deaths; + + public readonly string Palette; + public readonly Color Color; + + public readonly string PlayerName; + public readonly string InternalName; + public readonly CountryInfo Country; + public readonly int Index; + + public int Cash = 10000; + public int Ore = 0; + public int OreCapacity; + public int DisplayCash = 0; + public int PowerProvided = 0; + public int PowerDrained = 0; + + public ShroudRenderer Shroud; + public World World { get; private set; } + + public static List> PlayerColors(World world) + { + return world.WorldActor.Info.Traits.WithInterface() + .Where(p => p.Playable) + .Select(p => OpenRA.FileFormats.Tuple.New(p.Name, p.DisplayName, p.Color)) + .ToList(); + } + + public Player( World world, Session.Client client ) + { + World = world; + Shroud = new ShroudRenderer(this, world.Map); + + PlayerActor = world.CreateActor("Player", new int2(int.MaxValue, int.MaxValue), this); + + if (client != null) + { + Index = client.Index; + Palette = PlayerColors(world)[client.PaletteIndex % PlayerColors(world).Count()].a; + Color = PlayerColors(world)[client.PaletteIndex % PlayerColors(world).Count()].c; + PlayerName = client.Name; + InternalName = "Multi{0}".F(client.Index); + } + else + { + Index = -1; + PlayerName = InternalName = "Neutral"; + Palette = "neutral"; + Color = Color.Gray; // HACK HACK + } + + Country = world.GetCountries() + .FirstOrDefault(c => client != null && client.Country == c.Name) + ?? world.GetCountries().Random(world.SharedRandom); + } + + void UpdatePower() + { + var oldBalance = PowerProvided - PowerDrained; + + PowerProvided = 0; + PowerDrained = 0; + + var myBuildings = World.Queries.OwnedBy[this] + .WithTrait(); + + foreach (var a in myBuildings) + { + var p = a.Trait.GetPowerUsage(); + if (p > 0) + PowerProvided += p; + else + PowerDrained -= p; + } + + if (PowerProvided - PowerDrained < 0) + if (PowerProvided - PowerDrained != oldBalance) + GiveAdvice(World.WorldActor.Info.Traits.Get().LowPower); + } + + public float GetSiloFullness() + { + return (float)Ore / OreCapacity; + } + + public PowerState GetPowerState() + { + if (PowerProvided >= PowerDrained) return PowerState.Normal; + if (PowerProvided > PowerDrained / 2) return PowerState.Low; + return PowerState.Critical; + } + + void UpdateOreCapacity() + { + OreCapacity = World.Queries.OwnedBy[this] + .Where(a => a.traits.Contains()) + .Select(a => a.Info.Traits.Get()) + .Sum(b => b.Capacity); + } + + void GiveAdvice(string advice) + { + // todo: store the condition or something. + // repeat after World.Defaults.SpeakDelay, as long as the condition holds. + Sound.PlayToPlayer(this, advice); + } + + public void GiveCash( int num ) { Cash += num; } + public void GiveOre(int num) + { + Ore += num; + + if (Ore > OreCapacity) + Ore = OreCapacity; // trim off the overflow. + + if (Ore > .8 * OreCapacity) + GiveAdvice(World.WorldActor.Info.Traits.Get().SilosNeeded); + } + + public bool TakeCash( int num ) + { + if (Cash + Ore < num) return false; + if (Ore <= num) + { + num -= Ore; + Ore = 0; + Cash -= num; + } + else + Ore -= num; + + return true; + } + + const float displayCashFracPerFrame = .07f; + const int displayCashDeltaPerFrame = 37; + + public void Tick() + { + UpdatePower(); + UpdateOreCapacity(); + + var totalMoney = Cash + Ore; + var diff = Math.Abs(totalMoney - DisplayCash); + var move = Math.Min(Math.Max((int)(diff * displayCashFracPerFrame), + displayCashDeltaPerFrame), diff); + + var eva = World.WorldActor.Info.Traits.Get(); + if (DisplayCash < totalMoney) + { + DisplayCash += move; + Sound.PlayToPlayer(this, eva.CashTickUp); + } + else if (DisplayCash > totalMoney) + { + DisplayCash -= move; + Sound.PlayToPlayer(this, eva.CashTickDown); + } + } + + public Dictionary Stances = new Dictionary(); + } +} diff --git a/OpenRA.Game/Traits/Buildable.cs b/OpenRA.Game/Traits/Buildable.cs index 8286018638..7f1310a610 100755 --- a/OpenRA.Game/Traits/Buildable.cs +++ b/OpenRA.Game/Traits/Buildable.cs @@ -1,48 +1,49 @@ -#region Copyright & License Information -/* - * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. - * This file is part of OpenRA. - * - * OpenRA is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OpenRA is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with OpenRA. If not, see . - */ -#endregion - -namespace OpenRA.Traits -{ - class ValuedInfo : ITraitInfo - { - public readonly int Cost = 0; - public readonly string Description = ""; - public readonly string LongDesc = ""; - - public virtual object Create(Actor self) { return new Valued(); } - } - - class BuildableInfo : ValuedInfo - { - public readonly int TechLevel = -1; - public readonly string[] Prerequisites = { }; - public readonly string[] BuiltAt = { }; - public readonly string[] Owner = { }; - - public readonly string Icon = null; - public readonly string[] AlternateName = { }; - public readonly int BuildPaletteOrder = 50; - - public override object Create(Actor self) { return new Buildable(); } - } - - class Valued { } /* halfway to buildable */ - class Buildable { } -} +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + class ValuedInfo : ITraitInfo + { + public readonly int Cost = 0; + public readonly string Description = ""; + public readonly string LongDesc = ""; + + public virtual object Create(Actor self) { return new Valued(); } + } + + class BuildableInfo : ValuedInfo + { + public readonly int TechLevel = -1; + public readonly string[] Prerequisites = { }; + public readonly string[] BuiltAt = { }; + public readonly string[] Owner = { }; + + public readonly string Icon = null; + public readonly string[] AlternateName = { }; + public readonly int BuildPaletteOrder = 50; + public readonly string Hotkey = null; + + public override object Create(Actor self) { return new Buildable(); } + } + + class Valued { } /* halfway to buildable */ + class Buildable { } +} diff --git a/OpenRA.Game/Traits/World/ScreenShaker.cs b/OpenRA.Game/Traits/World/ScreenShaker.cs index b825a3ff64..53d0967ce4 100644 --- a/OpenRA.Game/Traits/World/ScreenShaker.cs +++ b/OpenRA.Game/Traits/World/ScreenShaker.cs @@ -1,72 +1,72 @@ -#region Copyright & License Information -/* - * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. - * This file is part of OpenRA. - * - * OpenRA is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OpenRA is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with OpenRA. If not, see . - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; -using OpenRA.FileFormats; - -namespace OpenRA.Traits -{ - class ScreenShakerInfo : ITraitInfo - { - public object Create( Actor self ) { return new ScreenShaker(); } - } - - public class ScreenShaker : ITick - { - int ticks = 0; - List> shakeEffects = new List>(); - - public void Tick (Actor self) - { - Game.viewport.Scroll(getScrollOffset()); - shakeEffects.RemoveAll(t => t.a == ticks); - ticks++; - } - - public void AddEffect(int time, float2 position, int intensity) - { - shakeEffects.Add(Tuple.New(ticks + time, position, intensity)); - } - - public float2 getScrollOffset() - { - int xFreq = 4; - int yFreq = 5; - - return GetIntensity() * new float2( - (float) Math.Sin((ticks*2*Math.PI)/xFreq) , - (float) Math.Cos((ticks*2*Math.PI)/yFreq)); - } - - public float GetIntensity() - { - var cp = Game.viewport.Location - + .5f * new float2(Game.viewport.Width, Game.viewport.Height); - - var intensity = 24 * 24 * 100 * shakeEffects.Sum( - e => e.c / (e.b - cp).LengthSquared); - - return Math.Min(intensity, 10); - } - - } -} +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.FileFormats; + +namespace OpenRA.Traits +{ + class ScreenShakerInfo : ITraitInfo + { + public object Create( Actor self ) { return new ScreenShaker(); } + } + + public class ScreenShaker : ITick + { + int ticks = 0; + List> shakeEffects = new List>(); + + public void Tick (Actor self) + { + Game.viewport.Scroll(getScrollOffset()); + shakeEffects.RemoveAll(t => t.a == ticks); + ticks++; + } + + public void AddEffect(int time, float2 position, int intensity) + { + shakeEffects.Add(OpenRA.FileFormats.Tuple.New(ticks + time, position, intensity)); + } + + public float2 getScrollOffset() + { + int xFreq = 4; + int yFreq = 5; + + return GetIntensity() * new float2( + (float) Math.Sin((ticks*2*Math.PI)/xFreq) , + (float) Math.Cos((ticks*2*Math.PI)/yFreq)); + } + + public float GetIntensity() + { + var cp = Game.viewport.Location + + .5f * new float2(Game.viewport.Width, Game.viewport.Height); + + var intensity = 24 * 24 * 100 * shakeEffects.Sum( + e => e.c / (e.b - cp).LengthSquared); + + return Math.Min(intensity, 10); + } + + } +} diff --git a/OpenRA.Game/Widgets/BuildPaletteWidget.cs b/OpenRA.Game/Widgets/BuildPaletteWidget.cs index f84d67344c..d1301ffc43 100644 --- a/OpenRA.Game/Widgets/BuildPaletteWidget.cs +++ b/OpenRA.Game/Widgets/BuildPaletteWidget.cs @@ -1,95 +1,95 @@ -#region Copyright & License Information -/* - * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. - * This file is part of OpenRA. - * - * OpenRA is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * OpenRA is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with OpenRA. If not, see . - */ -#endregion - -using System.Drawing; -using System.Collections.Generic; -using System.Linq; -using OpenRA; -using OpenRA.Traits; -using OpenRA.Graphics; -using OpenRA.FileFormats; -using OpenRA.Orders; -using System; - -namespace OpenRA.Widgets -{ - class BuildPaletteWidget : Widget - { - public int Columns = 3; - public int Rows = 5; - - string currentTab = "Building"; - bool paletteOpen = false; - Dictionary tabImageNames; - Dictionary tabSprites; - static float2 paletteOpenOrigin = new float2(Game.viewport.Width - 215, 280); - static float2 paletteClosedOrigin = new float2(Game.viewport.Width - 16, 280); - static float2 paletteOrigin = paletteClosedOrigin; - const int paletteAnimationLength = 7; - int paletteAnimationFrame = 0; - bool paletteAnimating = false; - List>> buttons = new List>>(); - Animation cantBuild; - Animation ready; - Animation clock; - List visibleTabs = new List(); - - public BuildPaletteWidget() : base() { } - - public BuildPaletteWidget(Widget other) - : base(other) - { - throw new NotImplementedException("Why are you Cloning BuildPalette?"); - } - - public override Widget Clone() { return new BuildPaletteWidget(this); } - - public override void Initialize() - { - base.Initialize(); - - cantBuild = new Animation("clock"); - cantBuild.PlayFetchIndex("idle", () => 0); - ready = new Animation("pips"); - ready.PlayRepeating("ready"); - clock = new Animation("clock"); - - tabSprites = Rules.Info.Values - .Where(u => u.Traits.Contains()) - .ToDictionary( - u => u.Name, - u => SpriteSheetBuilder.LoadAllSprites(u.Traits.Get().Icon ?? (u.Name + "icon"))[0]); - - var groups = Rules.Categories(); - - tabImageNames = groups.Select( - (g, i) => Pair.New(g, - OpenRA.Graphics.Util.MakeArray(3, - n => i.ToString()))) - .ToDictionary(a => a.First, a => a.Second); - - } - - public override void Tick(World world) - { - visibleTabs.Clear(); +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; +using System.Collections.Generic; +using System.Linq; +using OpenRA; +using OpenRA.Traits; +using OpenRA.Graphics; +using OpenRA.FileFormats; +using OpenRA.Orders; +using System; + +namespace OpenRA.Widgets +{ + class BuildPaletteWidget : Widget + { + public int Columns = 3; + public int Rows = 5; + + string currentTab = "Building"; + bool paletteOpen = false; + Dictionary tabImageNames; + Dictionary tabSprites; + static float2 paletteOpenOrigin = new float2(Game.viewport.Width - 215, 280); + static float2 paletteClosedOrigin = new float2(Game.viewport.Width - 16, 280); + static float2 paletteOrigin = paletteClosedOrigin; + const int paletteAnimationLength = 7; + int paletteAnimationFrame = 0; + bool paletteAnimating = false; + List>> buttons = new List>>(); + Animation cantBuild; + Animation ready; + Animation clock; + List visibleTabs = new List(); + + public BuildPaletteWidget() : base() { } + + public BuildPaletteWidget(Widget other) + : base(other) + { + throw new NotImplementedException("Why are you Cloning BuildPalette?"); + } + + public override Widget Clone() { return new BuildPaletteWidget(this); } + + public override void Initialize() + { + base.Initialize(); + + cantBuild = new Animation("clock"); + cantBuild.PlayFetchIndex("idle", () => 0); + ready = new Animation("pips"); + ready.PlayRepeating("ready"); + clock = new Animation("clock"); + + tabSprites = Rules.Info.Values + .Where(u => u.Traits.Contains()) + .ToDictionary( + u => u.Name, + u => SpriteSheetBuilder.LoadAllSprites(u.Traits.Get().Icon ?? (u.Name + "icon"))[0]); + + var groups = Rules.Categories(); + + tabImageNames = groups.Select( + (g, i) => Pair.New(g, + OpenRA.Graphics.Util.MakeArray(3, + n => i.ToString()))) + .ToDictionary(a => a.First, a => a.Second); + + } + + public override void Tick(World world) + { + visibleTabs.Clear(); foreach (var q in tabImageNames) if (!Rules.TechTree.BuildableItems(world.LocalPlayer, q.Key).Any()) { @@ -97,84 +97,84 @@ namespace OpenRA.Widgets currentTab = null; } else - visibleTabs.Add(q.Key); - - if (currentTab == null) - currentTab = visibleTabs.FirstOrDefault(); - - TickPaletteAnimation(world); - - base.Tick(world); - } - - void TickPaletteAnimation(World world) - { - if (!paletteAnimating) - return; - - // Increment frame - if (paletteOpen) - paletteAnimationFrame++; - else - paletteAnimationFrame--; - - // Calculate palette position - if (paletteAnimationFrame <= paletteAnimationLength) - paletteOrigin = float2.Lerp(paletteClosedOrigin, paletteOpenOrigin, paletteAnimationFrame * 1.0f / paletteAnimationLength); - - var eva = world.WorldActor.Info.Traits.Get(); - - // Play palette-open sound at the start of the activate anim (open) - if (paletteAnimationFrame == 1 && paletteOpen) - Sound.Play(eva.BuildPaletteOpen); - - // Play palette-close sound at the start of the activate anim (close) - if (paletteAnimationFrame == paletteAnimationLength + -1 && !paletteOpen) - Sound.Play(eva.BuildPaletteClose); - - // Animation is complete - if ((paletteAnimationFrame == 0 && !paletteOpen) - || (paletteAnimationFrame == paletteAnimationLength && paletteOpen)) - { - paletteAnimating = false; - } - } - - public void SetCurrentTab(string produces) - { - if (!paletteOpen) - paletteAnimating = true; - paletteOpen = true; - currentTab = produces; - } - - public override bool HandleInput(MouseInput mi) - { - // Are we able to handle this event? - if (!IsVisible() || !GetEventBounds().Contains(mi.Location.X,mi.Location.Y)) - return base.HandleInput(mi); - - if (base.HandleInput(mi)) - return true; - - if (mi.Event == MouseInputEvent.Down) - { - var action = buttons.Where(a => a.First.Contains(mi.Location.ToPoint())) - .Select(a => a.Second).FirstOrDefault(); - if (action == null) - return false; - - action(mi); - return true; - } - - return false; - } - - public override void Draw (World world) - { - int paletteHeight = DrawPalette(world, currentTab); - DrawBuildTabs(world, paletteHeight); + visibleTabs.Add(q.Key); + + if (currentTab == null) + currentTab = visibleTabs.FirstOrDefault(); + + TickPaletteAnimation(world); + + base.Tick(world); + } + + void TickPaletteAnimation(World world) + { + if (!paletteAnimating) + return; + + // Increment frame + if (paletteOpen) + paletteAnimationFrame++; + else + paletteAnimationFrame--; + + // Calculate palette position + if (paletteAnimationFrame <= paletteAnimationLength) + paletteOrigin = float2.Lerp(paletteClosedOrigin, paletteOpenOrigin, paletteAnimationFrame * 1.0f / paletteAnimationLength); + + var eva = world.WorldActor.Info.Traits.Get(); + + // Play palette-open sound at the start of the activate anim (open) + if (paletteAnimationFrame == 1 && paletteOpen) + Sound.Play(eva.BuildPaletteOpen); + + // Play palette-close sound at the start of the activate anim (close) + if (paletteAnimationFrame == paletteAnimationLength + -1 && !paletteOpen) + Sound.Play(eva.BuildPaletteClose); + + // Animation is complete + if ((paletteAnimationFrame == 0 && !paletteOpen) + || (paletteAnimationFrame == paletteAnimationLength && paletteOpen)) + { + paletteAnimating = false; + } + } + + public void SetCurrentTab(string produces) + { + if (!paletteOpen) + paletteAnimating = true; + paletteOpen = true; + currentTab = produces; + } + + public override bool HandleInput(MouseInput mi) + { + // Are we able to handle this event? + if (!IsVisible() || !GetEventBounds().Contains(mi.Location.X,mi.Location.Y)) + return base.HandleInput(mi); + + if (base.HandleInput(mi)) + return true; + + if (mi.Event == MouseInputEvent.Down) + { + var action = buttons.Where(a => a.First.Contains(mi.Location.ToPoint())) + .Select(a => a.Second).FirstOrDefault(); + if (action == null) + return false; + + action(mi); + return true; + } + + return false; + } + + public override void Draw (World world) + { + int paletteHeight = DrawPalette(world, currentTab); + DrawBuildTabs(world, paletteHeight); } int DrawPalette(World world, string queueName) @@ -307,98 +307,107 @@ namespace OpenRA.Widgets Game.chrome.renderer.RgbaSpriteRenderer.Flush(); return 48 * y + 9; - } - - Action HandleClick(string name, World world) - { - return mi => { - var eva = world.WorldActor.Info.Traits.Get(); - Sound.Play(eva.TabClick); - - if (name != null) - HandleBuildPalette(world, name, (mi.Button == MouseButton.Left)); - }; - } - - Action HandleTabClick(string button, World world) - { - return mi => { - if (mi.Button != MouseButton.Left) - return; - - var eva = world.WorldActor.Info.Traits.Get(); - Sound.Play(eva.TabClick); - var wasOpen = paletteOpen; - paletteOpen = (currentTab == button && wasOpen) ? false : true; - currentTab = button; - if (wasOpen != paletteOpen) - paletteAnimating = true; - }; - } - - static string Description( string a ) - { - if( a[ 0 ] == '@' ) - return "any " + a.Substring( 1 ); - else - return Rules.Info[ a.ToLowerInvariant() ].Traits.Get().Description; - } - - void HandleBuildPalette( World world, string item, bool isLmb ) - { - var player = world.LocalPlayer; - var unit = Rules.Info[item]; - var queue = player.PlayerActor.traits.Get(); - var eva = world.WorldActor.Info.Traits.Get(); - var producing = queue.AllItems(unit.Category).FirstOrDefault( a => a.Item == item ); - - if (isLmb) - { - if (producing != null && producing == queue.CurrentItem(unit.Category)) - { - if (producing.Done) - { - if (unit.Traits.Contains()) - Game.controller.orderGenerator = new PlaceBuildingOrderGenerator(player.PlayerActor, item); - return; - } - - if (producing.Paused) - { - Game.IssueOrder(Order.PauseProduction(player, item, false)); - return; - } - } - - StartProduction(world, item); - } - else - { - if (producing != null) - { - // instant cancel of things we havent really started yet, and things that are finished - if (producing.Paused || producing.Done || producing.TotalCost == producing.RemainingCost) - { - Sound.Play(eva.CancelledAudio); - Game.IssueOrder(Order.CancelProduction(player, item)); - } - else - { - Sound.Play(eva.OnHoldAudio); - Game.IssueOrder(Order.PauseProduction(player, item, true)); - } - } - } - } - - void StartProduction( World world, string item ) - { - var eva = world.WorldActor.Info.Traits.Get(); - var unit = Rules.Info[item]; - - Sound.Play(unit.Traits.Contains() ? eva.BuildingSelectAudio : eva.UnitSelectAudio); - Game.IssueOrder(Order.StartProduction(world.LocalPlayer, item, - Game.controller.GetModifiers().HasModifier(Modifiers.Shift) ? 5 : 1)); + } + + Action HandleClick(string name, World world) + { + return mi => { + var eva = world.WorldActor.Info.Traits.Get(); + Sound.Play(eva.TabClick); + + if (name != null) + HandleBuildPalette(world, name, (mi.Button == MouseButton.Left)); + }; + } + + static void Hotkey(World world, String name) + { + var eva = world.WorldActor.Info.Traits.Get(); + Sound.Play(eva.TabClick); + + if (name != null) + HandleBuildPalette(world, name, true); + } + + Action HandleTabClick(string button, World world) + { + return mi => { + if (mi.Button != MouseButton.Left) + return; + + var eva = world.WorldActor.Info.Traits.Get(); + Sound.Play(eva.TabClick); + var wasOpen = paletteOpen; + paletteOpen = (currentTab == button && wasOpen) ? false : true; + currentTab = button; + if (wasOpen != paletteOpen) + paletteAnimating = true; + }; + } + + static string Description( string a ) + { + if( a[ 0 ] == '@' ) + return "any " + a.Substring( 1 ); + else + return Rules.Info[ a.ToLowerInvariant() ].Traits.Get().Description; + } + + static void HandleBuildPalette( World world, string item, bool isLmb ) + { + var player = world.LocalPlayer; + var unit = Rules.Info[item]; + var queue = player.PlayerActor.traits.Get(); + var eva = world.WorldActor.Info.Traits.Get(); + var producing = queue.AllItems(unit.Category).FirstOrDefault( a => a.Item == item ); + + if (isLmb) + { + if (producing != null && producing == queue.CurrentItem(unit.Category)) + { + if (producing.Done) + { + if (unit.Traits.Contains()) + Game.controller.orderGenerator = new PlaceBuildingOrderGenerator(player.PlayerActor, item); + return; + } + + if (producing.Paused) + { + Game.IssueOrder(Order.PauseProduction(player, item, false)); + return; + } + } + + StartProduction(world, item); + } + else + { + if (producing != null) + { + // instant cancel of things we havent really started yet, and things that are finished + if (producing.Paused || producing.Done || producing.TotalCost == producing.RemainingCost) + { + Sound.Play(eva.CancelledAudio); + Game.IssueOrder(Order.CancelProduction(player, item)); + } + else + { + Sound.Play(eva.OnHoldAudio); + Game.IssueOrder(Order.PauseProduction(player, item, true)); + } + } + } + } + + static void StartProduction( World world, string item ) + { + var eva = world.WorldActor.Info.Traits.Get(); + var unit = Rules.Info[item]; + + Sound.Play(unit.Traits.Contains() ? eva.BuildingSelectAudio : eva.UnitSelectAudio); + Game.IssueOrder(Order.StartProduction(world.LocalPlayer, item, + Game.controller.GetModifiers().HasModifier(Modifiers.Shift) ? 5 : 1)); } static Dictionary CategoryNameRemaps = new Dictionary @@ -408,32 +417,32 @@ namespace OpenRA.Widgets { "Plane", "Aircraft" }, { "Ship", "Ships" }, { "Vehicle", "Vehicles" }, - }; - - void DrawBuildTabs( World world, int paletteHeight) - { - const int tabWidth = 24; - const int tabHeight = 40; - var x = paletteOrigin.X - tabWidth; - var y = paletteOrigin.Y + 9; - - var queue = world.LocalPlayer.PlayerActor.traits.Get(); - - foreach (var q in tabImageNames) - { - var groupName = q.Key; - if (!visibleTabs.Contains(groupName)) - continue; - - string[] tabKeys = { "normal", "ready", "selected" }; - var producing = queue.CurrentItem(groupName); - var index = q.Key == currentTab ? 2 : (producing != null && producing.Done) ? 1 : 0; - var race = world.LocalPlayer.Country.Race; - WidgetUtils.DrawRGBA(ChromeProvider.GetImage(Game.chrome.renderer,"tabs-"+tabKeys[index], race+"-"+q.Key), new float2(x, y)); - + }; + + void DrawBuildTabs( World world, int paletteHeight) + { + const int tabWidth = 24; + const int tabHeight = 40; + var x = paletteOrigin.X - tabWidth; + var y = paletteOrigin.Y + 9; + + var queue = world.LocalPlayer.PlayerActor.traits.Get(); + + foreach (var q in tabImageNames) + { + var groupName = q.Key; + if (!visibleTabs.Contains(groupName)) + continue; + + string[] tabKeys = { "normal", "ready", "selected" }; + var producing = queue.CurrentItem(groupName); + var index = q.Key == currentTab ? 2 : (producing != null && producing.Done) ? 1 : 0; + var race = world.LocalPlayer.Country.Race; + WidgetUtils.DrawRGBA(ChromeProvider.GetImage(Game.chrome.renderer,"tabs-"+tabKeys[index], race+"-"+q.Key), new float2(x, y)); + var rect = new Rectangle((int)x,(int)y,(int)tabWidth,(int)tabHeight); - buttons.Add(Pair.New(rect, HandleTabClick(groupName, world))); - + buttons.Add(Pair.New(rect, HandleTabClick(groupName, world))); + if (rect.Contains(Game.chrome.lastMousePos.ToPoint())) { var text = CategoryNameRemaps.ContainsKey(groupName) ? CategoryNameRemaps[groupName] : groupName; @@ -444,20 +453,20 @@ namespace OpenRA.Widgets Game.chrome.renderer.BoldFont.DrawText(text, new float2(rect.Left - sz.X - 20, rect.Top + 12), Color.White); - } - - y += tabHeight; - } - - Game.chrome.renderer.RgbaSpriteRenderer.Flush(); - } - + } + + y += tabHeight; + } + + Game.chrome.renderer.RgbaSpriteRenderer.Flush(); + } + void DrawRightAligned(string text, int2 pos, Color c) { Game.chrome.renderer.BoldFont.DrawText(text, pos - new int2(Game.chrome.renderer.BoldFont.Measure(text).X, 0), c); - } - + } + void DrawProductionTooltip(World world, string unit, int2 pos) { pos.Y += 15; @@ -481,6 +490,9 @@ namespace OpenRA.Widgets DrawRightAligned( "${0}".F(buildable.Cost), pos + new int2(-5,5), world.LocalPlayer.Cash + world.LocalPlayer.Ore >= buildable.Cost ? Color.White : Color.Red); + if (buildable.Hotkey != null) + DrawRightAligned("{0}".F(buildable.Hotkey.ToUpper()), pos + new int2(-5, 35),Color.White); + var bi = info.Traits.GetOrDefault(); if (bi != null) DrawRightAligned("{1}{0}".F(bi.Power, bi.Power > 0 ? "+" : ""), pos + new int2(-5, 20), @@ -506,6 +518,43 @@ namespace OpenRA.Widgets p.ToInt2(), Color.White); Game.chrome.renderer.RgbaSpriteRenderer.Flush(); - } - } + } + + public static void DoBuildingHotkey(char c, World world) + { + if (Game.world.LocalPlayer == null) return; + + var buildable = Rules.TechTree.BuildableItems(Game.world.LocalPlayer, Chrome.rootWidget.GetWidget("INGAME_BUILD_PALETTE").currentTab); + + var toBuild = buildable.FirstOrDefault(b => Rules.Info[b.ToLowerInvariant()].Traits.Get().Hotkey == c.ToString()); + + if (toBuild != null) Hotkey(world, toBuild); + + } + public static void TabChange(bool shift) + { + var p = Chrome.rootWidget.GetWidget("INGAME_BUILD_PALETTE"); + int size = p.visibleTabs.Count(); + if (size > 0) + { + string last = p.visibleTabs.Last(); + string first = p.visibleTabs.First(); + int current = p.visibleTabs.IndexOf(p.currentTab); + if (!shift) + { + if (current + 1 >= size) + p.SetCurrentTab(p.visibleTabs.FirstOrDefault()); + else + p.SetCurrentTab(p.visibleTabs[current + 1]); + } + else + { + if (current - 1 < 0) + p.SetCurrentTab(p.visibleTabs.LastOrDefault()); + else + p.SetCurrentTab(p.visibleTabs[current - 1]); + } + } + } + } } \ No newline at end of file diff --git a/mods/ra/structures.yaml b/mods/ra/structures.yaml index 352cc37e64..2064084687 100644 --- a/mods/ra/structures.yaml +++ b/mods/ra/structures.yaml @@ -1,884 +1,918 @@ -MSLO: - Category: Defense - NukeSilo: - Inherits: ^Building - Buildable: - TechLevel: 13 - Prerequisites: @Tech Center - Owner: soviet,allies - Cost: 2500 - Description: Missile Silo - LongDesc: Launches a devastating nuclear strike.\n Strong vs Infantry, Buildings\n Weak vs Tanks\n Special Ability: Nuclear Missile - Building: - Power: -100 - Footprint: xx - Dimensions: 2,1 - HP: 400 - Armor: heavy - Crewed: yes - Sight: 5 - IronCurtainable: - -GAP: - Category: Defense - RequiresPower: - CanPowerDown: - GeneratesGap: - Range: 10 - Inherits: ^Building - Buildable: - TechLevel: 10 - Prerequisites: atek - Owner: allies - Cost: 500 - Description: Gap Generator - LongDesc: Regenerates the Fog of War nearby, \nobscuring the area.\n Unarmed - Building: - Power: -60 - Footprint: _ x - Dimensions: 1,2 - Capturable: true - HP: 1000 - Armor: wood - Crewed: yes - Sight: 10 - IronCurtainable: - -SPEN: - InfiltrateForSonarPulse: - Inherits: ^Building - Buildable: - TechLevel: 3 - Prerequisites: @Power Plant - Owner: soviet - Cost: 650 - Description: Sub Pen - LongDesc: Produces and repairs submarines and \ntransports - Building: - Power: -30 - Footprint: xxx xxx xxx - Dimensions: 3,3 - Capturable: true - BaseNormal: no - Adjacent: 8 - HP: 1000 - Armor: light - WaterBound: yes - Sight: 4 - ProductionSurround: - Produces: Ship - IronCurtainable: - -EmitInfantryOnSell: - -SYRD: - InfiltrateForSonarPulse: - Inherits: ^Building - Buildable: - TechLevel: 3 - Prerequisites: @Power Plant - Owner: allies - Cost: 650 - Description: Shipyard - LongDesc: Produces and repairs ships - BuildPaletteOrder: 4 - Building: - Power: -30 - Footprint: xxx xxx xxx - Dimensions: 3,3 - Capturable: true - BaseNormal: no - Adjacent: 8 - HP: 1000 - Armor: light - WaterBound: yes - Sight: 4 - ProductionSurround: - Produces: Ship - IronCurtainable: - -EmitInfantryOnSell: - -IRON: - Category: Defense - RequiresPower: - CanPowerDown: - Inherits: ^Building - Buildable: - TechLevel: 12 - Prerequisites: stek - Owner: soviet - Cost: 2800 - Description: Iron Curtain - LongDesc: Makes a group of units invulnerable for a \nshort time.\n Special Ability: Invulnerability - Building: - Power: -200 - Footprint: xx xx - Dimensions: 2,2 - Capturable: true - HP: 400 - Armor: wood - Crewed: yes - Sight: 10 - Bib: - IronCurtainable: - IronCurtain: - -PDOX: - Category: Defense - RequiresPower: - CanPowerDown: - Inherits: ^Building - Buildable: - TechLevel: 12 - Prerequisites: atek - Owner: allies - Cost: 2800 - Description: Chronosphere - LongDesc: Teleports a unit from one place \nto another, for a limited time.\n Special Ability: Chronoshift - Building: - Power: -200 - Footprint: xx xx - Dimensions: 2,2 - Capturable: true - HP: 400 - Armor: wood - Crewed: yes - Sight: 10 - Bib: - Chronosphere: - IronCurtainable: - -TSLA: - Category: Defense - RequiresPower: - CanPowerDown: - Inherits: ^Building - Buildable: - TechLevel: 7 - Prerequisites: weap - Owner: soviet - Cost: 1500 - Description: Tesla Coil - LongDesc: Advanced base defense. Requires power\nto operate.\n Strong vs Tanks, Infantry\n Weak vs Aircraft - Building: - Power: -150 - Footprint: _ x - Dimensions: 1,2 - HP: 400 - Armor: heavy - Crewed: yes - Sight: 8 - RenderBuildingCharge: - AttackTesla: - PrimaryWeapon: TeslaZap - FireDelay: 8 - AutoTarget: - IronCurtainable: - -RenderBuilding: - RenderRangeCircle: - -AGUN: - Category: Defense - RequiresPower: - CanPowerDown: - Inherits: ^Building - Buildable: - TechLevel: 5 - Prerequisites: dome - Owner: allies - Cost: 600 - Description: AA Gun - LongDesc: Anti-Air base defense.\n Strong vs Aircraft\n Weak vs Infantry, Tanks - Building: - Power: -50 - Footprint: _ x - Dimensions: 1,2 - HP: 400 - Armor: heavy - Crewed: yes - Sight: 6 - Turreted: - ROT: 15 - InitialFacing: 224 - RenderBuildingTurreted: - AttackTurreted: - PrimaryWeapon: ZSU-23 - SecondaryWeapon: ZSU-23 - AutoTarget: - IronCurtainable: - -RenderBuilding: - RenderRangeCircle: - -DOME: - RequiresPower: - CanPowerDown: - Inherits: ^Building - Buildable: - TechLevel: 3 - Prerequisites: proc - Owner: allies,soviet - Cost: 1000 - Description: Radar Dome - LongDesc: Provides an overview of the battlefield.\n Requires power to operate. - BuildPaletteOrder: 6 - Building: - Power: -40 - Footprint: xx xx - Dimensions: 2,2 - Capturable: true - HP: 1000 - Armor: wood - Crewed: yes - Sight: 10 - Bib: - ProvidesRadar: - IronCurtainable: - -PBOX: - Category: Defense - Inherits: ^Building - Buildable: - TechLevel: 2 - Prerequisites: tent - Owner: allies - Cost: 400 - Description: Pillbox - LongDesc: Basic defensive structure.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks, Aircraft - Building: - Power: -15 - HP: 400 - Armor: wood - Crewed: yes - Sight: 5 - AttackOmni: - PrimaryWeapon: Vulcan - AutoTarget: - IronCurtainable: - RenderRangeCircle: - -HBOX: - Category: Defense - Inherits: ^Building - Buildable: - TechLevel: 3 - Prerequisites: tent - Owner: allies - Cost: 600 - Description: Camo Pillbox - LongDesc: Hidden defensive structure.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks, Aircraft - Building: - Power: -15 - HP: 600 - Armor: wood - Crewed: yes - Sight: 5 - AttackOmni: - PrimaryWeapon: Vulcan - AutoTarget: - IronCurtainable: - RenderRangeCircle: - -GUN: - Category: Defense - Inherits: ^Building - Buildable: - TechLevel: 4 - Prerequisites: tent - Owner: allies - Cost: 600 - Description: Turret - LongDesc: Anti-Armor base defense.\n Strong vs Tanks\n Weak vs Infantry, Aircraft - Building: - Power: -40 - HP: 400 - Armor: heavy - Crewed: yes - Sight: 6 - Turreted: - ROT: 12 - InitialFacing: 50 - RenderBuildingTurreted: - AttackTurreted: - PrimaryWeapon: TurretGun - AutoTarget: - IronCurtainable: - -RenderBuilding: - RenderRangeCircle: - -FTUR: - Category: Defense - Inherits: ^Building - Buildable: - TechLevel: 2 - Prerequisites: barr - Owner: soviet - Cost: 600 - Description: Flame Turret - LongDesc: Anti-Infantry base defense.\n Strong vs Infantry\n Weak vs Aircraft - Building: - Power: -20 - HP: 400 - Armor: heavy - Crewed: yes - Sight: 6 - AttackOmni: - PrimaryWeapon: FireballLauncher - PrimaryOffset: 0,0,12,8 - AutoTarget: - IronCurtainable: - RenderRangeCircle: - -SAM: - Category: Defense - Inherits: ^Building - Buildable: - TechLevel: 9 - Prerequisites: dome - Owner: soviet - Cost: 750 - Description: SAM Site - LongDesc: Anti-Air base defense.\n Strong vs Aircraft\n Weak vs Infantry, Tanks - Building: - Power: -20 - Footprint: xx - Dimensions: 2,1 - HP: 400 - Armor: heavy - Crewed: yes - Sight: 5 - Turreted: - ROT: 30 - InitialFacing: 0 - RenderBuildingTurreted: - AttackTurreted: - PrimaryWeapon: Nike - AutoTarget: - IronCurtainable: - -RenderBuilding: - RenderRangeCircle: - -ATEK: - Inherits: ^Building - Buildable: - TechLevel: 10 - Prerequisites: weap,dome - Owner: allies - Cost: 1500 - Description: Allied Tech Center - LongDesc: Provides Allied advanced technologies.\n Special Ability: GPS Satellite - AlternateName: @Tech Center - Building: - Power: -200 - Footprint: xx xx - Dimensions: 2,2 - Capturable: true - HP: 400 - Armor: wood - Crewed: yes - Sight: 10 - Bib: - IronCurtainable: - GpsLaunchSite: - -WEAP: - Inherits: ^Building - Buildable: - TechLevel: 3 - Prerequisites: proc - Owner: soviet,allies - Cost: 2000 - Description: War Factory - LongDesc: Produces tanks & light vehicles. - BuildPaletteOrder: 5 - Building: - Power: -30 - Footprint: xxx xxx - Dimensions: 3,2 - Capturable: true - HP: 1000 - Armor: light - Crewed: yes - Sight: 4 - Bib: - RenderWarFactory: - RallyPoint: - Production: - Produces: Vehicle - IronCurtainable: - -FACT: - Inherits: ^Building - Building: - Power: 0 - Footprint: xxx xxx xxx - Dimensions: 3,3 - Capturable: true - HP: 1000 - Armor: heavy - Crewed: yes - Sight: 5 - Bib: - Production: - Produces: Building,Defense - ConstructionYard: - IronCurtainable: - Valued: - Cost: 2500 - Description: Construction Yard - CustomSellValue: - Value: 2500 - BaseBuilding: - -PROC: - Inherits: ^Building - Buildable: - TechLevel: 1 - Prerequisites: @Power Plant - Owner: allies,soviet - Cost: 2000 - Description: Ore Refinery - LongDesc: Converts Ore and Gems into money - BuildPaletteOrder: 1 - Building: - Power: -30 - Footprint: _x_ xxx x== - Dimensions: 3,3 - Capturable: true - HP: 900 - Armor: wood - Crewed: yes - Sight: 6 - Bib: - OreRefinery: - StoresOre: - Pips: 17 - Capacity: 2000 - IronCurtainable: - CustomSellValue: - Value: 600 - HasUnitOnBuild: - Unit: HARV - InitialActivity: Harvest - SpawnOffset: 1,2 - Facing: 64 - -SILO: - Inherits: ^Building - Buildable: - TechLevel: 1 - Prerequisites: proc - Owner: allies,soviet - Cost: 150 - Description: Silo - LongDesc: Stores excess harvested Ore - Building: - Power: -10 - Capturable: true - HP: 300 - Armor: wood - Sight: 4 - RenderBuildingOre: - StoresOre: - Pips: 5 - Capacity: 1500 - IronCurtainable: - -RenderBuilding: - -HPAD: - Inherits: ^Building - Buildable: - TechLevel: 9 - Prerequisites: dome - Owner: allies - Cost: 1500 - Description: Helipad - LongDesc: Produces and reloads helicopters - Building: - Power: -10 - Footprint: xx xx - Dimensions: 2,2 - Capturable: true - HP: 800 - Armor: wood - Crewed: yes - Sight: 5 - Bib: - Production: - SpawnOffset: 0,-4 - Produces: Plane - BelowUnits: - Reservable: - IronCurtainable: - -AFLD: - Inherits: ^Building - Buildable: - TechLevel: 5 - Prerequisites: dome - Owner: soviet - Cost: 600 - Description: Airstrip - LongDesc: Produces and reloads planes\n Special Ability: Paratroopers\n Special Ability: Spy Plane - Building: - Power: -30 - Footprint: xxx xxx - Dimensions: 3,2 - Capturable: true - HP: 1000 - Armor: heavy - Crewed: yes - Sight: 7 - Production: - Produces: Plane - BelowUnits: - Reservable: - IronCurtainable: - -POWR: - Inherits: ^Building - Buildable: - TechLevel: 1 - Owner: allies,soviet - Cost: 300 - Description: Power Plant - LongDesc: Provides power for other structures - BuildPaletteOrder: 0 - AlternateName: @Power Plant - Building: - Power: 100 - Footprint: xx xx - Dimensions: 2,2 - Capturable: true - HP: 400 - Armor: wood - Crewed: yes - Sight: 4 - Bib: - IronCurtainable: - -APWR: - Inherits: ^Building - Buildable: - TechLevel: 8 - Prerequisites: @Power Plant - Owner: allies,soviet - Cost: 500 - Description: Advanced Power Plant - LongDesc: Provides more power, cheaper than the \nstandard Power Plant - BuildPaletteOrder:2 - AlternateName: @Power Plant - Building: - Power: 200 - Footprint: ___ xxx xxx - Dimensions: 3,3 - Capturable: true - HP: 700 - Armor: wood - Crewed: yes - Sight: 4 - Bib: - IronCurtainable: - -STEK: - Inherits: ^Building - Buildable: - TechLevel: 6 - Prerequisites: weap,dome - Owner: soviet - Cost: 1500 - Description: Soviet Tech Center - LongDesc: Provides Soviet advanced technologies - AlternateName: @Tech Center - Building: - Power: -100 - Footprint: xxx xxx - Dimensions: 3,2 - Capturable: true - HP: 600 - Armor: wood - Crewed: yes - Sight: 4 - Bib: - IronCurtainable: - -BARR: - Inherits: ^Building - Buildable: - TechLevel: 1 - Prerequisites: @Power Plant - Owner: soviet - Cost: 300 - Description: Soviet Barracks - LongDesc: Produces infantry - BuildPaletteOrder: 3 - Building: - Power: -20 - Footprint: xx xx - Dimensions: 2,2 - Capturable: true - HP: 800 - Armor: wood - Crewed: yes - Sight: 5 - Bib: - RallyPoint: - Production: - Produces: Infantry - IronCurtainable: - -TENT: - Inherits: ^Building - Buildable: - TechLevel: 1 - Prerequisites: @Power Plant - Owner: allies - Cost: 300 - Description: Allied Barracks - LongDesc: Produces infantry - BuildPaletteOrder: 3 - Building: - Power: -20 - Footprint: xx xx - Dimensions: 2,2 - Capturable: true - HP: 800 - Armor: wood - Crewed: yes - Sight: 5 - Bib: - RallyPoint: - Production: - Produces: Infantry - IronCurtainable: - -KENN: - Inherits: ^Building - Buildable: - TechLevel: 3 - Prerequisites: barr - Owner: soviet - Cost: 200 - Description: Kennel - LongDesc: Produces attack dogs - Building: - Power: -10 - HP: 400 - Armor: wood - Sight: 4 - RallyPoint: - Production: - IronCurtainable: - -FIX: - Inherits: ^Building - Buildable: - TechLevel: 3 - Prerequisites: weap - Owner: allies,soviet - Cost: 1200 - Description: Service Depot - LongDesc: Repairs vehicles, reloads minelayers, and \nallows the construction of additional bases. - Building: - Power: -30 - Footprint: _x_ xxx _x_ - Dimensions: 3,3 - Capturable: true - HP: 800 - Armor: wood - Crewed: yes - Sight: 5 - BelowUnits: - Reservable: - IronCurtainable: - RepairsUnits: - -FACF: - Inherits: ^Building - Buildable: - TechLevel: 1 - Owner: allies - Cost: 50 - Description: Fake Construction Yard - LongDesc: Looks like a Construction Yard. - BuildPaletteOrder: 90 - Building: - Power: -2 - Footprint: xxx xxx xxx - Dimensions: 3,3 - Capturable: true - BaseNormal: no - HP: 30 - Sight: 4 - Bib: - RenderBuilding: - Image: FACT - Fake: - IronCurtainable: - -WEAF: - Inherits: ^Building - Buildable: - TechLevel: 3 - Prerequisites: proc - Owner: allies - Cost: 50 - Description: Fake War Factory - LongDesc: Looks like a War Factory. - BuildPaletteOrder: 90 - Building: - Power: -2 - Footprint: xxx xxx - Dimensions: 3,2 - Capturable: true - BaseNormal: no - HP: 30 - Sight: 4 - Bib: - RenderWarFactory: - RenderBuilding: - Image: WEAP - Fake: - IronCurtainable: - -SYRF: - Inherits: ^Building - Buildable: - TechLevel: 3 - Prerequisites: @Power Plant - Owner: allies - Cost: 50 - Description: Fake Shipyard - LongDesc: Looks like a Shipyard - BuildPaletteOrder: 90 - Building: - Power: -2 - Footprint: xxx xxx xxx - Dimensions: 3,3 - Capturable: true - BaseNormal: no - Adjacent: 8 - HP: 30 - WaterBound: yes - Sight: 4 - RenderBuilding: - Image: SYRD - Fake: - -EmitInfantryOnSell: - -SPEF: - Inherits: ^Building - Building: - Power: -2 - Footprint: xxx xxx xxx - Dimensions: 3,3 - Capturable: true - BaseNormal: no - Adjacent: 8 - HP: 30 - WaterBound: yes - Sight: 4 - RenderBuilding: - Image: SPEN - Fake: - -EmitInfantryOnSell: - -DOMF: - Inherits: ^Building - Buildable: - TechLevel: 3 - Prerequisites: proc - Owner: allies - Cost: 50 - Description: Fake Radar Dome - LongDesc: Looks like a Radar Dome - BuildPaletteOrder: 90 - Building: - Power: -2 - Footprint: xx xx - Dimensions: 2,2 - Capturable: true - BaseNormal: no - HP: 30 - Sight: 4 - Bib: - RenderBuilding: - Image: DOME - Fake: - -SBAG: - Category: Defense - Inherits: ^Wall - Buildable: - TechLevel: 2 - Prerequisites: fact - Owner: allies - Cost: 25 - Description: Sandbag Wall - LongDesc: Stops infantry and blocks enemy fire.\nCan be crushed by tanks. - BuildPaletteOrder: 100 - Building: - HP: 100 - Armor: none - Wall: - CrushableBy: Wheel, Track - -FENC: - Category: Defense - Inherits: ^Wall - Buildable: - TechLevel: 2 - Prerequisites: fact - Owner: soviet - Cost: 25 - Description: Wire Fence - LongDesc: Stops infantry and blocks enemy fire.\nCan be crushed by tanks. - BuildPaletteOrder: 100 - Building: - HP: 100 - Armor: none - Wall: - CrushableBy: Track - -BRIK: - Category: Defense - Inherits: ^Wall - Buildable: - TechLevel: 8 - Prerequisites: fact - Owner: allies,soviet - Cost: 100 - Description: Concrete Wall - LongDesc: Stop units and blocks enemy fire. - BuildPaletteOrder: 100 - Building: - HP: 100 - Armor: none - DamagedSound: crmble2.aud - DestroyedSound: kaboom30.aud - RenderBuildingWall: - DamageStates: 4 - -CYCL: - Inherits: ^Wall - Building: - HP: 100 - Armor: none - RenderBuildingWall: - DamageStates: 3 - Wall: - CrushableBy: Track - -BARB: - Inherits: ^Wall - Building: - HP: 100 - Armor: none - Wall: - CrushableBy: Track - -WOOD: - Inherits: ^Wall - Building: - HP: 100 - Armor: none - Wall: - CrushableBy: Track +MSLO: + Category: Defense + NukeSilo: + Inherits: ^Building + Buildable: + TechLevel: 13 + Prerequisites: @Tech Center + Owner: soviet,allies + Cost: 2500 + Description: Missile Silo + LongDesc: Launches a devastating nuclear strike.\n Strong vs Infantry, Buildings\n Weak vs Tanks\n Special Ability: Nuclear Missile + Hotkey: m + Building: + Power: -100 + Footprint: xx + Dimensions: 2,1 + HP: 400 + Armor: heavy + Crewed: yes + Sight: 5 + IronCurtainable: + +GAP: + Category: Defense + RequiresPower: + CanPowerDown: + GeneratesGap: + Range: 10 + Inherits: ^Building + Buildable: + TechLevel: 10 + Prerequisites: atek + Owner: allies + Cost: 500 + Description: Gap Generator + LongDesc: Regenerates the Fog of War nearby, \nobscuring the area.\n Unarmed + Hotkey: g + Building: + Power: -60 + Footprint: _ x + Dimensions: 1,2 + Capturable: true + HP: 1000 + Armor: wood + Crewed: yes + Sight: 10 + IronCurtainable: + +SPEN: + InfiltrateForSonarPulse: + Inherits: ^Building + Buildable: + TechLevel: 3 + Prerequisites: @Power Plant + Owner: soviet + Cost: 650 + Description: Sub Pen + LongDesc: Produces and repairs submarines and \ntransports + Hotkey: s + Building: + Power: -30 + Footprint: xxx xxx xxx + Dimensions: 3,3 + Capturable: true + BaseNormal: no + Adjacent: 8 + HP: 1000 + Armor: light + WaterBound: yes + Sight: 4 + ProductionSurround: + Produces: Ship + IronCurtainable: + -EmitInfantryOnSell: + +SYRD: + InfiltrateForSonarPulse: + Inherits: ^Building + Buildable: + TechLevel: 3 + Prerequisites: @Power Plant + Owner: allies + Cost: 650 + Description: Shipyard + LongDesc: Produces and repairs ships + BuildPaletteOrder: 4 + Hotkey: s + Building: + Power: -30 + Footprint: xxx xxx xxx + Dimensions: 3,3 + Capturable: true + BaseNormal: no + Adjacent: 8 + HP: 1000 + Armor: light + WaterBound: yes + Sight: 4 + ProductionSurround: + Produces: Ship + IronCurtainable: + -EmitInfantryOnSell: + +IRON: + Category: Defense + RequiresPower: + CanPowerDown: + Inherits: ^Building + Buildable: + TechLevel: 12 + Prerequisites: stek + Owner: soviet + Cost: 2800 + Description: Iron Curtain + LongDesc: Makes a group of units invulnerable for a \nshort time.\n Special Ability: Invulnerability + Hotkey: c + Building: + Power: -200 + Footprint: xx xx + Dimensions: 2,2 + Capturable: true + HP: 400 + Armor: wood + Crewed: yes + Sight: 10 + Bib: + IronCurtainable: + IronCurtain: + +PDOX: + Category: Defense + RequiresPower: + CanPowerDown: + Inherits: ^Building + Buildable: + TechLevel: 12 + Prerequisites: atek + Owner: allies + Cost: 2800 + Description: Chronosphere + LongDesc: Teleports a unit from one place \nto another, for a limited time.\n Special Ability: Chronoshift + Hotkey: o + Building: + Power: -200 + Footprint: xx xx + Dimensions: 2,2 + Capturable: true + HP: 400 + Armor: wood + Crewed: yes + Sight: 10 + Bib: + Chronosphere: + IronCurtainable: + +TSLA: + Category: Defense + RequiresPower: + CanPowerDown: + Inherits: ^Building + Buildable: + TechLevel: 7 + Prerequisites: weap + Owner: soviet + Cost: 1500 + Description: Tesla Coil + LongDesc: Advanced base defense. Requires power\nto operate.\n Strong vs Tanks, Infantry\n Weak vs Aircraft + Hotkey: t + Building: + Power: -150 + Footprint: _ x + Dimensions: 1,2 + HP: 400 + Armor: heavy + Crewed: yes + Sight: 8 + RenderBuildingCharge: + AttackTesla: + PrimaryWeapon: TeslaZap + FireDelay: 8 + AutoTarget: + IronCurtainable: + -RenderBuilding: + RenderRangeCircle: + +AGUN: + Category: Defense + RequiresPower: + CanPowerDown: + Inherits: ^Building + Buildable: + TechLevel: 5 + Prerequisites: dome + Owner: allies + Cost: 600 + Description: AA Gun + LongDesc: Anti-Air base defense.\n Strong vs Aircraft\n Weak vs Infantry, Tanks + Hotkey: a + Building: + Power: -50 + Footprint: _ x + Dimensions: 1,2 + HP: 400 + Armor: heavy + Crewed: yes + Sight: 6 + Turreted: + ROT: 15 + InitialFacing: 224 + RenderBuildingTurreted: + AttackTurreted: + PrimaryWeapon: ZSU-23 + SecondaryWeapon: ZSU-23 + AutoTarget: + IronCurtainable: + -RenderBuilding: + RenderRangeCircle: + +DOME: + RequiresPower: + CanPowerDown: + Inherits: ^Building + Buildable: + TechLevel: 3 + Prerequisites: proc + Owner: allies,soviet + Cost: 1000 + Description: Radar Dome + LongDesc: Provides an overview of the battlefield.\n Requires power to operate. + BuildPaletteOrder: 6 + Hotkey: r + Building: + Power: -40 + Footprint: xx xx + Dimensions: 2,2 + Capturable: true + HP: 1000 + Armor: wood + Crewed: yes + Sight: 10 + Bib: + ProvidesRadar: + IronCurtainable: + +PBOX: + Category: Defense + Inherits: ^Building + Buildable: + TechLevel: 2 + Prerequisites: tent + Owner: allies + Cost: 400 + Description: Pillbox + LongDesc: Basic defensive structure.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks, Aircraft + Hotkey: x + Building: + Power: -15 + HP: 400 + Armor: wood + Crewed: yes + Sight: 5 + AttackOmni: + PrimaryWeapon: Vulcan + AutoTarget: + IronCurtainable: + RenderRangeCircle: + +HBOX: + Category: Defense + Inherits: ^Building + Buildable: + TechLevel: 3 + Prerequisites: tent + Owner: allies + Cost: 600 + Description: Camo Pillbox + LongDesc: Hidden defensive structure.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks, Aircraft + Hotkey: c + Building: + Power: -15 + HP: 600 + Armor: wood + Crewed: yes + Sight: 5 + AttackOmni: + PrimaryWeapon: Vulcan + AutoTarget: + IronCurtainable: + RenderRangeCircle: + +GUN: + Category: Defense + Inherits: ^Building + Buildable: + TechLevel: 4 + Prerequisites: tent + Owner: allies + Cost: 600 + Description: Turret + LongDesc: Anti-Armor base defense.\n Strong vs Tanks\n Weak vs Infantry, Aircraft + Hotkey: t + Building: + Power: -40 + HP: 400 + Armor: heavy + Crewed: yes + Sight: 6 + Turreted: + ROT: 12 + InitialFacing: 50 + RenderBuildingTurreted: + AttackTurreted: + PrimaryWeapon: TurretGun + AutoTarget: + IronCurtainable: + -RenderBuilding: + RenderRangeCircle: + +FTUR: + Category: Defense + Inherits: ^Building + Buildable: + TechLevel: 2 + Prerequisites: barr + Owner: soviet + Cost: 600 + Description: Flame Turret + LongDesc: Anti-Infantry base defense.\n Strong vs Infantry\n Weak vs Aircraft + Hotkey: f + Building: + Power: -20 + HP: 400 + Armor: heavy + Crewed: yes + Sight: 6 + AttackOmni: + PrimaryWeapon: FireballLauncher + PrimaryOffset: 0,0,12,8 + AutoTarget: + IronCurtainable: + RenderRangeCircle: + +SAM: + Category: Defense + Inherits: ^Building + Buildable: + TechLevel: 9 + Prerequisites: dome + Owner: soviet + Cost: 750 + Description: SAM Site + LongDesc: Anti-Air base defense.\n Strong vs Aircraft\n Weak vs Infantry, Tanks + Hotkey: s + Building: + Power: -20 + Footprint: xx + Dimensions: 2,1 + HP: 400 + Armor: heavy + Crewed: yes + Sight: 5 + Turreted: + ROT: 30 + InitialFacing: 0 + RenderBuildingTurreted: + AttackTurreted: + PrimaryWeapon: Nike + AutoTarget: + IronCurtainable: + -RenderBuilding: + RenderRangeCircle: + +ATEK: + Inherits: ^Building + Buildable: + TechLevel: 10 + Prerequisites: weap,dome + Owner: allies + Cost: 1500 + Description: Allied Tech Center + LongDesc: Provides Allied advanced technologies.\n Special Ability: GPS Satellite + AlternateName: @Tech Center + Hotkey: t + Building: + Power: -200 + Footprint: xx xx + Dimensions: 2,2 + Capturable: true + HP: 400 + Armor: wood + Crewed: yes + Sight: 10 + Bib: + IronCurtainable: + GpsLaunchSite: + +WEAP: + Inherits: ^Building + Buildable: + TechLevel: 3 + Prerequisites: proc + Owner: soviet,allies + Cost: 2000 + Description: War Factory + LongDesc: Produces tanks & light vehicles. + BuildPaletteOrder: 5 + Hotkey: w + Building: + Power: -30 + Footprint: xxx xxx + Dimensions: 3,2 + Capturable: true + HP: 1000 + Armor: light + Crewed: yes + Sight: 4 + Bib: + RenderWarFactory: + RallyPoint: + Production: + Produces: Vehicle + IronCurtainable: + +FACT: + Inherits: ^Building + Building: + Power: 0 + Footprint: xxx xxx xxx + Dimensions: 3,3 + Capturable: true + HP: 1000 + Armor: heavy + Crewed: yes + Sight: 5 + Bib: + Production: + Produces: Building,Defense + ConstructionYard: + IronCurtainable: + Valued: + Cost: 2500 + Description: Construction Yard + CustomSellValue: + Value: 2500 + BaseBuilding: + +PROC: + Inherits: ^Building + Buildable: + TechLevel: 1 + Prerequisites: @Power Plant + Owner: allies,soviet + Cost: 2000 + Description: Ore Refinery + LongDesc: Converts Ore and Gems into money + BuildPaletteOrder: 1 + Hotkey: e + Building: + Power: -30 + Footprint: _x_ xxx x== + Dimensions: 3,3 + Capturable: true + HP: 900 + Armor: wood + Crewed: yes + Sight: 6 + Bib: + OreRefinery: + StoresOre: + Pips: 17 + Capacity: 2000 + IronCurtainable: + CustomSellValue: + Value: 600 + HasUnitOnBuild: + Unit: HARV + InitialActivity: Harvest + SpawnOffset: 1,2 + Facing: 64 + +SILO: + Inherits: ^Building + Buildable: + TechLevel: 1 + Prerequisites: proc + Owner: allies,soviet + Cost: 150 + Description: Silo + LongDesc: Stores excess harvested Ore + Hotkey: o + Building: + Power: -10 + Capturable: true + HP: 300 + Armor: wood + Sight: 4 + RenderBuildingOre: + StoresOre: + Pips: 5 + Capacity: 1500 + IronCurtainable: + -RenderBuilding: + +HPAD: + Inherits: ^Building + Buildable: + TechLevel: 9 + Prerequisites: dome + Owner: allies + Cost: 1500 + Description: Helipad + LongDesc: Produces and reloads helicopters + Hotkey: i + Building: + Power: -10 + Footprint: xx xx + Dimensions: 2,2 + Capturable: true + HP: 800 + Armor: wood + Crewed: yes + Sight: 5 + Bib: + Production: + SpawnOffset: 0,-4 + Produces: Plane + BelowUnits: + Reservable: + IronCurtainable: + +AFLD: + Inherits: ^Building + Buildable: + TechLevel: 5 + Prerequisites: dome + Owner: soviet + Cost: 600 + Description: Airstrip + LongDesc: Produces and reloads planes\n Special Ability: Paratroopers\n Special Ability: Spy Plane + Hotkey: a + Building: + Power: -30 + Footprint: xxx xxx + Dimensions: 3,2 + Capturable: true + HP: 1000 + Armor: heavy + Crewed: yes + Sight: 7 + Production: + Produces: Plane + BelowUnits: + Reservable: + IronCurtainable: + +POWR: + Inherits: ^Building + Buildable: + TechLevel: 1 + Owner: allies,soviet + Cost: 300 + Description: Power Plant + LongDesc: Provides power for other structures + BuildPaletteOrder: 0 + AlternateName: @Power Plant + Hotkey: p + Building: + Power: 100 + Footprint: xx xx + Dimensions: 2,2 + Capturable: true + HP: 400 + Armor: wood + Crewed: yes + Sight: 4 + Bib: + IronCurtainable: + +APWR: + Inherits: ^Building + Buildable: + TechLevel: 8 + Prerequisites: @Power Plant + Owner: allies,soviet + Cost: 500 + Description: Advanced Power Plant + LongDesc: Provides more power, cheaper than the \nstandard Power Plant + BuildPaletteOrder:2 + Hotkey: l + AlternateName: @Power Plant + Building: + Power: 200 + Footprint: ___ xxx xxx + Dimensions: 3,3 + Capturable: true + HP: 700 + Armor: wood + Crewed: yes + Sight: 4 + Bib: + IronCurtainable: + +STEK: + Inherits: ^Building + Buildable: + TechLevel: 6 + Prerequisites: weap,dome + Owner: soviet + Cost: 1500 + Description: Soviet Tech Center + LongDesc: Provides Soviet advanced technologies + AlternateName: @Tech Center + Hotkey: t + Building: + Power: -100 + Footprint: xxx xxx + Dimensions: 3,2 + Capturable: true + HP: 600 + Armor: wood + Crewed: yes + Sight: 4 + Bib: + IronCurtainable: + +BARR: + Inherits: ^Building + Buildable: + TechLevel: 1 + Prerequisites: @Power Plant + Owner: soviet + Cost: 300 + Description: Soviet Barracks + LongDesc: Produces infantry + BuildPaletteOrder: 3 + Hotkey: b + Building: + Power: -20 + Footprint: xx xx + Dimensions: 2,2 + Capturable: true + HP: 800 + Armor: wood + Crewed: yes + Sight: 5 + Bib: + RallyPoint: + Production: + Produces: Infantry + IronCurtainable: + +TENT: + Inherits: ^Building + Buildable: + TechLevel: 1 + Prerequisites: @Power Plant + Owner: allies + Cost: 300 + Description: Allied Barracks + LongDesc: Produces infantry + BuildPaletteOrder: 3 + Hotkey: b + Building: + Power: -20 + Footprint: xx xx + Dimensions: 2,2 + Capturable: true + HP: 800 + Armor: wood + Crewed: yes + Sight: 5 + Bib: + RallyPoint: + Production: + Produces: Infantry + IronCurtainable: + +KENN: + Inherits: ^Building + Buildable: + TechLevel: 3 + Prerequisites: barr + Owner: soviet + Cost: 200 + Description: Kennel + LongDesc: Produces attack dogs + Hotkey: k + Building: + Power: -10 + HP: 400 + Armor: wood + Sight: 4 + RallyPoint: + Production: + IronCurtainable: + +FIX: + Inherits: ^Building + Buildable: + TechLevel: 3 + Prerequisites: weap + Owner: allies,soviet + Cost: 1200 + Description: Service Depot + LongDesc: Repairs vehicles, reloads minelayers, and \nallows the construction of additional bases. + Hotkey: d + Building: + Power: -30 + Footprint: _x_ xxx _x_ + Dimensions: 3,3 + Capturable: true + HP: 800 + Armor: wood + Crewed: yes + Sight: 5 + BelowUnits: + Reservable: + IronCurtainable: + RepairsUnits: + +FACF: + Inherits: ^Building + Buildable: + TechLevel: 1 + Owner: allies + Cost: 50 + Description: Fake Construction Yard + LongDesc: Looks like a Construction Yard. + BuildPaletteOrder: 90 + Hotkey: c + Building: + Power: -2 + Footprint: xxx xxx xxx + Dimensions: 3,3 + Capturable: true + BaseNormal: no + HP: 30 + Sight: 4 + Bib: + RenderBuilding: + Image: FACT + Fake: + IronCurtainable: + +WEAF: + Inherits: ^Building + Buildable: + TechLevel: 3 + Prerequisites: proc + Owner: allies + Cost: 50 + Description: Fake War Factory + LongDesc: Looks like a War Factory. + BuildPaletteOrder: 90 + Hotkey: x + Building: + Power: -2 + Footprint: xxx xxx + Dimensions: 3,2 + Capturable: true + BaseNormal: no + HP: 30 + Sight: 4 + Bib: + RenderWarFactory: + RenderBuilding: + Image: WEAP + Fake: + IronCurtainable: + +SYRF: + Inherits: ^Building + Buildable: + TechLevel: 3 + Prerequisites: @Power Plant + Owner: allies + Cost: 50 + Description: Fake Shipyard + LongDesc: Looks like a Shipyard + BuildPaletteOrder: 90 + Hotkey: z + Building: + Power: -2 + Footprint: xxx xxx xxx + Dimensions: 3,3 + Capturable: true + BaseNormal: no + Adjacent: 8 + HP: 30 + WaterBound: yes + Sight: 4 + RenderBuilding: + Image: SYRD + Fake: + -EmitInfantryOnSell: + +SPEF: + Inherits: ^Building + Building: + Power: -2 + Footprint: xxx xxx xxx + Dimensions: 3,3 + Capturable: true + BaseNormal: no + Adjacent: 8 + HP: 30 + WaterBound: yes + Sight: 4 + RenderBuilding: + Image: SPEN + Fake: + -EmitInfantryOnSell: + +DOMF: + Inherits: ^Building + Buildable: + TechLevel: 3 + Prerequisites: proc + Owner: allies + Cost: 50 + Description: Fake Radar Dome + LongDesc: Looks like a Radar Dome + BuildPaletteOrder: 90 + Hotkey: v + Building: + Power: -2 + Footprint: xx xx + Dimensions: 2,2 + Capturable: true + BaseNormal: no + HP: 30 + Sight: 4 + Bib: + RenderBuilding: + Image: DOME + Fake: + +SBAG: + Category: Defense + Inherits: ^Wall + Buildable: + TechLevel: 2 + Prerequisites: fact + Owner: allies + Cost: 25 + Description: Sandbag Wall + LongDesc: Stops infantry and blocks enemy fire.\nCan be crushed by tanks. + BuildPaletteOrder: 100 + Hotkey: b + Building: + HP: 100 + Armor: none + Wall: + CrushableBy: Wheel, Track + +FENC: + Category: Defense + Inherits: ^Wall + Buildable: + TechLevel: 2 + Prerequisites: fact + Owner: soviet + Cost: 25 + Description: Wire Fence + LongDesc: Stops infantry and blocks enemy fire.\nCan be crushed by tanks. + BuildPaletteOrder: 100 + Hotkey: n + Building: + HP: 100 + Armor: none + Wall: + CrushableBy: Track + +BRIK: + Category: Defense + Inherits: ^Wall + Buildable: + TechLevel: 8 + Prerequisites: fact + Owner: allies,soviet + Cost: 100 + Description: Concrete Wall + LongDesc: Stop units and blocks enemy fire. + BuildPaletteOrder: 100 + Hotkey: w + Building: + HP: 100 + Armor: none + DamagedSound: crmble2.aud + DestroyedSound: kaboom30.aud + RenderBuildingWall: + DamageStates: 4 + +CYCL: + Inherits: ^Wall + Building: + HP: 100 + Armor: none + RenderBuildingWall: + DamageStates: 3 + Wall: + CrushableBy: Track + +BARB: + Inherits: ^Wall + Building: + HP: 100 + Armor: none + Wall: + CrushableBy: Track + +WOOD: + Inherits: ^Wall + Building: + HP: 100 + Armor: none + Wall: + CrushableBy: Track