diff --git a/OpenRa.Game/Chrome.cs b/OpenRa.Game/Chrome.cs index 5ebd186d6e..ed168a1675 100644 --- a/OpenRa.Game/Chrome.cs +++ b/OpenRa.Game/Chrome.cs @@ -26,6 +26,7 @@ namespace OpenRa.Game readonly Animation repairButton; readonly Animation sellButton; + readonly Animation pwrdownButton; readonly SpriteRenderer buildPaletteRenderer; readonly Animation cantBuild; @@ -77,6 +78,9 @@ namespace OpenRa.Game sellButton = new Animation("sell"); sellButton.PlayRepeating("normal"); + + pwrdownButton = new Animation("repair"); + pwrdownButton.PlayRepeating("normal"); blank = SheetBuilder.Add(new Size(64, 48), 16); @@ -141,14 +145,21 @@ namespace OpenRa.Game DrawPower(); chromeRenderer.Flush(); DrawButtons(); - + DrawMinimap(); int paletteHeight = DrawBuildPalette(currentTab); DrawBuildTabs(paletteHeight); DrawChat(); } + void DrawMinimap() + { + var hasRadar = Game.world.Actors.Any(a => a.Owner == Game.LocalPlayer && a.traits.Contains() && a.traits.Get().IsActive()); + if (hasRadar) + Game.minimap.Draw(new float2(Game.viewport.Width - 128, 30)); + } + void AddButton(Rectangle r, Action b) { buttons.Add(Pair.New(r, b)); } - + void DrawBuildTabs(int paletteHeight) { const int tabWidth = 24; @@ -285,7 +296,7 @@ namespace OpenRa.Game // Repair - Rectangle repairRect = new Rectangle(Game.viewport.Width - 100, 5, repairButton.Image.bounds.Width, repairButton.Image.bounds.Height); + Rectangle repairRect = new Rectangle(Game.viewport.Width - 120, 5, repairButton.Image.bounds.Width, repairButton.Image.bounds.Height); var repairDrawPos = Game.viewport.Location + new float2(repairRect.Location); var hasFact = Game.world.Actors.Any(a => a.Owner == Game.LocalPlayer && a.traits.Contains()); @@ -300,7 +311,7 @@ namespace OpenRa.Game buildPaletteRenderer.DrawSprite(repairButton.Image, repairDrawPos, PaletteType.Chrome); // Sell - Rectangle sellRect = new Rectangle(Game.viewport.Width - 60, 5, + Rectangle sellRect = new Rectangle(Game.viewport.Width - 80, 5, sellButton.Image.bounds.Width, sellButton.Image.bounds.Height); var sellDrawPos = Game.viewport.Location + new float2(sellRect.Location); @@ -310,6 +321,21 @@ namespace OpenRa.Game AddButton(sellRect, isLmb => Game.controller.ToggleInputMode()); buildPaletteRenderer.DrawSprite(sellButton.Image, sellDrawPos, PaletteType.Chrome); buildPaletteRenderer.Flush(); + + if (Game.Settings.PowerDownBuildings) + { + // Power Down + Rectangle pwrdownRect = new Rectangle(Game.viewport.Width - 40, 5, + pwrdownButton.Image.bounds.Width, pwrdownButton.Image.bounds.Height); + + var pwrdownDrawPos = Game.viewport.Location + new float2(pwrdownRect.Location); + + pwrdownButton.ReplaceAnim(Game.controller.orderGenerator is PowerDownOrderGenerator ? "pressed" : "normal"); + + AddButton(pwrdownRect, isLmb => Game.controller.ToggleInputMode()); + buildPaletteRenderer.DrawSprite(pwrdownButton.Image, pwrdownDrawPos, PaletteType.Chrome); + } + buildPaletteRenderer.Flush(); } void HandleChronosphereButton() diff --git a/OpenRa.Game/Combat.cs b/OpenRa.Game/Combat.cs index ea855f1f38..b6cb373754 100644 --- a/OpenRa.Game/Combat.cs +++ b/OpenRa.Game/Combat.cs @@ -32,7 +32,7 @@ namespace OpenRa.Game var maxSpread = GetMaximumSpread(weapon, warhead); var hitActors = Game.FindUnitsInCircle(loc, maxSpread); - + foreach (var victim in hitActors) victim.InflictDamage(firedBy, (int)GetDamageToInflict(victim, loc, weapon, warhead), warhead); } @@ -46,11 +46,10 @@ namespace OpenRa.Game { if (!WeaponValidForTarget(weapon, target)) return 0f; - - var distance = (target.CenterLocation - loc).Length; + + var distance = (target.CenterLocation - loc).Length*1/24f; var rawDamage = weapon.Damage * (float)Math.Exp(-distance / warhead.Spread); var multiplier = warhead.EffectivenessAgainst(target.Info.Armor); - return rawDamage * multiplier; } diff --git a/OpenRa.Game/Cursor.cs b/OpenRa.Game/Cursor.cs index ee4115a229..54ebacc76d 100644 --- a/OpenRa.Game/Cursor.cs +++ b/OpenRa.Game/Cursor.cs @@ -32,5 +32,6 @@ namespace OpenRa.Game public static Cursor SellBlocked { get { return new Cursor("sell-blocked"); } } public static Cursor Repair { get { return new Cursor("repair"); } } public static Cursor RepairBlocked { get { return new Cursor("repair-blocked"); } } + public static Cursor PowerDown { get { return new Cursor("powerdown"); } } } } diff --git a/OpenRa.Game/GameRules/UserSettings.cs b/OpenRa.Game/GameRules/UserSettings.cs index fd102cf4bd..8c441320f3 100644 --- a/OpenRa.Game/GameRules/UserSettings.cs +++ b/OpenRa.Game/GameRules/UserSettings.cs @@ -27,6 +27,7 @@ namespace OpenRa.Game.GameRules // Gameplay options public readonly bool RepairRequiresConyard = true; + public readonly bool PowerDownBuildings = true; } } diff --git a/OpenRa.Game/Graphics/CursorSheetBuilder.cs b/OpenRa.Game/Graphics/CursorSheetBuilder.cs index 333a225da9..1a6b7291e3 100644 --- a/OpenRa.Game/Graphics/CursorSheetBuilder.cs +++ b/OpenRa.Game/Graphics/CursorSheetBuilder.cs @@ -11,8 +11,16 @@ namespace OpenRa.Game.Graphics static Sprite[] LoadCursors(string filename) { - var shp = new Dune2ShpReader(FileSystem.OpenWithExts(filename, exts)); - return shp.Select(a => SheetBuilder.Add(a.Image, a.Size)).ToArray(); + try + { + var shp = new Dune2ShpReader(FileSystem.OpenWithExts(filename, exts)); + return shp.Select(a => SheetBuilder.Add(a.Image, a.Size)).ToArray(); + } + catch (System.IndexOutOfRangeException) // This will occur when loading a custom (RA-format) .shp + { + var shp = new ShpReader(FileSystem.OpenWithExts(filename, exts)); + return shp.Select(a => SheetBuilder.Add(a.Image, shp.Size)).ToArray(); + } } public static Sprite[] LoadAllSprites(string filename) { return cursors[filename]; } diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj index 3c69270e79..4aa7ac18d0 100644 --- a/OpenRa.Game/OpenRa.Game.csproj +++ b/OpenRa.Game/OpenRa.Game.csproj @@ -103,6 +103,7 @@ + @@ -218,6 +219,7 @@ + @@ -302,4 +304,4 @@ --> - \ No newline at end of file + diff --git a/OpenRa.Game/Orders/PowerDownOrderGenerator.cs b/OpenRa.Game/Orders/PowerDownOrderGenerator.cs new file mode 100644 index 0000000000..5779e81a20 --- /dev/null +++ b/OpenRa.Game/Orders/PowerDownOrderGenerator.cs @@ -0,0 +1,47 @@ +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 PowerDownOrderGenerator : IOrderGenerator + { + public IEnumerable Order(int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Right) + Game.controller.CancelInputMode(); + + return OrderInner(xy, mi); + } + + IEnumerable OrderInner(int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Left) + { + var loc = mi.Location + Game.viewport.Location; + var underCursor = Game.FindUnits(loc, loc) + .Where(a => a.Owner == Game.LocalPlayer + && a.traits.Contains() + && a.Info.Selectable).FirstOrDefault(); + + var building = underCursor != null ? underCursor.Info as BuildingInfo : null; + + if (building != null) + yield return new Order("PowerDown", underCursor, null, int2.Zero, null); + } + } + + public void Tick() { } + public void Render() { } + + public Cursor GetCursor(int2 xy, MouseInput mi) + { + mi.Button = MouseButton.Left; + return OrderInner(xy, mi).Any() + ? Cursor.PowerDown : Cursor.PowerDown; + } + } +} diff --git a/OpenRa.Game/Player.cs b/OpenRa.Game/Player.cs index 90d6a9f57b..bae1f38aa5 100644 --- a/OpenRa.Game/Player.cs +++ b/OpenRa.Game/Player.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Collections.Generic; using OpenRa.Game.GameRules; using OpenRa.Game.Graphics; using OpenRa.Game.Traits; @@ -50,11 +51,11 @@ namespace OpenRa.Game foreach (var a in myBuildings) { - var bi = a.Info as BuildingInfo; - if (bi.Power > 0) /* todo: is this how real-ra scales it? */ - PowerProvided += (a.Health * bi.Power) / bi.Strength; + var p = a.traits.Get().GetPowerUsage(); + if (p > 0) + PowerProvided += p; else - PowerDrained -= bi.Power; + PowerDrained -= p; } if (PowerProvided - PowerDrained < 0) diff --git a/OpenRa.Game/Traits/AttackTurreted.cs b/OpenRa.Game/Traits/AttackTurreted.cs index 256bd25379..1d0007e223 100755 --- a/OpenRa.Game/Traits/AttackTurreted.cs +++ b/OpenRa.Game/Traits/AttackTurreted.cs @@ -26,12 +26,9 @@ namespace OpenRa.Game.Traits protected override void QueueAttack( Actor self, Order order ) { - var bi = self.Info as BuildingInfo; - if (bi != null && bi.Powered && self.Owner.GetPowerState() != PowerState.Normal) - { - if (self.Owner == Game.LocalPlayer) Sound.Play("nopowr1.aud"); + var b = self.traits.Get(); + if (b != null && b.InsuffientPower()) 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 */ diff --git a/OpenRa.Game/Traits/Building.cs b/OpenRa.Game/Traits/Building.cs index 5beea8d05f..dfb870285c 100644 --- a/OpenRa.Game/Traits/Building.cs +++ b/OpenRa.Game/Traits/Building.cs @@ -5,20 +5,66 @@ using System.Collections.Generic; using System.Linq; using System.Text; using OpenRa.Game.Effects; +using OpenRa.Game.Graphics; namespace OpenRa.Game.Traits { - class Building : INotifyDamage, IOrder, ITick + class Building : INotifyDamage, IOrder, ITick, IRenderModifier { + readonly Actor self; public readonly BuildingInfo unitInfo; bool isRepairing = false; + bool isPoweredDown = false; public Building(Actor self) { + this.self = self; unitInfo = (BuildingInfo)self.Info; self.CenterLocation = Game.CellSize * ((float2)self.Location + .5f * (float2)unitInfo.Dimensions); } + + public bool InsuffientPower() + { + return (isPoweredDown || (unitInfo.Powered && self.Owner.GetPowerState() != PowerState.Normal)); + } + + public int GetPowerUsage() + { + if (isPoweredDown) + return 0; + + if (unitInfo.Power > 0) /* todo: is this how real-ra scales it? */ + return (self.Health * unitInfo.Power) / unitInfo.Strength; + else + return unitInfo.Power; + } + + public Animation iconAnim; + public IEnumerable + ModifyRender(Actor self, IEnumerable rs) + { + if (!InsuffientPower()) + return rs; + + List nrs = new List(rs); + foreach(var r in rs) + { + // Need 2 shadows to make it dark enough + nrs.Add(r.WithPalette(PaletteType.Shadow)); + nrs.Add(r.WithPalette(PaletteType.Shadow)); + } + + if (isPoweredDown) + { + iconAnim = new Animation("powerdown"); + iconAnim.PlayRepeating("disabled"); + nrs.Add(new Renderable(iconAnim.Image, self.CenterLocation - 0.5f*iconAnim.Image.size, PaletteType.Chrome)); + } + + + return nrs; + } public void Damaged(Actor self, AttackInfo e) { @@ -43,6 +89,12 @@ namespace OpenRa.Game.Traits { isRepairing = !isRepairing; } + + if (order.OrderString == "PowerDown") + { + isPoweredDown = !isPoweredDown; + Sound.Play((isPoweredDown) ? "bleep12.aud" : "bleep11.aud"); + } } int remainingTicks; diff --git a/OpenRa.Game/Traits/ProvidesRadar.cs b/OpenRa.Game/Traits/ProvidesRadar.cs new file mode 100644 index 0000000000..41c3ced78c --- /dev/null +++ b/OpenRa.Game/Traits/ProvidesRadar.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenRa.Game.Traits +{ + class ProvidesRadar + { + Actor self; + public ProvidesRadar(Actor self) + { + this.self = self; + } + + public bool IsActive() + { + // TODO: Check for nearby MRJ + + // Check if powered + var b = self.traits.Get(); + if (b != null && b.InsuffientPower()) + return false; + + return true; + } + } +} diff --git a/sequences.xml b/sequences.xml index a56029f90d..9e1f0e0ef5 100644 --- a/sequences.xml +++ b/sequences.xml @@ -403,6 +403,12 @@ + + + + + + diff --git a/units.ini b/units.ini index f8a62c64b1..38418d8b72 100644 --- a/units.ini +++ b/units.ini @@ -411,7 +411,7 @@ SpawnOffset=0,-4 LongDesc=Produces and reloads helicopters [DOME] Description=Radar Dome -Traits=Building, RenderBuilding +Traits=Building, RenderBuilding, ProvidesRadar Dimensions=2,2 Footprint=xx xx SelectionPriority=3