diff --git a/OpenRa.DataStructures/Tuple.cs b/OpenRa.DataStructures/Tuple.cs index ebd08dedf8..f5260fcfbb 100644 --- a/OpenRa.DataStructures/Tuple.cs +++ b/OpenRa.DataStructures/Tuple.cs @@ -37,4 +37,12 @@ namespace OpenRa public Tuple(A a, B b, C c, D d) { this.a = a; this.b = b; this.c = c; this.d = d; } } + + public static class Tuple + { + public static Tuple New(A a, B b, C c) + { + return new Tuple(a, b, c); + } + } } diff --git a/OpenRa.FileFormats/PaletteRemap.cs b/OpenRa.FileFormats/PaletteRemap.cs index f4d3b57c41..92fda46b50 100644 --- a/OpenRa.FileFormats/PaletteRemap.cs +++ b/OpenRa.FileFormats/PaletteRemap.cs @@ -8,7 +8,9 @@ namespace OpenRa.FileFormats { public class PaletteRemap { + int offset; List remapColors = new List(); + Color shadowColor; public PaletteRemap(Stream s) { @@ -23,14 +25,26 @@ namespace OpenRa.FileFormats remapColors.Add(Color.FromArgb(r, g, b)); } } + + offset = 80; + } + + public PaletteRemap( Color shadowColor ) + { + this.shadowColor = shadowColor; } public Color GetRemappedColor(Color original, int index) { - if (index < 80 || index >= 96) - return original; + if (remapColors.Count > 0) + { + if (index < offset || index >= offset + remapColors.Count) + return original; - return remapColors[index - 80]; + return remapColors[index - offset]; + } + + return original.A > 0 ? shadowColor : original; } } } diff --git a/OpenRa.Game/Actor.cs b/OpenRa.Game/Actor.cs index 76085ea89b..e5cbbeec93 100755 --- a/OpenRa.Game/Actor.cs +++ b/OpenRa.Game/Actor.cs @@ -59,9 +59,9 @@ namespace OpenRa.Game } public float2 CenterLocation; - public float2 SelectedSize { get { return Render().First().First.size; } } + public float2 SelectedSize { get { return Render().First().a.size; } } - public IEnumerable> Render() + public IEnumerable> Render() { return traits.WithInterface().SelectMany( x => x.Render( this ) ); } diff --git a/OpenRa.Game/Bullet.cs b/OpenRa.Game/Bullet.cs index 6c3e05d445..38d15a4c78 100644 --- a/OpenRa.Game/Bullet.cs +++ b/OpenRa.Game/Bullet.cs @@ -11,7 +11,7 @@ namespace OpenRa.Game interface IEffect { void Tick(); - IEnumerable> Render(); + IEnumerable> Render(); Player Owner { get; } } @@ -94,16 +94,32 @@ namespace OpenRa.Game foreach (var victim in hitActors) victim.InflictDamage(FiredBy, this, (int)GetDamageToInflict(victim)); } - } + } + + const float height = .1f; - public IEnumerable> Render() + public IEnumerable> Render() { if (anim != null) - yield return Pair.New(anim.Image, - float2.Lerp( + { + var pos = float2.Lerp( Src.ToFloat2(), VisualDest.ToFloat2(), - (float)t / TotalTime()) - 0.5f * anim.Image.size); + (float)t / TotalTime()) - 0.5f * anim.Image.size; + + if (Projectile.High || Projectile.Arcing) + { + if (Projectile.Shadow) + yield return Tuple.New(anim.Image, pos, 8); /* todo: shadow pal */ + + var at = (float)t / TotalTime(); + var highPos = pos - new float2(0, (VisualDest - Src).Length * height * 4 * at * (1 - at)); + + yield return Tuple.New(anim.Image, highPos, Owner.Palette); + } + else + yield return Tuple.New(anim.Image, pos, Owner.Palette); + } } float GetMaximumSpread() diff --git a/OpenRa.Game/Explosion.cs b/OpenRa.Game/Explosion.cs index 3e8fe08c78..1180031d3f 100644 --- a/OpenRa.Game/Explosion.cs +++ b/OpenRa.Game/Explosion.cs @@ -24,9 +24,9 @@ namespace OpenRa.Game public void Tick() { anim.Tick(); } - public IEnumerable> Render() + public IEnumerable> Render() { - yield return Pair.New(anim.Image, pos.ToFloat2() - 0.5f * anim.Image.size); + yield return Tuple.New(anim.Image, pos.ToFloat2() - 0.5f * anim.Image.size, 0); } public Player Owner { get { return null; } } diff --git a/OpenRa.Game/Game.cs b/OpenRa.Game/Game.cs index 9f371fdb52..4ddd12fec6 100644 --- a/OpenRa.Game/Game.cs +++ b/OpenRa.Game/Game.cs @@ -46,7 +46,8 @@ namespace OpenRa.Game Rules.LoadRules(mapName); for (int i = 0; i < 8; i++) - players.Add(i, new Player(i, i, string.Format("Multi{0}", i), Race.Allies)); + players.Add(i, new Player(i, i, string.Format("Multi{0}", i), + Race.Allies)); localPlayerIndex = localPlayer; @@ -84,8 +85,7 @@ namespace OpenRa.Game PlaySound("intro.aud", false); skipMakeAnims = false; - - sw = new Stopwatch(); + PerfHistory.items["render"].hasNormalTick = false; } static void LoadMapBuildings( IniFile mapfile ) @@ -143,11 +143,6 @@ namespace OpenRa.Game const int oreFrequency = 30; static int oreTicks = oreFrequency; public static int RenderFrame = 0; - public static double RenderTime = 0.0; - public static double TickTime = 0.0; - public static double OreTime = 0.0; - - public static Stopwatch sw; public static void Tick() { @@ -157,7 +152,6 @@ namespace OpenRa.Game { using (new PerfSample("tick_time")) { - sw.Reset(); lastTime += timestep; if (orderManager.Tick()) @@ -166,29 +160,27 @@ namespace OpenRa.Game controller.orderGenerator.Tick(); if (--oreTicks == 0) - { - var oresw = new Stopwatch(); - map.GrowOre(SharedRandom); - OreTime = oresw.ElapsedTime(); - oreTicks = oreFrequency; - } + using( new PerfSample("ore")) + map.GrowOre(SharedRandom); world.Tick(); UnitInfluence.Tick(); foreach (var player in players.Values) player.Tick(); } - - TickTime = sw.ElapsedTime(); } PerfHistory.Tick(); } - sw.Reset(); - ++RenderFrame; - viewport.cursor = controller.ChooseCursor(); - viewport.DrawRegions(); - RenderTime = sw.ElapsedTime(); + using (new PerfSample("render")) + { + + ++RenderFrame; + viewport.cursor = controller.ChooseCursor(); + viewport.DrawRegions(); + } + + PerfHistory.items["render"].Tick(); } public static bool IsCellBuildable(int2 a, UnitMovementType umt) diff --git a/OpenRa.Game/Graphics/HardwarePalette.cs b/OpenRa.Game/Graphics/HardwarePalette.cs index d76e423a0f..61a7ea9343 100644 --- a/OpenRa.Game/Graphics/HardwarePalette.cs +++ b/OpenRa.Game/Graphics/HardwarePalette.cs @@ -10,7 +10,7 @@ namespace OpenRa.Game.Graphics { class HardwarePalette : Sheet { - const int maxEntries = 8; + const int maxEntries = 16; int allocated = 0; public HardwarePalette(Renderer renderer, Map map, int rotate) @@ -22,6 +22,8 @@ namespace OpenRa.Game.Graphics foreach (string remap in new string[] { "blue", "red", "orange", "teal", "salmon", "green", "gray" }) AddPalette(new Palette(pal, new PaletteRemap(FileSystem.Open(remap + ".rem")))); + AddPalette(new Palette(pal, new PaletteRemap(Color.FromArgb(140, 0, 0, 0)))); + using (var bitmapCopy = new Bitmap(bitmap)) for (int j = 0; j < maxEntries; j++) for (int i = 0; i < 7; i++) diff --git a/OpenRa.Game/Graphics/Util.cs b/OpenRa.Game/Graphics/Util.cs index 2f3f11f181..d4d7ab60c7 100644 --- a/OpenRa.Game/Graphics/Util.cs +++ b/OpenRa.Game/Graphics/Util.cs @@ -58,7 +58,7 @@ namespace OpenRa.Game.Graphics public static void FastCreateQuad(Vertex[] vertices, ushort[] indices, float2 o, Sprite r, int palette, int nv, int ni) { - float2 attrib = new float2(palette / 8.0f, channelSelect[(int)r.channel]); + float2 attrib = new float2(palette / 16.0f, channelSelect[(int)r.channel]); vertices[nv] = new Vertex(KLerp(o, r.size, 0), r.FastMapTextureCoords(0), attrib); vertices[nv + 1] = new Vertex(KLerp(o, r.size, 1), r.FastMapTextureCoords(1), attrib); diff --git a/OpenRa.Game/Graphics/WorldRenderer.cs b/OpenRa.Game/Graphics/WorldRenderer.cs index ec9ba83177..d3c51dea07 100644 --- a/OpenRa.Game/Graphics/WorldRenderer.cs +++ b/OpenRa.Game/Graphics/WorldRenderer.cs @@ -35,82 +35,80 @@ namespace OpenRa.Game.Graphics lineRenderer = new LineRenderer(renderer); uiOverlay = new UiOverlay(spriteRenderer); } - - void DrawSpriteList(Player owner, RectangleF rect, - IEnumerable> images) - { - foreach (var image in images) - { - var loc = image.Second; - - if (loc.X > rect.Right || loc.X < rect.Left - - image.First.bounds.Width) - continue; - if (loc.Y > rect.Bottom || loc.Y < rect.Top - - image.First.bounds.Height) - continue; - - spriteRenderer.DrawSprite(image.First, loc, - (owner != null) ? owner.Palette : 0); - } - } - - public void Draw() + + void DrawSpriteList(RectangleF rect, + IEnumerable> images) { - terrainRenderer.Draw( Game.viewport ); - - var rect = new RectangleF((region.Position + Game.viewport.Location).ToPointF(), - region.Size.ToSizeF()); - - foreach (Actor a in Game.world.Actors.OrderBy( u => u.CenterLocation.Y )) - DrawSpriteList(a.Owner, rect, a.Render()); - - foreach (var a in Game.world.Actors - .Where(u => u.traits.Contains()) - .Select(u => u.traits.Get())) - DrawSpriteList(a.self.Owner, rect, a.RenderRoof(a.self)); /* RUDE HACK */ - - foreach (IEffect e in Game.world.Effects) - DrawSpriteList(e.Owner, rect, e.Render()); - - uiOverlay.Draw(); - - spriteRenderer.Flush(); - - var selbox = Game.controller.SelectionBox; - if (selbox != null) - { - var a = selbox.Value.First; - var b = new float2(selbox.Value.Second.X - a.X, 0); - var c = new float2(0, selbox.Value.Second.Y - a.Y); - - lineRenderer.DrawLine(a, a + b, Color.White, Color.White); - lineRenderer.DrawLine(a + b, a + b + c, Color.White, Color.White); - lineRenderer.DrawLine(a + b + c, a + c, Color.White, Color.White); - lineRenderer.DrawLine(a, a + c, Color.White, Color.White); - - foreach (var u in Game.SelectUnitsInBox(selbox.Value.First, selbox.Value.Second)) - DrawSelectionBox(u, Color.Yellow, false); - } - - var uog = Game.controller.orderGenerator as UnitOrderGenerator; - if (uog != null) - foreach( var a in uog.selection ) - DrawSelectionBox(a, Color.White, true); - - lineRenderer.Flush(); - - renderer.DrawText(string.Format("RenderFrame {0} ({2:F1} ms)\nTick {1} ({3:F1} ms)\nOre ({4:F1} ms)\n$ {5}\nPower {7}\nTiles Expanded {6:F0}", - Game.RenderFrame, Game.orderManager.FrameNumber, - Game.RenderTime * 1000, - Game.TickTime * 1000, - Game.OreTime * 1000, - Game.LocalPlayer.Cash, - PerfHistory.items[ "nodes_expanded" ].LastValue, - Game.LocalPlayer.Power + foreach (var image in images) + { + var loc = image.b; + + if (loc.X > rect.Right || loc.X < rect.Left + - image.a.bounds.Width) + continue; + if (loc.Y > rect.Bottom || loc.Y < rect.Top + - image.a.bounds.Height) + continue; + + spriteRenderer.DrawSprite(image.a, loc, image.c); + } + } + + public void Draw() + { + terrainRenderer.Draw(Game.viewport); + + var rect = new RectangleF((region.Position + Game.viewport.Location).ToPointF(), + region.Size.ToSizeF()); + + foreach (Actor a in Game.world.Actors.OrderBy(u => u.CenterLocation.Y)) + DrawSpriteList(rect, a.Render()); + + foreach (var a in Game.world.Actors + .Where(u => u.traits.Contains()) + .Select(u => u.traits.Get())) + DrawSpriteList(rect, a.RenderRoof(a.self)); /* RUDE HACK */ + + foreach (IEffect e in Game.world.Effects) + DrawSpriteList(rect, e.Render()); + + uiOverlay.Draw(); + + spriteRenderer.Flush(); + + var selbox = Game.controller.SelectionBox; + if (selbox != null) + { + var a = selbox.Value.First; + var b = new float2(selbox.Value.Second.X - a.X, 0); + var c = new float2(0, selbox.Value.Second.Y - a.Y); + + lineRenderer.DrawLine(a, a + b, Color.White, Color.White); + lineRenderer.DrawLine(a + b, a + b + c, Color.White, Color.White); + lineRenderer.DrawLine(a + b + c, a + c, Color.White, Color.White); + lineRenderer.DrawLine(a, a + c, Color.White, Color.White); + + foreach (var u in Game.SelectUnitsInBox(selbox.Value.First, selbox.Value.Second)) + DrawSelectionBox(u, Color.Yellow, false); + } + + var uog = Game.controller.orderGenerator as UnitOrderGenerator; + if (uog != null) + foreach (var a in uog.selection) + DrawSelectionBox(a, Color.White, true); + + lineRenderer.Flush(); + + renderer.DrawText(string.Format("RenderFrame {0} ({2:F1} ms)\nTick {1} ({3:F1} ms)\n$ {4}\nPower {5}", + Game.RenderFrame, + Game.orderManager.FrameNumber, + PerfHistory.items["render"].LastValue, + PerfHistory.items["tick_time"].LastValue, + Game.LocalPlayer.Cash, + Game.LocalPlayer.GetTotalPower() ), new int2(5, 5), Color.White); - PerfHistory.Render(renderer, lineRenderer); + PerfHistory.Render(renderer, lineRenderer); } void DrawSelectionBox(Actor selectedUnit, Color c, bool drawHealthBar) diff --git a/OpenRa.Game/MainWindow.cs b/OpenRa.Game/MainWindow.cs index a759587ef0..92b4f4067f 100755 --- a/OpenRa.Game/MainWindow.cs +++ b/OpenRa.Game/MainWindow.cs @@ -61,10 +61,13 @@ namespace OpenRa.Game Game.world.Add( new Actor( "mcv", Game.map.Offset + new int2( 5, 5 ), Game.players[ 1 ]) ); Game.world.Add( new Actor( "mcv", Game.map.Offset + new int2( 7, 5 ), Game.players[ 2 ] ) ); Game.world.Add( new Actor( "mcv", Game.map.Offset + new int2( 9, 5 ), Game.players[ 0 ] ) ); - Game.world.Add( new Actor( "jeep", Game.map.Offset + new int2( 9, 15 ), Game.players[ 1 ] ) ); + Game.world.Add( new Actor( "jeep", Game.map.Offset + new int2( 9, 14 ), Game.players[ 1 ] ) ); Game.world.Add( new Actor( "3tnk", Game.map.Offset + new int2( 12, 7 ), Game.players[ 1 ] ) ); + Game.world.Add(new Actor("apc", Game.map.Offset + new int2(13, 7), Game.players[1])); Game.world.Add(new Actor("ca", Game.map.Offset + new int2(40, 7), Game.players[1])); Game.world.Add(new Actor("e1", Game.map.Offset + new int2(9, 13), Game.players[1])); + Game.world.Add(new Actor("arty", Game.map.Offset + new int2(10, 13), Game.players[1])); + Game.world.Add(new Actor("heli", Game.map.Offset + new int2(11, 12), Game.players[1])); renderer.BuildPalette(Game.map); sidebar = new Sidebar(renderer, Game.LocalPlayer); diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj index f601117070..5cd94575af 100644 --- a/OpenRa.Game/OpenRa.Game.csproj +++ b/OpenRa.Game/OpenRa.Game.csproj @@ -153,6 +153,7 @@ + diff --git a/OpenRa.Game/Player.cs b/OpenRa.Game/Player.cs index c3b08fdc17..76d43c7e59 100644 --- a/OpenRa.Game/Player.cs +++ b/OpenRa.Game/Player.cs @@ -11,7 +11,8 @@ namespace OpenRa.Game public Race Race; public readonly int Index; public int Cash; - public int Power; + int powerProvided; + int powerDrained; public Player( int index, int palette, string playerName, Race race ) { @@ -20,7 +21,20 @@ namespace OpenRa.Game this.PlayerName = playerName; this.Race = race; this.Cash = 10000; - this.Power = 0; + this.powerProvided = this.powerDrained = 0; + } + + public void ChangePower(int dPower) + { + if (dPower > 0) + powerProvided += dPower; + if (dPower < 0) + powerDrained -= dPower; + } + + public int GetTotalPower() + { + return powerProvided - powerDrained; } public float GetSiloFullness() diff --git a/OpenRa.Game/Support/PerfHistory.cs b/OpenRa.Game/Support/PerfHistory.cs index 60954912e1..bad5cd80c0 100644 --- a/OpenRa.Game/Support/PerfHistory.cs +++ b/OpenRa.Game/Support/PerfHistory.cs @@ -29,7 +29,8 @@ namespace OpenRa.Game.Support public static void Tick() { foreach (var item in items.Values) - item.Tick(); + if (item.hasNormalTick) + item.Tick(); } public static void Render(Renderer r, LineRenderer lr) @@ -65,6 +66,7 @@ namespace OpenRa.Game.Support public double[] samples = new double[100]; public double val = 0.0; int head = 1, tail = 0; + public bool hasNormalTick = true; public PerfItem(string name, Color c) { diff --git a/OpenRa.Game/Traits/AttackTurreted.cs b/OpenRa.Game/Traits/AttackTurreted.cs index 0dda1090c1..e79f5ddc28 100755 --- a/OpenRa.Game/Traits/AttackTurreted.cs +++ b/OpenRa.Game/Traits/AttackTurreted.cs @@ -13,6 +13,8 @@ namespace OpenRa.Game.Traits protected int primaryFireDelay = 0; protected int secondaryFireDelay = 0; + public float primaryRecoil = 0.0f, secondaryRecoil = 0.0f; + public AttackBase(Actor self) { } protected bool CanAttack( Actor self ) @@ -25,6 +27,9 @@ namespace OpenRa.Game.Traits if (primaryFireDelay > 0) --primaryFireDelay; if (secondaryFireDelay > 0) --secondaryFireDelay; + primaryRecoil = Math.Max(0f, primaryRecoil - .2f); + secondaryRecoil = Math.Max(0f, secondaryRecoil - .2f); + if (target != null && target.IsDead) target = null; /* he's dead, jim. */ } @@ -36,20 +41,15 @@ namespace OpenRa.Game.Traits self.unitInfo.PrimaryOffset ) ) { secondaryFireDelay = Math.Max( 4, secondaryFireDelay ); - if (rut != null) rut.primaryRecoil = 1; + primaryRecoil = 1; return; } if (self.unitInfo.Secondary != null && CheckFire(self, self.unitInfo.Secondary, ref secondaryFireDelay, self.unitInfo.SecondaryOffset ?? self.unitInfo.PrimaryOffset)) { - if (rut != null) - { - if (self.unitInfo.SecondaryOffset != null) - rut.secondaryRecoil = 1; - else - rut.primaryRecoil = 1; - } + if (self.unitInfo.SecondaryOffset != null) secondaryRecoil = 1; + else primaryRecoil = 1; return; } } diff --git a/OpenRa.Game/Traits/Building.cs b/OpenRa.Game/Traits/Building.cs index 4f1890680a..0894e00baf 100644 --- a/OpenRa.Game/Traits/Building.cs +++ b/OpenRa.Game/Traits/Building.cs @@ -26,7 +26,7 @@ namespace OpenRa.Game.Traits UnitInfo.BuildingInfo bi = self.unitInfo as UnitInfo.BuildingInfo; if (bi == null) return; - self.Owner.Power += bi.Power; + self.Owner.ChangePower(bi.Power); } } } diff --git a/OpenRa.Game/Traits/InfantrySquad.cs b/OpenRa.Game/Traits/InfantrySquad.cs index d54e2c93a4..2a05288db7 100644 --- a/OpenRa.Game/Traits/InfantrySquad.cs +++ b/OpenRa.Game/Traits/InfantrySquad.cs @@ -35,11 +35,11 @@ namespace OpenRa.Game.Traits self.CenterLocation.ToInt2() + elementOffsets[elements.Count][i], self); } - public IEnumerable> Render(Actor self) + public IEnumerable> Render(Actor self) { return elements.Select( - e => Util.Centered(e.anim.Image, e.location)) - .OrderBy( a => a.Second.Y ); /* important to y-order elements of a squad! */ + e => Util.Centered(self, e.anim.Image, e.location)) + .OrderBy( a => a.b.Y ); /* important to y-order elements of a squad! */ } } diff --git a/OpenRa.Game/Traits/RallyPoint.cs b/OpenRa.Game/Traits/RallyPoint.cs index e71f33eeb7..c2527f29b9 100644 --- a/OpenRa.Game/Traits/RallyPoint.cs +++ b/OpenRa.Game/Traits/RallyPoint.cs @@ -21,11 +21,11 @@ namespace OpenRa.Game.Traits anim.PlayRepeating("idle"); } - public IEnumerable> Render(Actor self) + public IEnumerable> Render(Actor self) { var uog = Game.controller.orderGenerator as UnitOrderGenerator; if (uog != null && self.Owner == Game.LocalPlayer && uog.selection.Contains(self)) - yield return Util.Centered( + yield return Util.Centered( self, anim.Image, Game.CellSize * (new float2(.5f, .5f) + rallyPoint.ToFloat2())); } diff --git a/OpenRa.Game/Traits/RenderBuilding.cs b/OpenRa.Game/Traits/RenderBuilding.cs index 67468e3975..b8f46e28b6 100644 --- a/OpenRa.Game/Traits/RenderBuilding.cs +++ b/OpenRa.Game/Traits/RenderBuilding.cs @@ -64,9 +64,9 @@ namespace OpenRa.Game.Traits } } - public override IEnumerable> Render(Actor self) + public override IEnumerable> Render(Actor self) { - yield return Pair.New(anim.Image, 24f * (float2)self.Location); + yield return Tuple.New(anim.Image, 24f * (float2)self.Location, self.Owner.Palette); } public virtual void Damaged(Actor self, DamageState state) diff --git a/OpenRa.Game/Traits/RenderBuildingWarFactory.cs b/OpenRa.Game/Traits/RenderBuildingWarFactory.cs index 9bed59f2c7..e489a47482 100644 --- a/OpenRa.Game/Traits/RenderBuildingWarFactory.cs +++ b/OpenRa.Game/Traits/RenderBuildingWarFactory.cs @@ -29,10 +29,11 @@ namespace OpenRa.Game.Traits }, self); } - public IEnumerable> RenderRoof(Actor self) + public IEnumerable> RenderRoof(Actor self) { if (doneBuilding) - yield return Pair.New(roof.Image, 24f * (float2)self.Location); + yield return Tuple.New(roof.Image, + 24f * (float2)self.Location, self.Owner.Palette); } public override void Tick(Actor self) diff --git a/OpenRa.Game/Traits/RenderSimple.cs b/OpenRa.Game/Traits/RenderSimple.cs index 3a098b6a32..abee7174ad 100644 --- a/OpenRa.Game/Traits/RenderSimple.cs +++ b/OpenRa.Game/Traits/RenderSimple.cs @@ -16,7 +16,7 @@ namespace OpenRa.Game.Traits anim = new Animation(self.unitInfo.Image ?? self.unitInfo.Name); } - public abstract IEnumerable> Render(Actor self); + public abstract IEnumerable> Render(Actor self); public virtual void Tick(Actor self) { diff --git a/OpenRa.Game/Traits/RenderUnit.cs b/OpenRa.Game/Traits/RenderUnit.cs index efd2ac58ae..18f43284fb 100644 --- a/OpenRa.Game/Traits/RenderUnit.cs +++ b/OpenRa.Game/Traits/RenderUnit.cs @@ -29,11 +29,11 @@ namespace OpenRa.Game.Traits anim.PlayThen(newAnim, () => { PlayFacingAnim(self); if (after != null) after(); }); } - public override IEnumerable> Render(Actor self) + public override IEnumerable> Render(Actor self) { - yield return Util.Centered(anim.Image, self.CenterLocation); + yield return Util.Centered(self, anim.Image, self.CenterLocation); if (isSmoking) - yield return Util.Centered(smoke.Image, self.CenterLocation); + yield return Util.Centered(self, smoke.Image, self.CenterLocation); } bool isSmoking; diff --git a/OpenRa.Game/Traits/RenderUnitMuzzleFlash.cs b/OpenRa.Game/Traits/RenderUnitMuzzleFlash.cs new file mode 100644 index 0000000000..194cc8d2c8 --- /dev/null +++ b/OpenRa.Game/Traits/RenderUnitMuzzleFlash.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using OpenRa.Game.Graphics; + +namespace OpenRa.Game.Traits +{ + class RenderUnitMuzzleFlash : RenderUnit + { + Animation muzzleFlash; + + public RenderUnitMuzzleFlash(Actor self) + : base(self) + { + if (!self.unitInfo.MuzzleFlash) throw new InvalidOperationException("wtf??"); + + muzzleFlash = new Animation(self.unitInfo.Name); + muzzleFlash.PlayFetchIndex("muzzle", + () => + { + var attack = self.traits.WithInterface().First(); + var mobile = self.traits.WithInterface().First(); + return (Util.QuantizeFacing( + mobile.facing, 8)) * 6 + (int)(attack.primaryRecoil * 5.9f); + }); + } + + public override void Tick(Actor self) + { + base.Tick(self); + muzzleFlash.Tick(); + } + + public override IEnumerable> Render(Actor self) + { + var attack = self.traits.WithInterface().First(); + if (attack.primaryRecoil > 0) + return base.Render(self).Concat(new[] {Util.Centered(self, + muzzleFlash.Image, self.CenterLocation + new float2( + self.unitInfo.PrimaryOffset.ElementAtOrDefault(2), + self.unitInfo.PrimaryOffset.ElementAtOrDefault(3)))}); + else + return base.Render(self); + } + } +} diff --git a/OpenRa.Game/Traits/RenderUnitRotor.cs b/OpenRa.Game/Traits/RenderUnitRotor.cs index 4b446d7d8c..090b84ee60 100755 --- a/OpenRa.Game/Traits/RenderUnitRotor.cs +++ b/OpenRa.Game/Traits/RenderUnitRotor.cs @@ -23,18 +23,31 @@ namespace OpenRa.Game.Traits } } - public override IEnumerable> Render(Actor self) + public override IEnumerable> Render(Actor self) { var mobile = self.traits.Get(); - yield return Util.Centered(anim.Image, self.CenterLocation); - yield return Util.Centered(rotorAnim.Image, self.CenterLocation + yield return Util.CenteredShadow(self, anim.Image, self.CenterLocation); + yield return Util.CenteredShadow(self, rotorAnim.Image, self.CenterLocation + Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, 0)); if (self.unitInfo.SecondaryOffset != null) - yield return Util.Centered((secondRotorAnim ?? rotorAnim).Image, self.CenterLocation + yield return Util.CenteredShadow(self, (secondRotorAnim ?? rotorAnim).Image, self.CenterLocation + + Util.GetTurretPosition(self, self.unitInfo.SecondaryOffset, 0)); + + var p = self.CenterLocation - new float2( 0, altitude ); + + yield return Util.Centered(self, anim.Image, p); + yield return Util.Centered(self, rotorAnim.Image, p + + Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, 0)); + if (self.unitInfo.SecondaryOffset != null) + yield return Util.Centered(self, (secondRotorAnim ?? rotorAnim).Image, p + Util.GetTurretPosition(self, self.unitInfo.SecondaryOffset, 0)); } + int altitude = 0; + const int climbRate = 1; + const int cruiseAltitude = 20; + public override void Tick(Actor self) { base.Tick(self); @@ -44,12 +57,18 @@ namespace OpenRa.Game.Traits var mobile = self.traits.Get(); var isFlying = mobile.HasActivity; - if (isFlying ^ (rotorAnim.CurrentSequence.Name != "rotor")) + + if (isFlying && altitude < cruiseAltitude) + altitude = Math.Min(altitude + climbRate, cruiseAltitude); + else if (!isFlying && altitude > 0) + altitude = Math.Max(altitude - climbRate, 0); + + if ((altitude >0) ^ (rotorAnim.CurrentSequence.Name != "rotor")) return; - rotorAnim.PlayRepeatingPreservingPosition(isFlying ? "rotor" : "slow-rotor"); + rotorAnim.PlayRepeatingPreservingPosition((altitude>0) ? "rotor" : "slow-rotor"); if (secondRotorAnim != null) - secondRotorAnim.PlayRepeatingPreservingPosition(isFlying ? "rotor2" : "slow-rotor2"); + secondRotorAnim.PlayRepeatingPreservingPosition((altitude>0) ? "rotor2" : "slow-rotor2"); } } } diff --git a/OpenRa.Game/Traits/RenderUnitTurreted.cs b/OpenRa.Game/Traits/RenderUnitTurreted.cs index 130a3a0430..bcdac9a98d 100644 --- a/OpenRa.Game/Traits/RenderUnitTurreted.cs +++ b/OpenRa.Game/Traits/RenderUnitTurreted.cs @@ -11,7 +11,6 @@ namespace OpenRa.Game.Traits { public Animation turretAnim; public Animation muzzleFlash; - public float primaryRecoil = 0.0f, secondaryRecoil = 0.0f; public RenderUnitTurreted(Actor self) : base(self) @@ -21,9 +20,11 @@ namespace OpenRa.Game.Traits { if (self.unitInfo.MuzzleFlash) { + var attack = self.traits.WithInterface().First(); muzzleFlash = new Animation(self.unitInfo.Name); muzzleFlash.PlayFetchIndex("muzzle", - () => (Util.QuantizeFacing(self.traits.Get().turretFacing,8)) * 6 + (int)(primaryRecoil * 6)); + () => (Util.QuantizeFacing(self.traits.Get().turretFacing,8)) * 6 + (int)(attack.primaryRecoil * 5.9f)); + /* hack: recoil can be 1.0f, but don't overflow into next anim */ } turretAnim.PlayFetchIndex("turret", @@ -33,28 +34,27 @@ namespace OpenRa.Game.Traits turretAnim.PlayRepeating("turret"); /* not really a turret; it's a spinner */ } - public override IEnumerable> Render(Actor self) + public override IEnumerable> Render(Actor self) { var mobile = self.traits.Get(); + var attack = self.traits.WithInterface().FirstOrDefault(); - yield return Util.Centered(anim.Image, self.CenterLocation); - yield return Util.Centered(turretAnim.Image, self.CenterLocation - + Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, primaryRecoil)); + yield return Util.Centered(self, anim.Image, self.CenterLocation); + yield return Util.Centered(self, turretAnim.Image, self.CenterLocation + + Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, attack.primaryRecoil)); if (self.unitInfo.SecondaryOffset != null) - yield return Util.Centered(turretAnim.Image, self.CenterLocation - + Util.GetTurretPosition(self, self.unitInfo.SecondaryOffset, secondaryRecoil)); + yield return Util.Centered(self, turretAnim.Image, self.CenterLocation + + Util.GetTurretPosition(self, self.unitInfo.SecondaryOffset, attack.secondaryRecoil)); - if (muzzleFlash != null && primaryRecoil > 0) - yield return Util.Centered(muzzleFlash.Image, self.CenterLocation - + Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, primaryRecoil)); + if (muzzleFlash != null && attack.primaryRecoil > 0) + yield return Util.Centered(self, muzzleFlash.Image, self.CenterLocation + + Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, attack.primaryRecoil)); } public override void Tick(Actor self) { base.Tick(self); turretAnim.Tick(); - primaryRecoil = Math.Max(0f, primaryRecoil - .2f); - secondaryRecoil = Math.Max(0f, secondaryRecoil - .2f); if (muzzleFlash != null) muzzleFlash.Tick(); } diff --git a/OpenRa.Game/Traits/TraitsInterfaces.cs b/OpenRa.Game/Traits/TraitsInterfaces.cs index d2400acf00..cf3505afce 100644 --- a/OpenRa.Game/Traits/TraitsInterfaces.cs +++ b/OpenRa.Game/Traits/TraitsInterfaces.cs @@ -10,7 +10,7 @@ namespace OpenRa.Game.Traits enum DamageState { Normal, Half, Dead }; interface ITick { void Tick(Actor self); } - interface IRender { IEnumerable> Render(Actor self); } + interface IRender { IEnumerable> Render(Actor self); } interface IOrder { Order Order(Actor self, int2 xy, bool lmb, Actor underCursor); } interface INotifyDamage { void Damaged(Actor self, DamageState ds); } interface INotifyDamageEx : INotifyDamage { void Damaged(Actor self, int damage); } diff --git a/OpenRa.Game/Traits/Tree.cs b/OpenRa.Game/Traits/Tree.cs index bb6aa717bc..44cfd7c1a6 100644 --- a/OpenRa.Game/Traits/Tree.cs +++ b/OpenRa.Game/Traits/Tree.cs @@ -16,9 +16,9 @@ namespace OpenRa.Game.Traits Image = treeImage; } - public IEnumerable> Render(Actor self) + public IEnumerable> Render(Actor self) { - yield return Pair.New(Image, Game.CellSize * (float2)self.Location); + yield return Tuple.New(Image, Game.CellSize * (float2)self.Location, 0); } } } diff --git a/OpenRa.Game/Traits/Util.cs b/OpenRa.Game/Traits/Util.cs index b420a01c91..c7b3507869 100755 --- a/OpenRa.Game/Traits/Util.cs +++ b/OpenRa.Game/Traits/Util.cs @@ -81,7 +81,7 @@ namespace OpenRa.Game.Traits if (rut == null) return float2.Zero; var facing = self.traits.Get().turretFacing; - var quantizedFacing = facing - facing % rut.turretAnim.CurrentSequence.Length; + var quantizedFacing = QuantizeFacing(facing, rut.turretAnim.CurrentSequence.Length) * (256 / rut.turretAnim.CurrentSequence.Length); return RotateVectorByFacing(new float2(0, recoil * self.unitInfo.Recoil), quantizedFacing, .7f); } @@ -92,17 +92,22 @@ namespace OpenRa.Game.Traits if (ru == null) return int2.Zero; /* things that don't have a rotating base don't need the turrets repositioned */ var bodyFacing = self.traits.Get().facing; - var quantizedFacing = bodyFacing - bodyFacing % ru.anim.CurrentSequence.Length; + var quantizedFacing = QuantizeFacing(bodyFacing, ru.anim.CurrentSequence.Length) * (256 / ru.anim.CurrentSequence.Length); return (RotateVectorByFacing(new float2(offset[0], offset[1]), quantizedFacing, .7f) + GetRecoil(self, recoil)) + new float2(offset.ElementAtOrDefault(2), offset.ElementAtOrDefault(3)); } - public static Pair Centered(Sprite s, float2 location) + public static Tuple Centered(Actor self, Sprite s, float2 location) { var loc = location - 0.5f * s.size; - return Pair.New(s, loc.Round()); + return Tuple.New(s, loc.Round(), self.Owner.Palette); } + public static Tuple CenteredShadow(Actor self, Sprite s, float2 location) + { + var loc = location - 0.5f * s.size; + return Tuple.New(s, loc.Round(), 8); + } } } diff --git a/sequences.xml b/sequences.xml index e3b88530a2..cc38b910b7 100644 --- a/sequences.xml +++ b/sequences.xml @@ -275,6 +275,7 @@ + diff --git a/units.ini b/units.ini index 1f194060e3..5fd9cd1c0a 100755 --- a/units.ini +++ b/units.ini @@ -15,7 +15,7 @@ MNLY [V2RL] Description=V2 Rocket -Traits=Mobile, RenderUnit +Traits=Mobile, RenderUnit, AttackBase [1TNK] Description=Light Tank Traits=Mobile, Turreted, AttackTurreted, RenderUnitTurreted @@ -55,7 +55,9 @@ PrimaryOffset=0,0,0,-2 MuzzleFlash=yes [APC] Description=Armored Personnel Carrier -Traits=Mobile, RenderUnit, AttackBase +Traits=Mobile, AttackBase, RenderUnitMuzzleFlash +PrimaryOffset=0,0,0,-4 +MuzzleFlash=yes [MNLY] Description=Minelayer Traits=Mobile, RenderUnit