diff --git a/OpenRa.FileFormats/IPaletteRemap.cs b/OpenRa.FileFormats/IPaletteRemap.cs new file mode 100644 index 0000000000..321596490a --- /dev/null +++ b/OpenRa.FileFormats/IPaletteRemap.cs @@ -0,0 +1,9 @@ +using System; +using System.Drawing; +namespace OpenRa.FileFormats +{ + public interface IPaletteRemap + { + Color GetRemappedColor(Color original, int index); + } +} diff --git a/OpenRa.FileFormats/OpenRa.FileFormats.csproj b/OpenRa.FileFormats/OpenRa.FileFormats.csproj index ce057e867c..26f9983855 100644 --- a/OpenRa.FileFormats/OpenRa.FileFormats.csproj +++ b/OpenRa.FileFormats/OpenRa.FileFormats.csproj @@ -55,6 +55,7 @@ + @@ -62,6 +63,7 @@ + diff --git a/OpenRa.FileFormats/Palette.cs b/OpenRa.FileFormats/Palette.cs index 6244913786..5016372a8a 100644 --- a/OpenRa.FileFormats/Palette.cs +++ b/OpenRa.FileFormats/Palette.cs @@ -31,7 +31,7 @@ namespace OpenRa.FileFormats colors[4] = Color.FromArgb(140, 0, 0, 0); } - public Palette(Palette p, PaletteRemap r) + public Palette(Palette p, IPaletteRemap r) { for (int i = 0; i < 256; i++) colors.Add(r.GetRemappedColor(p.GetColor(i), i)); diff --git a/OpenRa.FileFormats/PaletteRemap.cs b/OpenRa.FileFormats/PaletteRemap.cs index ecd6de41cb..635c0d5653 100644 --- a/OpenRa.FileFormats/PaletteRemap.cs +++ b/OpenRa.FileFormats/PaletteRemap.cs @@ -4,7 +4,7 @@ using System.IO; namespace OpenRa.FileFormats { - public class PaletteRemap + public class PaletteRemap : IPaletteRemap { int offset; List remapColors = new List(); diff --git a/OpenRa.FileFormats/ShroudPaletteRemap.cs b/OpenRa.FileFormats/ShroudPaletteRemap.cs new file mode 100644 index 0000000000..be08298412 --- /dev/null +++ b/OpenRa.FileFormats/ShroudPaletteRemap.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Drawing; + +namespace OpenRa.FileFormats +{ + public class ShroudPaletteRemap : IPaletteRemap + { + public Color GetRemappedColor(Color original, int index) + { + // false-color version for debug + + //return new[] { + // Color.Orange, Color.Green, + // Color.Blue, Color.Yellow, + // Color.Black, + // Color.Red, + // Color.Purple, + // Color.Cyan}[index % 8]; + + return new[] { + Color.Transparent, Color.Green, + Color.Blue, Color.Yellow, + Color.Black, + Color.FromArgb(192,0,0,0), + Color.FromArgb(128,0,0,0), + Color.FromArgb(64,0,0,0)}[index % 8]; + } + } +} diff --git a/OpenRa.Game/Actor.cs b/OpenRa.Game/Actor.cs index 83b1ece4c6..3d99808bbc 100755 --- a/OpenRa.Game/Actor.cs +++ b/OpenRa.Game/Actor.cs @@ -123,6 +123,7 @@ namespace OpenRa.Game } public bool IsDead { get { return Health <= 0; } } + public bool IsInWorld { get; set; } public DamageState GetDamageState() { diff --git a/OpenRa.Game/Chrome.cs b/OpenRa.Game/Chrome.cs index 10513861fb..5ebd186d6e 100644 --- a/OpenRa.Game/Chrome.cs +++ b/OpenRa.Game/Chrome.cs @@ -132,6 +132,8 @@ namespace OpenRa.Game PerfHistory.Render(renderer, Game.worldRenderer.lineRenderer); + Game.minimap.Draw(new float2(Game.viewport.Width - 256, 8)); + chromeRenderer.DrawSprite(specialBinSprite, float2.Zero, PaletteType.Chrome); chromeRenderer.DrawSprite(moneyBinSprite, new float2(Game.viewport.Width - 320, 0), PaletteType.Chrome); @@ -143,8 +145,6 @@ namespace OpenRa.Game int paletteHeight = DrawBuildPalette(currentTab); DrawBuildTabs(paletteHeight); DrawChat(); - - Game.minimap.Draw(new float2(Game.viewport.Width - 128,30)); } void AddButton(Rectangle r, Action b) { buttons.Add(Pair.New(r, b)); } diff --git a/OpenRa.Game/Game.cs b/OpenRa.Game/Game.cs index cb2e3fed1b..c4b1e52605 100644 --- a/OpenRa.Game/Game.cs +++ b/OpenRa.Game/Game.cs @@ -59,6 +59,11 @@ namespace OpenRa.Game palette = new HardwarePalette(renderer, Rules.Map); world = new World(); + Game.world.ActorAdded += a => + { + if (a.Owner != null && a.Info != null) + a.Owner.Shroud.Explore(a); + }; for (int i = 0; i < 8; i++) { @@ -100,7 +105,7 @@ namespace OpenRa.Game chrome = new Chrome(renderer); - oreFrequency = (int)(Rules.General.GrowthRate * 60 * 1000); + oreFrequency = (int)(Rules.General.GrowthRate * 60 * 25); oreTicks = oreFrequency; } @@ -182,11 +187,12 @@ namespace OpenRa.Game controller.orderGenerator.Tick(); if (--oreTicks == 0) - { using (new PerfSample("ore")) + { Rules.Map.GrowOre(SharedRandom); - oreTicks = oreFrequency; - } + minimap.InvalidateOre(); + oreTicks = oreFrequency; + } world.Tick(); UnitInfluence.Tick(); diff --git a/OpenRa.Game/GameRules/UnitInfo.cs b/OpenRa.Game/GameRules/UnitInfo.cs index 8401ae04ef..63acbe1870 100755 --- a/OpenRa.Game/GameRules/UnitInfo.cs +++ b/OpenRa.Game/GameRules/UnitInfo.cs @@ -55,13 +55,15 @@ namespace OpenRa.Game.GameRules public readonly int OrePips = 0; public readonly string Icon = null; public readonly int[] SelectionSize = null; + public readonly int Passengers = 0; + public readonly int UnloadFacing = 0; + public readonly UnitMovementType[] PassengerTypes = null; public UnitInfo(string name) { Name = name; } } public class MobileInfo : UnitInfo { - public readonly int Passengers = 0; public readonly int Speed = 0; public readonly bool NoMovingFire = false; public readonly string Voice = "GenericVoice"; diff --git a/OpenRa.Game/Graphics/HardwarePalette.cs b/OpenRa.Game/Graphics/HardwarePalette.cs index 9c0538e61f..2092291dd0 100644 --- a/OpenRa.Game/Graphics/HardwarePalette.cs +++ b/OpenRa.Game/Graphics/HardwarePalette.cs @@ -3,7 +3,12 @@ using OpenRa.FileFormats; namespace OpenRa.Game.Graphics { - public enum PaletteType { Gold, Blue, Red, Orange, Teal, Salmon, Green, Gray, Shadow, Invuln, Chrome }; + public enum PaletteType + { + Gold, Blue, Red, Orange, Teal, Salmon, Green, Gray, + Shadow, Invuln, Chrome, Shroud, + }; + class HardwarePalette : Sheet { const int maxEntries = 16; @@ -21,6 +26,7 @@ namespace OpenRa.Game.Graphics AddPalette(new Palette(pal, new PaletteRemap(Color.FromArgb(140, 0, 0, 0)))); AddPalette(pal); // iron curtain. todo: remap! AddPalette(pal); // chrome (it's like gold, but we're not going to hax it in palettemods) + AddPalette(new Palette(pal, new ShroudPaletteRemap())); } int AddPalette(Palette p) diff --git a/OpenRa.Game/Graphics/Minimap.cs b/OpenRa.Game/Graphics/Minimap.cs index a8dfcebf4c..fbb13f3ef9 100644 --- a/OpenRa.Game/Graphics/Minimap.cs +++ b/OpenRa.Game/Graphics/Minimap.cs @@ -1,6 +1,7 @@ using System.Drawing; using System.Linq; using OpenRa.Game.Traits; +using OpenRa.FileFormats; namespace OpenRa.Game.Graphics { @@ -9,7 +10,7 @@ namespace OpenRa.Game.Graphics Sheet sheet; SpriteRenderer spriteRenderer; Sprite sprite; - Bitmap terrain; + Bitmap terrain, oreLayer; public void Tick() { } @@ -21,20 +22,29 @@ namespace OpenRa.Game.Graphics } // todo: extract these from the palette - static readonly Color[] terrainTypeColors = { - Color.Green, - Color.Red, - Color.Blue, - Color.Yellow, - Color.Purple, - Color.Turquoise, - Color.Violet, - Color.Tomato, - Color.Teal, - }; + Color[] terrainTypeColors; + + public void InvalidateOre() { oreLayer = null; } public void Update() { + if (terrainTypeColors == null) + { + var pal = new Palette(FileSystem.Open(Rules.Map.Theater + ".pal")); + terrainTypeColors = new[] { + pal.GetColor(0x1a), + pal.GetColor(0x63), + pal.GetColor(0x2f), + pal.GetColor(0x1f), + pal.GetColor(0x14), + pal.GetColor(0x64), + pal.GetColor(0x1f), + pal.GetColor(0x68), + pal.GetColor(0x6b), + pal.GetColor(0x6d), + }; + } + if (terrain == null) { terrain = new Bitmap(128, 128); @@ -45,14 +55,23 @@ namespace OpenRa.Game.Graphics : Color.Black); } - var bitmap = new Bitmap(terrain); + if (oreLayer == null) + { + oreLayer = new Bitmap(terrain); + for (var y = 0; y < 128; y++) + for (var x = 0; x < 128; x++) + if (Rules.Map.ContainsResource(new int2(x, y))) + oreLayer.SetPixel(x, y, terrainTypeColors[(int)TerrainMovementType.Ore]); + } + + var bitmap = new Bitmap(oreLayer); for( var y = 0; y < 128; y++ ) for (var x = 0; x < 128; x++) { var b = Game.BuildingInfluence.GetBuildingAt(new int2(x, y)); if (b != null) - bitmap.SetPixel(x, y, b.Owner != null ? Chat.paletteColors[(int)b.Owner.Palette] : Color.Gray); + bitmap.SetPixel(x, y, b.Owner != null ? Chat.paletteColors[(int)b.Owner.Palette] : terrainTypeColors[4]); } foreach (var a in Game.world.Actors.Where(a => a.traits.Contains())) @@ -63,7 +82,7 @@ namespace OpenRa.Game.Graphics public void Draw(float2 pos) { - spriteRenderer.DrawSprite(sprite, pos, PaletteType.Chrome); + spriteRenderer.DrawSprite(sprite, pos, PaletteType.Chrome, new float2(256,256)); spriteRenderer.Flush(); } } diff --git a/OpenRa.Game/Graphics/SpriteRenderer.cs b/OpenRa.Game/Graphics/SpriteRenderer.cs index 87e28c693f..3074c43a0f 100644 --- a/OpenRa.Game/Graphics/SpriteRenderer.cs +++ b/OpenRa.Game/Graphics/SpriteRenderer.cs @@ -55,12 +55,17 @@ namespace OpenRa.Game.Graphics } public void DrawSprite(Sprite s, float2 location, PaletteType palette) + { + DrawSprite(s, location, palette, s.size); + } + + public void DrawSprite(Sprite s, float2 location, PaletteType palette, float2 size) { if (s.sheet != currentSheet) Flush(); currentSheet = s.sheet; - Util.FastCreateQuad(vertices, indices, location.ToInt2(), s, (int) palette, nv, ni); + Util.FastCreateQuad(vertices, indices, location.ToInt2(), s, (int)palette, nv, ni, size); nv += 4; ni += 6; if (++sprites >= spritesPerBatch) Flush(); diff --git a/OpenRa.Game/Graphics/TerrainRenderer.cs b/OpenRa.Game/Graphics/TerrainRenderer.cs index 07d4263984..1032c3b0a0 100644 --- a/OpenRa.Game/Graphics/TerrainRenderer.cs +++ b/OpenRa.Game/Graphics/TerrainRenderer.cs @@ -34,7 +34,7 @@ namespace OpenRa.Game.Graphics for( int i = map.XOffset ; i < map.XOffset + map.Width; i++ ) { Sprite tile = tileMapping[map.MapTiles[i, j]]; - Util.FastCreateQuad(vertices, indices, Game.CellSize * new float2(i, j), tile, 0, nv, ni); + Util.FastCreateQuad(vertices, indices, Game.CellSize * new float2(i, j), tile, 0, nv, ni, tile.size); nv += 4; ni += 6; } diff --git a/OpenRa.Game/Graphics/Util.cs b/OpenRa.Game/Graphics/Util.cs index 97ad03da94..85dada816d 100644 --- a/OpenRa.Game/Graphics/Util.cs +++ b/OpenRa.Game/Graphics/Util.cs @@ -57,14 +57,14 @@ namespace OpenRa.Game.Graphics static float[] channelSelect = { 0.75f, 0.25f, -0.25f, -0.75f }; - public static void FastCreateQuad(Vertex[] vertices, ushort[] indices, float2 o, Sprite r, int palette, int nv, int ni) + public static void FastCreateQuad(Vertex[] vertices, ushort[] indices, float2 o, Sprite r, int palette, int nv, int ni, float2 size) { 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); - vertices[nv + 2] = new Vertex(KLerp(o, r.size, 2), r.FastMapTextureCoords(2), attrib); - vertices[nv + 3] = new Vertex(KLerp(o, r.size, 3), r.FastMapTextureCoords(3), attrib); + vertices[nv] = new Vertex(KLerp(o, size, 0), r.FastMapTextureCoords(0), attrib); + vertices[nv + 1] = new Vertex(KLerp(o, size, 1), r.FastMapTextureCoords(1), attrib); + vertices[nv + 2] = new Vertex(KLerp(o, size, 2), r.FastMapTextureCoords(2), attrib); + vertices[nv + 3] = new Vertex(KLerp(o, size, 3), r.FastMapTextureCoords(3), attrib); indices[ni] = (ushort)(nv); indices[ni + 1] = indices[ni + 3] = (ushort)(nv + 1); diff --git a/OpenRa.Game/Graphics/WorldRenderer.cs b/OpenRa.Game/Graphics/WorldRenderer.cs index 14bbff4afe..157b9ee2ab 100644 --- a/OpenRa.Game/Graphics/WorldRenderer.cs +++ b/OpenRa.Game/Graphics/WorldRenderer.cs @@ -87,6 +87,8 @@ namespace OpenRa.Game.Graphics if (Game.controller.orderGenerator != null) Game.controller.orderGenerator.Render(); + Game.LocalPlayer.Shroud.Draw(spriteRenderer); + lineRenderer.Flush(); spriteRenderer.Flush(); } @@ -211,7 +213,7 @@ namespace OpenRa.Game.Graphics foreach (var pips in selectedUnit.traits.WithInterface()) { - foreach (var pip in pips.GetPips()) + foreach (var pip in pips.GetPips(selectedUnit)) { var pipImages = new Animation("pips"); pipImages.PlayRepeating(pipStrings[(int)pip]); diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj index 72f4caec37..3c69270e79 100644 --- a/OpenRa.Game/OpenRa.Game.csproj +++ b/OpenRa.Game/OpenRa.Game.csproj @@ -110,6 +110,7 @@ + @@ -118,6 +119,7 @@ + @@ -185,6 +187,7 @@ + @@ -198,6 +201,7 @@ + @@ -213,6 +217,7 @@ + diff --git a/OpenRa.Game/Orders/UnitOrderGenerator.cs b/OpenRa.Game/Orders/UnitOrderGenerator.cs index ec64b5e9c6..dc4019ceea 100644 --- a/OpenRa.Game/Orders/UnitOrderGenerator.cs +++ b/OpenRa.Game/Orders/UnitOrderGenerator.cs @@ -27,7 +27,7 @@ namespace OpenRa.Game.Orders public void Tick() { - selection.RemoveAll(a => a.IsDead); + selection.RemoveAll(a => !a.IsInWorld); } public void Render() @@ -81,6 +81,7 @@ namespace OpenRa.Game.Orders else return Cursor.MoveBlocked; case "Enter": return Cursor.Enter; + case "EnterTransport": return Cursor.Enter; case "Deliver": return Cursor.Enter; case "Infiltrate": return Cursor.Enter; case "Capture": return Cursor.Capture; diff --git a/OpenRa.Game/Player.cs b/OpenRa.Game/Player.cs index 5e40e92b32..90d6a9f57b 100644 --- a/OpenRa.Game/Player.cs +++ b/OpenRa.Game/Player.cs @@ -17,15 +17,17 @@ namespace OpenRa.Game public string InternalName; public Race Race; public readonly int Index; - public int Cash; - public int Ore; + public int Cash = 10000; + public int Ore = 0; public int OreCapacity; - public int DisplayCash; - public int PowerProvided; - public int PowerDrained; + public int DisplayCash = 0; + public int PowerProvided = 0; + public int PowerDrained = 0; public bool IsReady; + public Shroud Shroud = new Shroud(); + public Player( Actor playerActor, int index, PaletteType palette, string playerName, Race race, string internalName ) { this.PlayerActor = playerActor; @@ -34,10 +36,6 @@ namespace OpenRa.Game this.InternalName = internalName; this.PlayerName = playerName; this.Race = race; - this.Cash = 10000; - this.Ore = 0; - this.DisplayCash = 0; - this.PowerProvided = this.PowerDrained = 0; } void UpdatePower() diff --git a/OpenRa.Game/Shroud.cs b/OpenRa.Game/Shroud.cs new file mode 100644 index 0000000000..831af634a2 --- /dev/null +++ b/OpenRa.Game/Shroud.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using IjwFramework.Types; +using OpenRa.Game.Graphics; + +namespace OpenRa.Game +{ + class Shroud + { + bool[,] explored = new bool[128, 128]; + Sprite[] shadowBits = SpriteSheetBuilder.LoadAllSprites("shadow"); + Sprite[,] sprites = new Sprite[128, 128]; + bool dirty; + + public void Explore(Actor a) + { + foreach (var t in Game.FindTilesInCircle((1f/Game.CellSize * a.CenterLocation).ToInt2(), a.Info.Sight)) + explored[t.X, t.Y] = true; + + dirty = true; + } + + Sprite ChooseShroud(int i, int j) + { + // bits are for exploredness: left, right, up, down, self + var n = new[] { + 0xf,0xf,0xf,0xf, + 0xf,0x0f,0x0f,0xf, + 0xf,0x0f,0x0f,0xf, + 0xf,0xf,0xf,0xf, + 0,7,13,0, + 14,6,12,4, + 11,3,9,1, + 0,2,8,0, + }; + + var v = 0; + if (explored[i-1,j]) v |= 1; + if (explored[i+1,j]) v |= 2; + if (explored[i,j-1]) v |= 4; + if (explored[i,j+1]) v |= 8; + if (explored[i, j]) v |= 16; + + var x = n[v]; + + if (x == 0) + { + // bits are for exploredness: TL, TR, BR, BL + var m = new[] { + 46, 41, 42, 38, + 43, 45, 39, 35, + 40, 37, 44, 34, + 36, 33, 32, 47, + }; + + var u = 0; + if (explored[i - 1, j - 1]) u |= 1; + if (explored[i + 1, j - 1]) u |= 2; + if (explored[i + 1, j + 1]) u |= 4; + if (explored[i - 1, j + 1]) u |= 8; + return shadowBits[m[u]]; + } + + return shadowBits[x]; + } + + public void Draw(SpriteRenderer r) + { + if (dirty) + { + dirty = false; + for (int j = 1; j < 127; j++) + for (int i = 1; i < 127; i++) + sprites[i, j] = ChooseShroud(i, j); + } + + for (var j = 0; j < 128; j++) + for (var i = 0; i < 128; i++) + if (sprites[i,j] != null) + r.DrawSprite(sprites[i, j], + Game.CellSize * new float2(i, j), + PaletteType.Shroud); + } + } +} diff --git a/OpenRa.Game/TerrainCosts.cs b/OpenRa.Game/TerrainCosts.cs index 78f17a96ac..7c4ffc9dfb 100644 --- a/OpenRa.Game/TerrainCosts.cs +++ b/OpenRa.Game/TerrainCosts.cs @@ -2,7 +2,7 @@ using OpenRa.Game.Graphics; namespace OpenRa.Game { - enum UnitMovementType : byte + public enum UnitMovementType : byte { Foot = 0, Track = 1, diff --git a/OpenRa.Game/Traits/Activities/EnterTransport.cs b/OpenRa.Game/Traits/Activities/EnterTransport.cs new file mode 100644 index 0000000000..daa875173d --- /dev/null +++ b/OpenRa.Game/Traits/Activities/EnterTransport.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenRa.Game.Traits.Activities +{ + class EnterTransport : IActivity + { + public IActivity NextActivity { get; set; } + bool isCanceled; + public Actor transport; + + public EnterTransport(Actor self, Actor transport) + { + this.transport = transport; + } + + public IActivity Tick(Actor self) + { + if (isCanceled) return NextActivity; + if (transport == null || !transport.IsInWorld) return NextActivity; + + var cargo = transport.traits.Get(); + if (cargo.IsFull(transport)) + return NextActivity; + + cargo.Load(transport, self); + Game.world.AddFrameEndTask(w => w.Remove(self)); + + return this; + } + + public void Cancel(Actor self) { isCanceled = true; NextActivity = null; } + } +} diff --git a/OpenRa.Game/Traits/Activities/Turn.cs b/OpenRa.Game/Traits/Activities/Turn.cs index f07033d93e..1b81859e31 100755 --- a/OpenRa.Game/Traits/Activities/Turn.cs +++ b/OpenRa.Game/Traits/Activities/Turn.cs @@ -5,7 +5,7 @@ namespace OpenRa.Game.Traits.Activities { public IActivity NextActivity { get; set; } - public int desiredFacing; + int desiredFacing; public Turn( int desiredFacing ) { diff --git a/OpenRa.Game/Traits/Activities/UnloadCargo.cs b/OpenRa.Game/Traits/Activities/UnloadCargo.cs new file mode 100644 index 0000000000..b0af980ab5 --- /dev/null +++ b/OpenRa.Game/Traits/Activities/UnloadCargo.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenRa.Game.Traits.Activities +{ + class UnloadCargo : IActivity + { + public IActivity NextActivity { get; set; } + bool isCanceled; + + int2? ChooseExitTile(Actor self) + { + // is anyone still hogging this tile? + if (Game.UnitInfluence.GetUnitsAt(self.Location).Count() > 1) + return null; + + for (var i = -1; i < 2; i++) + for (var j = -1; j < 2; j++) + if ((i != 0 || j != 0) && + Game.IsCellBuildable(self.Location + new int2(i, j), + UnitMovementType.Foot)) + return self.Location + new int2(i, j); + + return null; + } + + public IActivity Tick(Actor self) + { + if (isCanceled) return NextActivity; + + // if we're a thing that can turn, turn to the + // right facing for the unload animation + var unit = self.traits.GetOrDefault(); + if (unit != null && unit.Facing != self.Info.UnloadFacing) + return new Turn(self.Info.UnloadFacing) { NextActivity = this }; + + // todo: handle the BS of open/close sequences, which are inconsistent, + // for reasons that probably make good sense to the westwood guys. + + var cargo = self.traits.Get(); + if (cargo.IsEmpty(self)) + return NextActivity; + + var ru = self.traits.WithInterface().FirstOrDefault(); + if (ru != null) + ru.PlayCustomAnimation(self, "unload", null); + + var exitTile = ChooseExitTile(self); + if (exitTile == null) + return this; + + var actor = cargo.Unload(self); + + Game.world.AddFrameEndTask(w => + { + w.Add(actor); + actor.traits.Get().TeleportTo(actor, self.Location); + actor.CancelActivity(); + actor.QueueActivity(new Move(exitTile.Value, 0)); + }); + + return this; + } + + public void Cancel(Actor self) { NextActivity = null; isCanceled = true; } + } +} diff --git a/OpenRa.Game/Traits/Cargo.cs b/OpenRa.Game/Traits/Cargo.cs new file mode 100644 index 0000000000..3d1f27cea0 --- /dev/null +++ b/OpenRa.Game/Traits/Cargo.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using OpenRa.Game.GameRules; +using OpenRa.Game.Traits.Activities; + +namespace OpenRa.Game.Traits +{ + class Cargo : IPips, IOrder + { + List cargo = new List(); + + public Cargo(Actor self) {} + + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + // todo: check if there is an unoccupied `land` tile adjacent + if (mi.Button == MouseButton.Right && underCursor == self && cargo.Count > 0) + { + var unit = underCursor.traits.GetOrDefault(); + if (unit != null && unit.Altitude > 0) return null; + + return new Order("Deploy", self, null, int2.Zero, null); + } + + return null; + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "Deploy") + { + // todo: eject the units + self.CancelActivity(); + self.QueueActivity(new UnloadCargo()); + } + } + + public bool IsFull(Actor self) + { + return cargo.Count == self.Info.Passengers; + } + + public bool IsEmpty(Actor self) + { + return cargo.Count == 0; + } + + public Actor Unload(Actor self) + { + var a = cargo[0]; + cargo.RemoveAt(0); + return a; + } + + public IEnumerable GetPips( Actor self ) + { + for (var i = 0; i < self.Info.Passengers; i++) + if (i >= cargo.Count) + yield return PipType.Transparent; + else + yield return GetPipForPassenger(cargo[i]); + } + + static PipType GetPipForPassenger(Actor a) + { + // probably not actually right yet; fix to match real-ra + + if (a.traits.Contains()) + return PipType.Yellow; + if (!a.traits.WithInterface().Any()) + return PipType.Yellow; // noncombat [E6,SPY,THF] + if (a.traits.Contains()) + return PipType.Red; // E7 + + return PipType.Green; + } + + public void Load(Actor self, Actor a) + { + cargo.Add(a); + } + } +} diff --git a/OpenRa.Game/Traits/ChronoshiftDeploy.cs b/OpenRa.Game/Traits/ChronoshiftDeploy.cs index cd02343320..7c4252fc97 100644 --- a/OpenRa.Game/Traits/ChronoshiftDeploy.cs +++ b/OpenRa.Game/Traits/ChronoshiftDeploy.cs @@ -55,7 +55,7 @@ namespace OpenRa.Game.Traits } // Display 5 pips indicating the current charge status - public IEnumerable GetPips() + public IEnumerable GetPips(Actor self) { const int numPips = 5; for (int i = 0; i < numPips; i++) diff --git a/OpenRa.Game/Traits/Harvester.cs b/OpenRa.Game/Traits/Harvester.cs index ef535cd876..f68f52f008 100644 --- a/OpenRa.Game/Traits/Harvester.cs +++ b/OpenRa.Game/Traits/Harvester.cs @@ -57,7 +57,7 @@ namespace OpenRa.Game.Traits } } - public IEnumerable GetPips() + public IEnumerable GetPips(Actor self) { const int numPips = 7; for (int i = 0; i < numPips; i++) diff --git a/OpenRa.Game/Traits/LimitedAmmo.cs b/OpenRa.Game/Traits/LimitedAmmo.cs index 2a1a938450..b5c18c55a2 100644 --- a/OpenRa.Game/Traits/LimitedAmmo.cs +++ b/OpenRa.Game/Traits/LimitedAmmo.cs @@ -23,7 +23,7 @@ namespace OpenRa.Game.Traits public void Attacking(Actor self) { --ammo; } - public IEnumerable GetPips() + public IEnumerable GetPips(Actor self) { return Graphics.Util.MakeArray(self.Info.Ammo, i => ammo > i ? PipType.Green : PipType.Transparent); diff --git a/OpenRa.Game/Traits/Mobile.cs b/OpenRa.Game/Traits/Mobile.cs index 4bf3a76e1f..e917df2610 100644 --- a/OpenRa.Game/Traits/Mobile.cs +++ b/OpenRa.Game/Traits/Mobile.cs @@ -18,7 +18,16 @@ namespace OpenRa.Game.Traits public int2 toCell { get { return self.Location; } - set { Game.UnitInfluence.Remove(self, this); self.Location = value; Game.UnitInfluence.Add(self, this); } + set + { + if (self.Location != value) + { + Game.UnitInfluence.Remove(self, this); + self.Location = value; + self.Owner.Shroud.Explore(self); + } + Game.UnitInfluence.Add(self, this); + } } public Mobile(Actor self) diff --git a/OpenRa.Game/Traits/Passenger.cs b/OpenRa.Game/Traits/Passenger.cs new file mode 100644 index 0000000000..29521ecb09 --- /dev/null +++ b/OpenRa.Game/Traits/Passenger.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using OpenRa.Game.Traits.Activities; + +namespace OpenRa.Game.Traits +{ + class Passenger : IOrder + { + public Passenger(Actor self) { } + + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button != MouseButton.Right) + return null; + + if (underCursor == null || underCursor.Owner != self.Owner) + return null; + + var cargo = underCursor.traits.GetOrDefault(); + if (cargo == null || cargo.IsFull(underCursor)) + return null; + + var umt = self.traits.WithInterface().First().GetMovementType(); + if (!underCursor.Info.PassengerTypes.Contains(umt)) + return null; + + return new Order("EnterTransport", self, underCursor, int2.Zero, null); + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "EnterTransport") + { + self.CancelActivity(); + self.QueueActivity(new Move(order.TargetActor.Location, 1)); + self.QueueActivity(new EnterTransport(self, order.TargetActor)); + } + } + } +} diff --git a/OpenRa.Game/Traits/StoresOre.cs b/OpenRa.Game/Traits/StoresOre.cs index cd2d5210a8..25d16af2d0 100644 --- a/OpenRa.Game/Traits/StoresOre.cs +++ b/OpenRa.Game/Traits/StoresOre.cs @@ -11,7 +11,7 @@ namespace OpenRa.Game.Traits this.self = self; } - public IEnumerable GetPips() + public IEnumerable GetPips(Actor self) { for (int i = 0; i < self.Info.OrePips; i++) { diff --git a/OpenRa.Game/Traits/TraitsInterfaces.cs b/OpenRa.Game/Traits/TraitsInterfaces.cs index fdf5a05cbd..de5bc49c19 100644 --- a/OpenRa.Game/Traits/TraitsInterfaces.cs +++ b/OpenRa.Game/Traits/TraitsInterfaces.cs @@ -29,7 +29,7 @@ namespace OpenRa.Game.Traits interface IDamageModifier { float GetDamageModifier(); } interface ISpeedModifier { float GetSpeedModifier(); } interface IPaletteModifier { void AdjustPalette(Bitmap b); } - interface IPips { IEnumerable GetPips(); } + interface IPips { IEnumerable GetPips(Actor self); } interface ITags { IEnumerable GetTags(); } interface IMovement { diff --git a/OpenRa.Game/World.cs b/OpenRa.Game/World.cs index dc55e86213..f8db600406 100644 --- a/OpenRa.Game/World.cs +++ b/OpenRa.Game/World.cs @@ -10,8 +10,19 @@ namespace OpenRa.Game List effects = new List(); List> frameEndActions = new List>(); - public void Add(Actor a) { actors.Add(a); ActorAdded(a); } - public void Remove(Actor a) { actors.Remove(a); ActorRemoved(a); } + public void Add(Actor a) + { + a.IsInWorld = true; + actors.Add(a); + ActorAdded(a); + } + + public void Remove(Actor a) + { + a.IsInWorld = false; + actors.Remove(a); + ActorRemoved(a); + } public void Add(IEffect b) { effects.Add(b); } public void Remove(IEffect b) { effects.Remove(b); } diff --git a/SequenceEditor/Program.cs b/SequenceEditor/Program.cs index cdef4b4285..90d01db517 100644 --- a/SequenceEditor/Program.cs +++ b/SequenceEditor/Program.cs @@ -83,7 +83,8 @@ namespace SequenceEditor Doc = new XmlDocument(); Doc.Load(XmlFilename); - Pal = new Palette(FileSystem.Open("temperat.pal")); + var tempPal = new Palette(FileSystem.Open("temperat.pal")); + Pal = new Palette(tempPal, new ShroudPaletteRemap()); UnitName = args.FirstOrDefault( x => !x.EndsWith(".xml") ); if (UnitName == null) diff --git a/doc/progress.txt b/doc/progress.txt index bcc268c1fb..cd612fc2cd 100644 --- a/doc/progress.txt +++ b/doc/progress.txt @@ -21,7 +21,7 @@ All tracked vehicles Light vehicles V2RL Works -APC Cargo doesn't work +APC Works MNLY Works MGG No gap MRJ No radar @@ -31,8 +31,7 @@ HARV Works ARTY Works Helicopters - - Return to base after attack doesnt work -TRAN Cargo doesn't work +TRAN Works HELI Works HIND Works @@ -47,5 +46,5 @@ CA Works SS Works DD depth charges don't work PT depth charges don't work -LST Cargo doesn't work +LST Works diff --git a/sequences.xml b/sequences.xml index f659cf186a..a56029f90d 100644 --- a/sequences.xml +++ b/sequences.xml @@ -283,6 +283,8 @@ + + @@ -376,7 +378,7 @@ - + @@ -536,6 +538,8 @@ + + @@ -572,6 +576,8 @@ + + @@ -1008,8 +1014,8 @@ - - - + + + \ No newline at end of file diff --git a/session.ini b/session.ini index acafbf8481..d63ac62a10 100644 --- a/session.ini +++ b/session.ini @@ -10,4 +10,4 @@ s2=Multi1,mcv,600,12505,0,Guard,None ;s2=Multi1,e3,600,12505,0,Guard,None s3=Multi3,mcv,600,2910,0,Guard,None ;s4=Multi1,ctnk,600,12506,Gaurd,None -s5=Multi1,pdox,600,12510,Gaurd,None \ No newline at end of file +s5=Multi1,apc,600,12510,Gaurd,None \ No newline at end of file diff --git a/units.ini b/units.ini index 5b0d90121e..f8a62c64b1 100644 --- a/units.ini +++ b/units.ini @@ -16,89 +16,91 @@ MNLY.AT [V2RL] Description=V2 Rocket -Traits=Unit, Mobile, AttackBase, RenderUnitReload, AutoTarget, Repairable, Chronoshiftable +Traits=Unit, Mobile, AttackBase, RenderUnitReload, AutoTarget, Repairable, Chronoshiftable, Passenger Voice=VehicleVoice LongDesc=Long-range rocket artillery.\n Strong vs Infantry, Buildings\n Weak vs Tanks, Aircraft [1TNK] Description=Light Tank -Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable +Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger Recoil=2 Voice=VehicleVoice LongDesc=Light Tank, good for scouting.\n Strong vs Light Vehicles\n Weak vs Tanks, Aircraft [2TNK] Description=Medium Tank -Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable +Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger Recoil=3 Voice=VehicleVoice LongDesc=Allied Main Battle Tank.\n Strong vs Tanks, Light Vehicles\n Weak vs Infantry, Aircraft [3TNK] Description=Heavy Tank -Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable +Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger Recoil=3 Voice=VehicleVoice LongDesc=Soviet Main Battle Tank, with dual cannons\n Strong vs Tanks, Light Vehicles\n Weak vs Infantry, Aircraft [4TNK] Description=Mammoth Tank -Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable +Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger Voice=VehicleVoice LongDesc=Big and slow tank, with anti-air capability.\n Strong vs Tanks, Aircraft\n Weak vs Infantry [ARTY] Description=Artillery -Traits=Unit, Mobile, AttackBase, RenderUnit, Explodes, AutoTarget, Repairable, Chronoshiftable +Traits=Unit, Mobile, AttackBase, RenderUnit, Explodes, AutoTarget, Repairable, Chronoshiftable, Passenger Voice=VehicleVoice LongDesc=Long-range artillery.\n Strong vs Infantry, Buildings\n Weak vs Tanks, Aircraft [JEEP] Description=Ranger -Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable +Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger PrimaryOffset=0,0,0,-2 MuzzleFlash=yes Voice=VehicleVoice LongDesc=Fast scout & anti-infantry vehicle.\n Strong vs Infantry\n Weak vs Tanks, Aircraft [APC] Description=Armored Personnel Carrier -Traits=Unit, Mobile, AttackBase, RenderUnitMuzzleFlash, AutoTarget, Repairable, Chronoshiftable +Traits=Unit, Mobile, AttackBase, RenderUnitMuzzleFlash, AutoTarget, Repairable, Chronoshiftable, Cargo, Passenger PrimaryOffset=0,0,0,-4 MuzzleFlash=yes Voice=VehicleVoice LongDesc=Tough infantry transport.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks, Aircraft +UnloadFacing=220 +PassengerTypes=Foot ;; non-combat vehicles [MRJ] Description=Radar Jammer -Traits=Unit, Mobile, RenderUnitSpinner, Repairable, Chronoshiftable +Traits=Unit, Mobile, RenderUnitSpinner, Repairable, Chronoshiftable, Passenger PrimaryOffset=0,4,0,-6 SelectionPriority=3 Voice=VehicleVoice LongDesc=Hides nearby units on the enemy's minimap.\n Unarmed [MGG] Description=Mobile Gap Generator -Traits=Unit, Mobile, RenderUnitSpinner, Repairable, Chronoshiftable +Traits=Unit, Mobile, RenderUnitSpinner, Repairable, Chronoshiftable, Passenger PrimaryOffset=0,6,0,-3 SelectionPriority=3 Voice=VehicleVoice LongDesc=Regenerates Fog of War in a small area \naround the unit.\n Unarmed [HARV] Description=Ore Truck -Traits=Harvester, Unit, Mobile, RenderUnit, Repairable, Chronoshiftable +Traits=Harvester, Unit, Mobile, RenderUnit, Repairable, Chronoshiftable, Passenger SelectionPriority=7 Voice=VehicleVoice LongDesc=Collects Ore and Gems for processing.\n Unarmed [MCV] Description=Mobile Construction Vehicle -Traits=Unit, Mobile, McvDeploy, RenderUnit, Repairable, Chronoshiftable +Traits=Unit, Mobile, McvDeploy, RenderUnit, Repairable, Chronoshiftable, Passenger SelectionPriority=3 Voice=VehicleVoice LongDesc=Deploys into another Construction Yard.\n Unarmed [MNLY.AP] Description=Minelayer (Anti-Personnel) -Traits=Unit, Mobile, RenderUnit, Minelayer, MineImmune, Repairable, LimitedAmmo, Chronoshiftable +Traits=Unit, Mobile, RenderUnit, Minelayer, MineImmune, Repairable, LimitedAmmo, Chronoshiftable, Passenger Voice=VehicleVoice LongDesc=Lays mines to destroy unwary enemy units.\n Unarmed Primary=MINP ;; temporary hack [MNLY.AT] Description=Minelayer (Anti-Tank) -Traits=Unit, Mobile, RenderUnit, Minelayer, MineImmune, Repairable, LimitedAmmo, Chronoshiftable +Traits=Unit, Mobile, RenderUnit, Minelayer, MineImmune, Repairable, LimitedAmmo, Chronoshiftable, Passenger Voice=VehicleVoice LongDesc=Lays mines to destroy unwary enemy units.\n Unarmed Primary=MINV ;; temporary hack @@ -138,8 +140,9 @@ Recoil=3 [LST] Description=Transport WaterBound=yes -Traits=Unit, Mobile, RenderUnit +Traits=Unit, Mobile, RenderUnit, Cargo LongDesc=General-purpose naval transport.\nCan carry infantry and tanks.\n Unarmed +PassengerTypes=Foot,Wheel,Track [PT] Description=Gunboat WaterBound=yes @@ -179,9 +182,10 @@ Description=Transport Helicopter RotorOffset=0,14,0,-4 RotorOffset2=0,-14,0,-2 BuiltAt=hpad -Traits=Unit, Helicopter, RenderUnitRotor, WithShadow +Traits=Unit, Helicopter, RenderUnitRotor, WithShadow, Cargo InitialFacing=20 LongDesc=Fast Infantry Transport Helicopter.\n Unarmed +PassengerTypes=Foot [HELI] Description=Longbow BuiltAt=hpad @@ -544,62 +548,62 @@ MEDI Description=Attack Dog BuiltAt=KENN Voice=DogVoice -Traits=Unit, Mobile, RenderInfantry ;; AttackBase, SquishByTank, AutoTarget, dog?? +Traits=Unit, Mobile, RenderInfantry, Passenger ;; AttackBase, SquishByTank, AutoTarget, dog?? LongDesc=Anti-infantry unit. Not fooled by the \nSpy's disguise.\n Strong vs Infantry\n Weak vs Vehicles SelectionSize=12,17,-1,-4 [E1] Description=Rifle Infantry -Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget +Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget, Passenger LongDesc=General-purpose infantry. Strong vs Infantry\n Weak vs Vehicles SelectionSize=12,17,0,-9 [E2] Description=Grenadier -Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget +Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget, Passenger FireDelay=15 PrimaryOffset=0,0,0,-13 LongDesc=Infantry armed with grenades. \n Strong vs Buildings, Infantry\n Weak vs Vehicles SelectionSize=12,17,0,-9 [E3] Description=Rocket Soldier -Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget +Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget, Passenger PrimaryOffset=0,0,0,-13 LongDesc=Anti-tank/Anti-aircraft infantry.\n Strong vs Tanks, Aircraft\n Weak vs Infantry SelectionSize=12,17,0,-9 [E4] Description=Flamethrower -Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget +Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget, Passenger FireDelay=8 PrimaryOffset=0,0,0,-7 LongDesc=Advanced Anti-infantry unit.\n Strong vs Infantry, Buildings\n Weak vs Vehicles SelectionSize=12,17,0,-9 [E6] Description=Engineer -Traits=Unit, Mobile, EngineerCapture, RenderInfantry, TakeCover, SquishByTank +Traits=Unit, Mobile, EngineerCapture, RenderInfantry, TakeCover, SquishByTank, Passenger Voice=EngineerVoice LongDesc=Infiltrates and captures enemy structures.\n Strong vs Nothing\n Weak vs Everything SelectionSize=12,17,0,-9 [SPY] Description=Spy Voice=SpyVoice -Traits=Unit, Mobile, RenderInfantry, TakeCover, SquishByTank +Traits=Unit, Mobile, RenderInfantry, TakeCover, SquishByTank, Passenger LongDesc=Infiltrates enemy structures to gather \nintelligence. Exact effect depends on the \nbuilding infiltrated.\n Strong vs Nothing\n Weak vs Everything\n Special Ability: Disguised SelectionSize=12,17,0,-9 [THF] Description=Thief Voice=ThiefVoice -Traits=Unit, Mobile, RenderInfantry, TakeCover, SquishByTank +Traits=Unit, Mobile, RenderInfantry, TakeCover, SquishByTank, Passenger LongDesc=Infiltrates enemy refineries & \nsilos, and steals money stored there.\n Unarmed SelectionSize=12,17,0,-9 [E7] Description=Tanya Voice=TanyaVoice -Traits=Unit, Mobile, RenderInfantry, C4Demolition, AttackBase, TakeCover, SquishByTank, AutoTarget +Traits=Unit, Mobile, RenderInfantry, C4Demolition, AttackBase, TakeCover, SquishByTank, AutoTarget, Passenger LongDesc=Elite commando infantry, armed with \ndual pistols and C4.\n Strong vs Infantry, Buildings\n Weak vs Vehicles\n Special Ability: Destroy Building with C4 SelectionSize=12,17,0,-9 [MEDI] Description=Medic Voice=MedicVoice -Traits=Unit, Mobile, RenderInfantry, AutoHeal, AttackBase, TakeCover, SquishByTank +Traits=Unit, Mobile, RenderInfantry, AutoHeal, AttackBase, TakeCover, SquishByTank, Passenger LongDesc=Heals nearby infantry.\n Strong vs Nothing\n Weak vs Everything SelectionSize=12,17,0,-9