diff --git a/.gitignore b/.gitignore index eddde75363..ac341d94d4 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,8 @@ mods/*/packages/*.[mM][iI][xX] # backup files by various editors *~ *.orig +\#* +.*.sw? # dependency DLLs (different for every platform!) cg.dll cgGL.dll @@ -32,8 +34,6 @@ cgGL.dll *.pidb *.userprefs -packaging/windows/*.exe - # osx crap .DS_Store @@ -52,8 +52,3 @@ OpenRA.Launcher.Mac/OpenRA.xcodeproj/*.mode1v3 *.config *.resources -# other crap -Logs/ -Replays/ -\#* -.*.sw? diff --git a/OpenRA.FileFormats/FieldLoader.cs b/OpenRA.FileFormats/FieldLoader.cs index a61990b5b3..d06fab6252 100755 --- a/OpenRA.FileFormats/FieldLoader.cs +++ b/OpenRA.FileFormats/FieldLoader.cs @@ -56,6 +56,11 @@ namespace OpenRA.FileFormats ret = GetValue( fieldName, fieldType, n[ 0 ].Value.Value ); return true; } + else if ( n.Count > 1 ) + { + throw new InvalidOperationException("The field {0} has multiple definitions:\n{1}" + .F(fieldName, n.Select(m => "\t- " + m.Location).JoinWith("\n"))); + } throw new InvalidOperationException( "TryGetValueFromYaml: unable to load field {0} (of type {1})".F( fieldName, fieldType ) ); } diff --git a/OpenRA.FileFormats/MiniYaml.cs b/OpenRA.FileFormats/MiniYaml.cs index 1807c05f77..1962bcfbce 100755 --- a/OpenRA.FileFormats/MiniYaml.cs +++ b/OpenRA.FileFormats/MiniYaml.cs @@ -105,11 +105,14 @@ namespace OpenRA.FileFormats levels.Add(new List()); var lineNo = 0; - foreach (var line in lines) + foreach (var ll in lines) { + var line = ll; ++lineNo; + if (line.Contains('#')) + line = line.Substring(0, line.IndexOf('#')).TrimEnd(' ', '\t'); var t = line.TrimStart(' ', '\t'); - if (t.Length == 0 || t[0] == '#') + if (t.Length == 0) continue; var level = line.Length - t.Length; var location = new MiniYamlNode.SourceLocation { Filename = filename, Line = lineNo }; diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index deed94a085..41638ce1b4 100755 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -166,7 +166,7 @@ namespace OpenRA { var isNetTick = LocalTick % NetTickScale == 0; - if (!isNetTick || orderManager.IsReadyForNextFrame) + if ((!isNetTick || orderManager.IsReadyForNextFrame) && !orderManager.GamePaused ) { ++orderManager.LocalFrameNumber; @@ -188,6 +188,8 @@ namespace OpenRA else if (orderManager.NetFrameNumber == 0) orderManager.LastTickTime = Environment.TickCount; + + viewport.Tick(); } } } diff --git a/OpenRA.Game/GameRules/Settings.cs b/OpenRA.Game/GameRules/Settings.cs index 4bbc9cdb19..2c748f7a22 100644 --- a/OpenRA.Game/GameRules/Settings.cs +++ b/OpenRA.Game/GameRules/Settings.cs @@ -19,6 +19,9 @@ using OpenRA.Server; namespace OpenRA.GameRules { + public enum MouseScrollType { Disabled, Standard, Inverted } + public enum SoundCashTicks { Disabled, Normal, Extreme } + public class ServerSettings { public string Name = "OpenRA Game"; @@ -26,8 +29,11 @@ namespace OpenRA.GameRules public int ExternalPort = 1234; public bool AdvertiseOnline = true; public string MasterServer = "http://master.open-ra.org/"; + public bool AllowUPnP = true; public bool AllowCheats = false; public string Map = null; + public string[] Ban = null; + public int TimeOut = 0; public ServerSettings() { } @@ -38,8 +44,11 @@ namespace OpenRA.GameRules ExternalPort = other.ExternalPort; AdvertiseOnline = other.AdvertiseOnline; MasterServer = other.MasterServer; + AllowUPnP = other.AllowUPnP; AllowCheats = other.AllowCheats; Map = other.Map; + Ban = other.Ban; + TimeOut = other.TimeOut; } } @@ -77,6 +86,8 @@ namespace OpenRA.GameRules public bool Repeat = false; public bool ShellmapMusic = true; public string Engine = "AL"; + + public SoundCashTicks SoundCashTickType = SoundCashTicks.Extreme; } public class PlayerSettings @@ -86,8 +97,6 @@ namespace OpenRA.GameRules public string LastServer = "localhost:1234"; } - public enum MouseScrollType { Disabled, Standard, Inverted } - public class GameSettings { public string[] Mods = { "ra" }; diff --git a/OpenRA.Game/Graphics/Sequence.cs b/OpenRA.Game/Graphics/Sequence.cs index 5b114e9d07..d8a0bfeceb 100644 --- a/OpenRA.Game/Graphics/Sequence.cs +++ b/OpenRA.Game/Graphics/Sequence.cs @@ -27,10 +27,9 @@ namespace OpenRA.Graphics public int Facings { get { return facings; } } public int Tick { get { return tick; } } - string srcOverride; public Sequence(string unit, string name, MiniYaml info) { - srcOverride = info.Value; + var srcOverride = info.Value; Name = name; var d = info.NodesDict; @@ -62,26 +61,6 @@ namespace OpenRA.Graphics info.Nodes[0].Location)); } - public MiniYaml Save() - { - var root = new List(); - - root.Add(new MiniYamlNode("Start", start.ToString())); - - if (length > 1 && (start != 0 || length != sprites.Length - start)) - root.Add(new MiniYamlNode("Length", length.ToString())); - else if (length > 1 && length == sprites.Length - start) - root.Add(new MiniYamlNode("Length", "*")); - - if (facings > 1) - root.Add(new MiniYamlNode("Facings", facings.ToString())); - - if (tick != 40) - root.Add(new MiniYamlNode("Tick", tick.ToString())); - - return new MiniYaml(srcOverride, root); - } - public Sprite GetSprite( int frame ) { return GetSprite( frame, 0 ); diff --git a/OpenRA.Game/Network/Order.cs b/OpenRA.Game/Network/Order.cs index b81a9f2731..7f2bb449b7 100755 --- a/OpenRA.Game/Network/Order.cs +++ b/OpenRA.Game/Network/Order.cs @@ -199,7 +199,18 @@ namespace OpenRA { return new Order("HandshakeResponse", null, false) { IsImmediate = true, TargetString = text }; } + + public static Order PauseRequest() + { + return new Order("PauseRequest", null, false) { IsImmediate = true, TargetString="" }; //TODO: targetbool? + } + + public static Order PauseGame() + { + return new Order("PauseGame", null, false) { IsImmediate = true, TargetString=""}; //TODO: targetbool? + } + public static Order Command(string text) { return new Order("Command", null, false) { IsImmediate = true, TargetString = text }; diff --git a/OpenRA.Game/Network/OrderManager.cs b/OpenRA.Game/Network/OrderManager.cs index 708caf08fd..8d16467ad1 100755 --- a/OpenRA.Game/Network/OrderManager.cs +++ b/OpenRA.Game/Network/OrderManager.cs @@ -36,6 +36,7 @@ namespace OpenRA.Network public int LastTickTime = Environment.TickCount; public bool GameStarted { get { return NetFrameNumber != 0; } } + public bool GamePaused {get; set;} public IConnection Connection { get; private set; } public readonly int SyncHeaderSize = 9; diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs index 38447a5a51..686bed1306 100644 --- a/OpenRA.Game/Network/Session.cs +++ b/OpenRA.Game/Network/Session.cs @@ -70,6 +70,7 @@ namespace OpenRA.Network { public string ServerName; public string Map; + public string[] Ban; public string[] Mods = { "ra" }; // mod names public int OrderLatency = 3; public int RandomSeed = 0; diff --git a/OpenRA.Game/Network/UnitOrders.cs b/OpenRA.Game/Network/UnitOrders.cs index 92f3fb3e93..a7e1d9c730 100755 --- a/OpenRA.Game/Network/UnitOrders.cs +++ b/OpenRA.Game/Network/UnitOrders.cs @@ -94,6 +94,19 @@ namespace OpenRA.Network Game.StartGame(orderManager.LobbyInfo.GlobalSettings.Map, false); break; } + + case "PauseGame": + { + var client = orderManager.LobbyInfo.ClientWithIndex(clientId); + + if(client != null) + { + orderManager.GamePaused = !orderManager.GamePaused; + var pausetext = "The game is {0} by {1}".F( orderManager.GamePaused ? "paused" : "un-paused", client.Name ); + Game.AddChatLine(Color.White, "", pausetext); + } + break; + } case "HandshakeRequest": { diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index ea15d2ac38..17ab5219b7 100755 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -142,6 +142,7 @@ + diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index e50101bb5b..3879fd76ae 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2012 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, @@ -15,11 +15,15 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; +using System.Net.NetworkInformation; +using UPnP; using System.Threading; using OpenRA.FileFormats; using OpenRA.GameRules; using OpenRA.Network; +using XTimer = System.Timers.Timer; + namespace OpenRA.Server { public class Server @@ -45,6 +49,7 @@ namespace OpenRA.Server public ServerSettings Settings; public ModData ModData; public Map Map; + XTimer gameTimeout; volatile bool shutdown = false; public void Shutdown() { shutdown = true; } @@ -64,6 +69,9 @@ namespace OpenRA.Server randomSeed = (int)DateTime.Now.ToBinary(); + if (settings.AllowUPnP) + PortForward(); + foreach (var trait in modData.Manifest.ServerTraits) ServerTraits.Add( modData.ObjectCreator.CreateObject(trait) ); @@ -71,6 +79,7 @@ namespace OpenRA.Server lobbyInfo.GlobalSettings.RandomSeed = randomSeed; lobbyInfo.GlobalSettings.Map = settings.Map; lobbyInfo.GlobalSettings.ServerName = settings.Name; + lobbyInfo.GlobalSettings.Ban = settings.Ban; foreach (var t in ServerTraits.WithInterface()) t.ServerStarted(this); @@ -122,6 +131,20 @@ namespace OpenRA.Server } ) { IsBackground = true }.Start(); } + void PortForward() + { + if (UPnP.NAT.Discover()) + { + Log.Write("server", "UPnP-enabled router discovered."); + UPnP.NAT.ForwardPort(Port, ProtocolType.Tcp, "OpenRA"); //might timeout after second try + Log.Write("server", "Port {0} (TCP) has been forwarded.", Port); + Log.Write("server", "Your IP is: {0}", UPnP.NAT.GetExternalIP() ); + } + else + Log.Write("server", "No UPnP-enabled router detected."); + return; + } + /* lobby rework todo: * - "teams together" option for team games -- will eliminate most need * for manual spawnpoint choosing. @@ -205,6 +228,21 @@ namespace OpenRA.Server return; } + // Check if IP is banned + if (lobbyInfo.GlobalSettings.Ban != null) + { + var remote_addr = ((IPEndPoint)newConn.socket.RemoteEndPoint).Address.ToString(); + if (lobbyInfo.GlobalSettings.Ban.Contains(remote_addr)) + { + Console.WriteLine("Rejected connection from "+client.Name+"("+newConn.socket.RemoteEndPoint+"); Banned."); + Log.Write("server", "Rejected connection from {0}; Banned.", + newConn.socket.RemoteEndPoint); + SendOrderTo(newConn, "ServerError", "You are banned from the server!"); + DropClient(newConn); + return; + } + } + // Promote connection to a valid client preConns.Remove(newConn); conns.Add(newConn); @@ -332,6 +370,9 @@ namespace OpenRA.Server void InterpretServerOrder(Connection conn, ServerOrder so) { + var fromClient = GetClient(conn); + var fromIndex = fromClient != null ? fromClient.Index : 0; + switch (so.Name) { case "Command": @@ -347,17 +388,23 @@ namespace OpenRA.Server } break; + case "HandshakeResponse": ValidateClient(conn, so.Data); break; + case "Chat": case "TeamChat": - var fromClient = GetClient(conn); - var fromIndex = fromClient != null ? fromClient.Index : 0; - foreach (var c in conns.Except(conn).ToArray()) DispatchOrdersToClient(c, fromIndex, 0, so.Serialize()); - break; + break; + + case "PauseRequest": + foreach (var c in conns.ToArray()) + { var x = Order.PauseGame(); + DispatchOrdersToClient(c, fromIndex, 0, x.Serialize()); + } + break; } } @@ -406,6 +453,9 @@ namespace OpenRA.Server public void StartGame() { GameStarted = true; + listener.Stop(); + Console.WriteLine("Game started"); + foreach( var c in conns ) foreach( var d in conns ) DispatchOrdersToClient( c, d.PlayerIndex, 0x7FFFFFFF, new byte[] { 0xBF } ); @@ -419,6 +469,18 @@ namespace OpenRA.Server foreach (var t in ServerTraits.WithInterface()) t.GameStarted(this); + + // Check TimeOut + if ( Settings.TimeOut > 10000 ) + { + gameTimeout = new XTimer(Settings.TimeOut); + gameTimeout.Elapsed += (_,e) => + { + Console.WriteLine("Timeout at {0}!!!", e.SignalTime); + Environment.Exit(0); + }; + gameTimeout.Enabled = true; + } } } } diff --git a/OpenRA.Game/Server/UPnP.cs b/OpenRA.Game/Server/UPnP.cs new file mode 100644 index 0000000000..6b3e2ecdc4 --- /dev/null +++ b/OpenRA.Game/Server/UPnP.cs @@ -0,0 +1,137 @@ +#region Copyright & License Information +/* + * Copyright 2008-2009 http://www.codeproject.com/Members/Harold-Aptroot + * Source: http://www.codeproject.com/Articles/27992/NAT-Traversal-with-UPnP-in-C + * This file is licensed under A Public Domain dedication. + * For more information, see http://creativecommons.org/licenses/publicdomain/ + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Net.Sockets; +using System.Net; +using System.Xml; +using System.IO; + +namespace UPnP +{ + public class NAT + { + public static TimeSpan _timeout = new TimeSpan(0, 0, 0, 3); + static string _serviceUrl; + + public static bool Discover() + { + Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); + string req = "M-SEARCH * HTTP/1.1\r\n" + + "HOST: 239.255.255.250:1900\r\n" + + "ST:upnp:rootdevice\r\n" + + "MAN:\"ssdp:discover\"\r\n" + + "MX:3\r\n\r\n"; + byte[] data = Encoding.ASCII.GetBytes(req); + IPEndPoint ipe = new IPEndPoint(IPAddress.Broadcast, 1900); + byte[] buffer = new byte[0x1000]; + + DateTime start = DateTime.Now; + + do + { + s.SendTo(data, ipe); + + int length = 0; + do + { + length = s.Receive(buffer); + + string resp = Encoding.ASCII.GetString(buffer, 0, length).ToLower(); + if (resp.Contains("upnp:rootdevice")) + { + resp = resp.Substring(resp.ToLower().IndexOf("location:") + 9); + resp = resp.Substring(0, resp.IndexOf("\r")).Trim(); + if (!string.IsNullOrEmpty(_serviceUrl = GetServiceUrl(resp))) + return true; + } + } while (length > 0); + } while ((start - DateTime.Now) < _timeout); + return false; + } + + private static String GetServiceUrl(string resp) + { + XmlDocument desc = new XmlDocument(); + desc.Load(WebRequest.Create(resp).GetResponse().GetResponseStream()); + XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable); + nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); + XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr); + if (!typen.Value.Contains("InternetGatewayDevice")) + return null; + XmlNode node = desc.SelectSingleNode("//tns:service[tns:serviceType=\"urn:schemas-upnp-org:service:WANIPConnection:1\"]/tns:controlURL/text()", nsMgr); + if (node == null) + return null; + Uri respUri = new Uri(resp); + Uri combinedUri = new Uri(respUri, node.Value); + return combinedUri.AbsoluteUri; + } + + public static void ForwardPort(int port, ProtocolType protocol, string description) + { + if (string.IsNullOrEmpty(_serviceUrl)) + throw new Exception("No UPnP service available or Discover() has not been called"); + string body = String.Format(""+ + "{0}"+ + "{1}{0}" + + "{2}1" + + "{3}"+ + "0", + port, protocol.ToString().ToUpper(),Dns.GetHostAddresses(Dns.GetHostName())[0], description); + SOAPRequest(_serviceUrl, body, "AddPortMapping"); + } + + public static void DeleteForwardingRule(int port, ProtocolType protocol) + { + if (string.IsNullOrEmpty(_serviceUrl)) + throw new Exception("No UPnP service available or Discover() has not been called"); + string body = String.Format("" + + "{0}"+ + "{1}", port, protocol.ToString().ToUpper() ); + SOAPRequest(_serviceUrl, body, "DeletePortMapping"); + } + + public static IPAddress GetExternalIP() + { + if (string.IsNullOrEmpty(_serviceUrl)) + throw new Exception("No UPnP service available or Discover() has not been called"); + XmlDocument xdoc = SOAPRequest(_serviceUrl, "" + + "", "GetExternalIPAddress"); + XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable); + nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); + string IP = xdoc.SelectSingleNode("//NewExternalIPAddress/text()", nsMgr).Value; + return IPAddress.Parse(IP); + } + + private static XmlDocument SOAPRequest(string url, string soap, string function) + { + string req = "" + + "" + + "" + + soap + + "" + + ""; + WebRequest r = HttpWebRequest.Create(url); + r.Method = "POST"; + byte[] b = Encoding.UTF8.GetBytes(req); + r.Headers.Add("SOAPACTION", "\"urn:schemas-upnp-org:service:WANIPConnection:1#" + function + "\""); + r.ContentType = "text/xml; charset=\"utf-8\""; + r.ContentLength = b.Length; + r.GetRequestStream().Write(b, 0, b.Length); + XmlDocument resp = new XmlDocument(); + WebResponse wres = r.GetResponse(); + Stream ress = wres.GetResponseStream(); + resp.Load(ress); + return resp; + } + } +} diff --git a/OpenRA.Game/Traits/Health.cs b/OpenRA.Game/Traits/Health.cs index 432157094c..3016aece4b 100755 --- a/OpenRA.Game/Traits/Health.cs +++ b/OpenRA.Game/Traits/Health.cs @@ -41,7 +41,7 @@ namespace OpenRA.Traits } public int HP { get { return hp; } } - public readonly int MaxHP; + public int MaxHP; public bool IsDead { get { return hp <= 0; } } public bool RemoveOnDeath = true; diff --git a/OpenRA.Game/Traits/Player/PlayerResources.cs b/OpenRA.Game/Traits/Player/PlayerResources.cs index 4aa24f15ea..c5af6d6362 100644 --- a/OpenRA.Game/Traits/Player/PlayerResources.cs +++ b/OpenRA.Game/Traits/Player/PlayerResources.cs @@ -10,6 +10,7 @@ using System; using System.Linq; +using OpenRA.GameRules; namespace OpenRA.Traits { @@ -62,6 +63,8 @@ namespace OpenRA.Traits { readonly Player Owner; int AdviceInterval; + + int cashtickallowed = 0; public PlayerResources(Actor self, PlayerResourcesInfo info) { @@ -134,7 +137,11 @@ namespace OpenRA.Traits public void Tick(Actor self) { var eva = self.World.WorldActor.Info.Traits.Get(); - + + if(cashtickallowed > 0) { + cashtickallowed = cashtickallowed - 1; + } + OreCapacity = self.World.ActorsWithTrait() .Where(a => a.Actor.Owner == Owner) .Sum(a => a.Trait.Capacity); @@ -158,12 +165,12 @@ namespace OpenRA.Traits if (DisplayCash < Cash) { DisplayCash += move; - Sound.PlayToPlayer(self.Owner, eva.CashTickUp); + playCashTickUp(self); } else if (DisplayCash > Cash) { DisplayCash -= move; - Sound.PlayToPlayer(self.Owner, eva.CashTickDown); + playCashTickDown(self); } diff = Math.Abs(Ore - DisplayOre); @@ -173,13 +180,36 @@ namespace OpenRA.Traits if (DisplayOre < Ore) { DisplayOre += move; - Sound.PlayToPlayer(self.Owner, eva.CashTickUp); + playCashTickUp(self); } else if (DisplayOre > Ore) { DisplayOre -= move; - Sound.PlayToPlayer(self.Owner, eva.CashTickDown); + playCashTickDown(self); + } + } + + + public void playCashTickUp(Actor self) + { + var eva = self.World.WorldActor.Info.Traits.Get(); + if (Game.Settings.Sound.SoundCashTickType != SoundCashTicks.Disabled) + { + Sound.PlayToPlayer(self.Owner, eva.CashTickUp); } } + + public void playCashTickDown(Actor self) + { + var eva = self.World.WorldActor.Info.Traits.Get(); + if ( + Game.Settings.Sound.SoundCashTickType == SoundCashTicks.Extreme || + (Game.Settings.Sound.SoundCashTickType == SoundCashTicks.Normal && cashtickallowed == 0) + ) { + Sound.PlayToPlayer(self.Owner, eva.CashTickDown); + cashtickallowed = 3; + } + + } } } diff --git a/OpenRA.Game/Widgets/TimerWidget.cs b/OpenRA.Game/Widgets/TimerWidget.cs index 420352d896..b2f5bfb2ba 100644 --- a/OpenRA.Game/Widgets/TimerWidget.cs +++ b/OpenRA.Game/Widgets/TimerWidget.cs @@ -16,11 +16,11 @@ namespace OpenRA.Widgets { public override void Draw() { - var s = WidgetUtils.FormatTime(Game.LocalTick); var font = Game.Renderer.Fonts["Title"]; var rb = RenderBounds; + + var s = WidgetUtils.FormatTime(Game.LocalTick) + (Game.orderManager.GamePaused?" (paused)":""); var pos = new float2(rb.Left - font.Measure(s).X / 2, rb.Top); - font.DrawTextWithContrast(s, pos, Color.White, Color.Black, 1); } } diff --git a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs index accef80e75..29721ccaa2 100644 --- a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs +++ b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs @@ -148,6 +148,10 @@ namespace OpenRA.Widgets world.Selection.DoControlGroup(world, e.KeyName[0] - '0', e.Modifiers); return true; } + else if(e.KeyName == "pause" || e.KeyName == "f3") + { + world.IssueOrder(Order.PauseRequest()); + } bool handled = false; diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index e2ec53704c..e4c6dc4b50 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -183,7 +183,7 @@ namespace OpenRA while (frameEndActions.Count != 0) frameEndActions.Dequeue()(this); - Game.viewport.Tick(); + } public IEnumerable Actors { get { return actors; } } diff --git a/OpenRA.Mods.Cnc/RenderCargo.cs b/OpenRA.Mods.Cnc/RenderCargo.cs index ee5b9f926a..fa38c27d09 100644 --- a/OpenRA.Mods.Cnc/RenderCargo.cs +++ b/OpenRA.Mods.Cnc/RenderCargo.cs @@ -15,31 +15,43 @@ using OpenRA.Traits; namespace OpenRA.Mods.Cnc { - class RenderCargoInfo : ITraitInfo, Requires + public class RenderCargoInfo : ITraitInfo, Requires { - public object Create(ActorInitializer init) { return new RenderCargo(init.self); } + /* altitude of the cargo, relative to us. -ve is underneath us */ + public readonly int RelativeAltitude = 0; + + public object Create(ActorInitializer init) { return new RenderCargo(init.self, this); } } public class RenderCargo : IRenderModifier { Cargo cargo; IFacing facing; + IMove move; + RenderCargoInfo Info; - public RenderCargo(Actor self) + public RenderCargo(Actor self, RenderCargoInfo info) { cargo = self.Trait(); facing = self.TraitOrDefault(); + move = self.Trait(); + Info = info; } public IEnumerable ModifyRender(Actor self, IEnumerable r) { foreach (var c in cargo.Passengers) { - c.Trait().SetPxPosition( c, self.Trait().PxPosition ); - if (facing != null && c.HasTrait()) - c.Trait().Facing = facing.Facing; + c.Trait().SetPxPosition( c, move.PxPosition ); + + var cargoFacing = c.TraitOrDefault(); + if (facing != null && cargoFacing != null) + cargoFacing.Facing = facing.Facing; } - return r.Concat(cargo.Passengers.SelectMany(a => a.Render())); + + return r.Concat(cargo.Passengers.SelectMany(a => a.Render()) + .Select(a => a.WithPos(a.Pos - new float2(0, Info.RelativeAltitude)) + .WithZOffset(a.ZOffset + Info.RelativeAltitude))); } } } \ No newline at end of file diff --git a/OpenRA.Mods.Cnc/Widgets/Logic/CncSettingsLogic.cs b/OpenRA.Mods.Cnc/Widgets/Logic/CncSettingsLogic.cs index e71efe4de0..a4f30f8705 100644 --- a/OpenRA.Mods.Cnc/Widgets/Logic/CncSettingsLogic.cs +++ b/OpenRA.Mods.Cnc/Widgets/Logic/CncSettingsLogic.cs @@ -86,6 +86,10 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic Game.viewport.Zoom = graphicsSettings.PixelDouble ? 2 : 1; }; + var showShellmapCheckbox = generalPane.Get("SHOW_SHELLMAP"); + showShellmapCheckbox.IsChecked = () => gameSettings.ShowShellmap; + showShellmapCheckbox.OnClick = () => gameSettings.ShowShellmap ^= true; + generalPane.Get("WINDOW_RESOLUTION").IsVisible = () => graphicsSettings.Mode == WindowMode.Windowed; var windowWidth = generalPane.Get("WINDOW_WIDTH"); windowWidth.Text = graphicsSettings.WindowedSize.X.ToString(); diff --git a/OpenRA.Mods.RA/Activities/CaptureActor.cs b/OpenRA.Mods.RA/Activities/CaptureActor.cs index 5b82ff672f..dd22a4f412 100644 --- a/OpenRA.Mods.RA/Activities/CaptureActor.cs +++ b/OpenRA.Mods.RA/Activities/CaptureActor.cs @@ -28,8 +28,13 @@ namespace OpenRA.Mods.RA.Activities if( !target.OccupiesSpace.OccupiedCells().Any( x => x.First == self.Location ) ) return NextActivity; + var capturable = target.TraitOrDefault(); + if (capturable != null && capturable.CaptureInProgress && capturable.Captor.Owner.Stances[self.Owner] == Stance.Ally) + return NextActivity; + var sellable = target.TraitOrDefault(); - if (sellable != null && sellable.Selling) return NextActivity; + if (sellable != null && sellable.Selling) + return NextActivity; target.Trait().BeginCapture(target, self); self.World.AddFrameEndTask(w => self.Destroy()); diff --git a/OpenRA.Mods.RA/Capturable.cs b/OpenRA.Mods.RA/Capturable.cs index 2680f2b98b..3fa232ae55 100644 --- a/OpenRA.Mods.RA/Capturable.cs +++ b/OpenRA.Mods.RA/Capturable.cs @@ -27,9 +27,9 @@ namespace OpenRA.Mods.RA public class Capturable : ITick { - [Sync] Actor captor = null; + [Sync] public Actor Captor = null; [Sync] public int CaptureProgressTime = 0; - public bool CaptureInProgress { get { return captor != null; } } + public bool CaptureInProgress { get { return Captor != null; } } public CapturableInfo Info; public Capturable(CapturableInfo info) @@ -41,7 +41,7 @@ namespace OpenRA.Mods.RA { CaptureProgressTime = 0; - this.captor = captor; + this.Captor = captor; if (self.Owner != self.World.WorldActor.Owner) self.ChangeOwner(self.World.WorldActor.Owner); @@ -57,17 +57,28 @@ namespace OpenRA.Mods.RA { self.World.AddFrameEndTask(w => { - self.ChangeOwner(captor.Owner); + self.ChangeOwner(Captor.Owner); + ChangeCargoOwner(self, Captor.Owner); foreach (var t in self.TraitsImplementing()) - t.OnCapture(self, captor, self.Owner, captor.Owner); + t.OnCapture(self, Captor, self.Owner, Captor.Owner); - foreach (var t in captor.World.ActorsWithTrait()) - t.Trait.OnActorCaptured(t.Actor, self, captor, self.Owner, captor.Owner); + foreach (var t in Captor.World.ActorsWithTrait()) + t.Trait.OnActorCaptured(t.Actor, self, Captor, self.Owner, Captor.Owner); - captor = null; + Captor = null; }); } } + + void ChangeCargoOwner(Actor self, Player captor) + { + var cargo = self.TraitOrDefault(); + if (cargo == null) + return; + + foreach (var c in cargo.Passengers) + c.Owner = captor; + } } } diff --git a/OpenRA.Mods.RA/Captures.cs b/OpenRA.Mods.RA/Captures.cs index f7baa8e191..375a840a88 100644 --- a/OpenRA.Mods.RA/Captures.cs +++ b/OpenRA.Mods.RA/Captures.cs @@ -8,6 +8,7 @@ */ #endregion +using System; using System.Collections.Generic; using System.Drawing; using System.Linq; @@ -21,14 +22,17 @@ namespace OpenRA.Mods.RA class CapturesInfo : ITraitInfo { public string[] CaptureTypes = {"building"}; - public object Create(ActorInitializer init) { return new Captures(this); } + public object Create(ActorInitializer init) { return new Captures(init.self, this); } } class Captures : IIssueOrder, IResolveOrder, IOrderVoice { public readonly CapturesInfo Info; - public Captures(CapturesInfo info) + readonly Actor self; + + public Captures(Actor self, CapturesInfo info) { + this.self = self; Info = info; } @@ -36,7 +40,7 @@ namespace OpenRA.Mods.RA { get { - yield return new CaptureOrderTargeter(Info.CaptureTypes); + yield return new CaptureOrderTargeter(Info.CaptureTypes, target => CanEnter(target)); } } @@ -50,13 +54,16 @@ namespace OpenRA.Mods.RA public string VoicePhraseForOrder(Actor self, Order order) { - return (order.OrderString == "CaptureActor") ? "Attack" : null; + return (order.OrderString == "CaptureActor" + && CanEnter(order.TargetActor)) ? "Attack" : null; } public void ResolveOrder(Actor self, Order order) { if (order.OrderString == "CaptureActor") { + if (!CanEnter(order.TargetActor)) return; + self.SetTargetLine(Target.FromOrder(order), Color.Red); self.CancelActivity(); @@ -64,15 +71,24 @@ namespace OpenRA.Mods.RA self.QueueActivity(new CaptureActor(order.TargetActor)); } } + + bool CanEnter(Actor target) + { + var c = target.TraitOrDefault(); + return c != null && ( !c.CaptureInProgress || c.Captor.Owner.Stances[self.Owner] != Stance.Ally ); + } } class CaptureOrderTargeter : UnitTraitOrderTargeter { readonly string[] captureTypes; - public CaptureOrderTargeter(string[] captureTypes) - : base( "CaptureActor", 6, "enter", true, true ) + readonly Func useEnterCursor; + + public CaptureOrderTargeter(string[] captureTypes, Func useEnterCursor) + : base( "CaptureActor", 6, "enter", true, true) { this.captureTypes = captureTypes; + this.useEnterCursor = useEnterCursor; } public override bool CanTargetActor(Actor self, Actor target, bool forceAttack, bool forceQueued, ref string cursor) @@ -87,9 +103,10 @@ namespace OpenRA.Mods.RA if( playerRelationship == Stance.Neutral && !ci.AllowNeutral ) return false; IsQueued = forceQueued; + if (captureTypes.Contains(ci.Type)) { - cursor = "enter"; + cursor = useEnterCursor(target) ? "enter" : "enter-blocked"; return true; } diff --git a/OpenRA.Mods.RA/Cloak.cs b/OpenRA.Mods.RA/Cloak.cs index 038d20e55f..8002adee7f 100644 --- a/OpenRA.Mods.RA/Cloak.cs +++ b/OpenRA.Mods.RA/Cloak.cs @@ -81,6 +81,8 @@ namespace OpenRA.Mods.RA if (remainingTime > 0 && canCloak) if (--remainingTime <= 0) Sound.Play(info.CloakSound, self.CenterLocation); + if (self.IsDisabled()) + Uncloak(); } public bool IsVisible(Actor self) diff --git a/OpenRA.Mods.RA/Crate.cs b/OpenRA.Mods.RA/Crate.cs index 53f33a8fdd..5324a12287 100644 --- a/OpenRA.Mods.RA/Crate.cs +++ b/OpenRA.Mods.RA/Crate.cs @@ -38,13 +38,10 @@ namespace OpenRA.Mods.RA class Crate : ITick, IOccupySpace, ITeleportable, ICrushable, ISync { readonly Actor self; - [Sync] - int ticks; - - [Sync] - public int2 Location; - + [Sync] int ticks; + [Sync] public int2 Location; CrateInfo Info; + public Crate(ActorInitializer init, CrateInfo info) { this.self = init.self; diff --git a/OpenRA.Mods.RA/Effects/Missile.cs b/OpenRA.Mods.RA/Effects/Missile.cs index 5d69008d73..d7df87b0ce 100755 --- a/OpenRA.Mods.RA/Effects/Missile.cs +++ b/OpenRA.Mods.RA/Effects/Missile.cs @@ -99,13 +99,14 @@ namespace OpenRA.Mods.RA.Effects Altitude += Math.Sign(targetAltitude - Altitude); - Facing = Traits.Util.TickFacing(Facing, - Traits.Util.GetFacing(dist, Facing), - Info.ROT); + if (Args.target.IsValid) + Facing = Traits.Util.TickFacing(Facing, + Traits.Util.GetFacing(dist, Facing), + Info.ROT); anim.Tick(); - if (dist.LengthSquared < MissileCloseEnough * MissileCloseEnough || !Args.target.IsValid ) + if (dist.LengthSquared < MissileCloseEnough * MissileCloseEnough && Args.target.IsValid ) Explode(world); // TODO: Replace this with a lookup table diff --git a/OpenRA.Mods.RA/Explodes.cs b/OpenRA.Mods.RA/Explodes.cs index 7d64a67aa2..8d23da8c3f 100644 --- a/OpenRA.Mods.RA/Explodes.cs +++ b/OpenRA.Mods.RA/Explodes.cs @@ -13,7 +13,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA { - class ExplodesInfo : TraitInfo + class ExplodesInfo : ITraitInfo { [WeaponReference] public readonly string Weapon = "UnitExplode"; @@ -21,13 +21,23 @@ namespace OpenRA.Mods.RA public readonly string EmptyWeapon = "UnitExplode"; public readonly int Chance = 100; + public readonly int[] InfDeath = null; + + public object Create (ActorInitializer init) { return new Explodes(this); } } class Explodes : INotifyKilled { + readonly ExplodesInfo Info; + + public Explodes( ExplodesInfo info ) { Info = info; } + public void Killed(Actor self, AttackInfo e) { - if (self.World.SharedRandom.Next(100) > self.Info.Traits.Get().Chance) + if (self.World.SharedRandom.Next(100) > Info.Chance) + return; + + if (Info.InfDeath != null && e.Warhead != null && !Info.InfDeath.Contains(e.Warhead.InfDeath)) return; var weapon = ChooseWeaponForExplosion(self); @@ -42,9 +52,7 @@ namespace OpenRA.Mods.RA string ChooseWeaponForExplosion(Actor self) { var shouldExplode = self.TraitsImplementing().All(a => a.ShouldExplode(self)); - - var info = self.Info.Traits.Get(); - return shouldExplode ? info.Weapon : info.EmptyWeapon; + return shouldExplode ? Info.Weapon : Info.EmptyWeapon; } } } diff --git a/OpenRA.Mods.RA/InfiltrateForExploration.cs b/OpenRA.Mods.RA/InfiltrateForExploration.cs index bf153f6438..d0ccefbe66 100644 --- a/OpenRA.Mods.RA/InfiltrateForExploration.cs +++ b/OpenRA.Mods.RA/InfiltrateForExploration.cs @@ -25,7 +25,13 @@ namespace OpenRA.Mods.RA * - actually steal their exploration before resetting it */ if (self.World.LocalPlayer != null && self.World.LocalPlayer.Stances[self.Owner] == Stance.Ally) + { + var gpsWatcher = self.Owner.PlayerActor.TraitOrDefault(); + if (gpsWatcher != null && (gpsWatcher.Granted || gpsWatcher.GrantedAllies)) + return; + self.Owner.Shroud.ResetExploration(); + } } } } diff --git a/OpenRA.Mods.RA/Move/Mobile.cs b/OpenRA.Mods.RA/Move/Mobile.cs index 0dddfdd7f0..c674139016 100755 --- a/OpenRA.Mods.RA/Move/Mobile.cs +++ b/OpenRA.Mods.RA/Move/Mobile.cs @@ -30,6 +30,7 @@ namespace OpenRA.Mods.RA.Move public readonly int Speed = 1; public readonly bool OnRails = false; public readonly bool SharesCell = false; + public readonly int Altitude; public virtual object Create(ActorInitializer init) { return new Mobile(init, this); } @@ -110,7 +111,7 @@ namespace OpenRA.Mods.RA.Move int2 __fromCell, __toCell; public SubCell fromSubCell, toSubCell; - int __altitude; + //int __altitude; [Sync] public int Facing @@ -120,11 +121,7 @@ namespace OpenRA.Mods.RA.Move } [Sync] - public int Altitude - { - get { return __altitude; } - set { __altitude = value; } - } + public int Altitude { get; set; } public int ROT { get { return Info.ROT; } } public int InitialFacing { get { return Info.InitialFacing; } } diff --git a/OpenRA.Mods.RA/Move/Move.cs b/OpenRA.Mods.RA/Move/Move.cs index fb4ecbcd5d..15d090d3c4 100755 --- a/OpenRA.Mods.RA/Move/Move.cs +++ b/OpenRA.Mods.RA/Move/Move.cs @@ -95,6 +95,14 @@ namespace OpenRA.Mods.RA.Move public override Activity Tick( Actor self ) { var mobile = self.Trait(); + var info = self.Info.Traits.Get(); + + if (mobile.Altitude != info.Altitude) + { + if (mobile.Altitude < info.Altitude) + ++mobile.Altitude; + return this; + } if (destination == mobile.toCell) return NextActivity; diff --git a/OpenRA.Mods.RA/Orders/PowerDownOrderGenerator.cs b/OpenRA.Mods.RA/Orders/PowerDownOrderGenerator.cs index c344a6706a..3dcf38a460 100755 --- a/OpenRA.Mods.RA/Orders/PowerDownOrderGenerator.cs +++ b/OpenRA.Mods.RA/Orders/PowerDownOrderGenerator.cs @@ -48,7 +48,13 @@ namespace OpenRA.Mods.RA.Orders } } - public void Tick(World world) { } + public void Tick(World world) + { + if (world.LocalPlayer != null && + world.LocalPlayer.WinState != WinState.Undefined) + world.CancelInputMode(); + } + public void RenderAfterWorld(WorldRenderer wr, World world) { } public void RenderBeforeWorld(WorldRenderer wr, World world) { } diff --git a/OpenRA.Mods.RA/Orders/RepairOrderGenerator.cs b/OpenRA.Mods.RA/Orders/RepairOrderGenerator.cs index c1ede6fc60..cbbf16e54c 100644 --- a/OpenRA.Mods.RA/Orders/RepairOrderGenerator.cs +++ b/OpenRA.Mods.RA/Orders/RepairOrderGenerator.cs @@ -42,7 +42,13 @@ namespace OpenRA.Mods.RA.Orders } } - public void Tick(World world) { } + public void Tick(World world) + { + if (world.LocalPlayer != null && + world.LocalPlayer.WinState != WinState.Undefined) + world.CancelInputMode(); + } + public void RenderAfterWorld(WorldRenderer wr, World world) { } public void RenderBeforeWorld(WorldRenderer wr, World world) { } diff --git a/OpenRA.Mods.RA/Render/WithShadow.cs b/OpenRA.Mods.RA/Render/WithShadow.cs index a69fb29839..1419dd0d8a 100644 --- a/OpenRA.Mods.RA/Render/WithShadow.cs +++ b/OpenRA.Mods.RA/Render/WithShadow.cs @@ -13,6 +13,7 @@ using System.Collections.Generic; using System.Linq; using OpenRA.Traits; using OpenRA.Mods.RA.Air; +using OpenRA.Mods.RA.Move; namespace OpenRA.Mods.RA.Render { @@ -25,12 +26,12 @@ namespace OpenRA.Mods.RA.Render var move = self.Trait(); /* rude hack */ - var visualOffset = (move is Helicopter && move.Altitude > 0) + var visualOffset = ((move is Helicopter || move is Mobile) && move.Altitude > 0) ? Math.Abs((self.ActorID + Game.LocalTick) / 5 % 4 - 1) - 1 : 0; var shadowSprites = r.Select(a => a.WithPalette("shadow")); var flyingSprites = (move.Altitude <= 0) ? r - : r.Select(a => a.WithPos(a.Pos - new float2(0, move.Altitude + visualOffset)).WithZOffset(move.Altitude)); + : r.Select(a => a.WithPos(a.Pos - new float2(0, move.Altitude + visualOffset)).WithZOffset(move.Altitude + a.ZOffset)); return shadowSprites.Concat(flyingSprites); } diff --git a/OpenRA.Mods.RA/Spy.cs b/OpenRA.Mods.RA/Spy.cs index a06a0ab21a..407685eb3f 100644 --- a/OpenRA.Mods.RA/Spy.cs +++ b/OpenRA.Mods.RA/Spy.cs @@ -121,7 +121,8 @@ namespace OpenRA.Mods.RA public string VoicePhraseForOrder(Actor self, Order order) { - return order.OrderString == "Disguise" ? "Attack" : null; + return (order.OrderString == "Disguise" + || order.OrderString == "SpyInfiltrate") ? "Attack" : null; } public Color RadarColorOverride(Actor self) diff --git a/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs index 2cb45a5bab..03d8ef28e2 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs @@ -435,8 +435,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic } } - bool SpawnPointAvailable(int index) { return (index == 0) || orderManager.LobbyInfo.Clients.All(c => c.SpawnPoint != index); } - void CycleReady() { orderManager.IssueOrder(Order.Command("ready")); diff --git a/OpenRA.Mods.RA/Widgets/Logic/ServerCreationLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/ServerCreationLogic.cs index abe2a490e7..e93d00a2f4 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/ServerCreationLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/ServerCreationLogic.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2012 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, @@ -22,6 +22,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic Action onExit; Map map; bool advertiseOnline; + bool allowUPnP; [ObjectCreator.UseCtor] public ServerCreationLogic(Widget widget, Action onExit, Action openLobby) @@ -64,6 +65,10 @@ namespace OpenRA.Mods.RA.Widgets.Logic var advertiseCheckbox = panel.Get("ADVERTISE_CHECKBOX"); advertiseCheckbox.IsChecked = () => advertiseOnline; advertiseCheckbox.OnClick = () => advertiseOnline ^= true; + + var UPnPCheckbox = panel.Get("UPNP_CHECKBOX"); + UPnPCheckbox.IsChecked = () => allowUPnP; + UPnPCheckbox.OnClick = () => allowUPnP ^= true; } void CreateAndJoin() @@ -81,6 +86,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic Game.Settings.Server.ListenPort = listenPort; Game.Settings.Server.ExternalPort = externalPort; Game.Settings.Server.AdvertiseOnline = advertiseOnline; + Game.Settings.Server.AllowUPnP = allowUPnP; Game.Settings.Server.Map = map.Uid; Game.Settings.Save(); diff --git a/OpenRA.Mods.RA/Widgets/Logic/SettingsMenuLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/SettingsMenuLogic.cs index 89eff81fb6..a81e9ec86a 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/SettingsMenuLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/SettingsMenuLogic.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2012 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, @@ -71,6 +71,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic // Audio var audio = bg.Get("AUDIO_PANE"); + var soundSettings = Game.Settings.Sound; var soundslider = audio.Get("SOUND_VOLUME"); soundslider.OnChange += x => Sound.SoundVolume = x; @@ -80,10 +81,21 @@ namespace OpenRA.Mods.RA.Widgets.Logic musicslider.OnChange += x => Sound.MusicVolume = x; musicslider.Value = Sound.MusicVolume; + var cashticksdropdown = audio.Get("CASH_TICK_TYPE"); + cashticksdropdown.OnMouseDown = _ => ShowSoundTickDropdown(cashticksdropdown, soundSettings); + cashticksdropdown.GetText = () => soundSettings.SoundCashTickType == SoundCashTicks.Extreme ? + "Extreme" : soundSettings.SoundCashTickType == SoundCashTicks.Normal ? "Normal" : "Disabled"; + + // Display var display = bg.Get("DISPLAY_PANE"); var gs = Game.Settings.Graphics; + var GraphicsRendererDropdown = display.Get("GRAPHICS_RENDERER"); + GraphicsRendererDropdown.OnMouseDown = _ => ShowRendererDropdown(GraphicsRendererDropdown, gs); + GraphicsRendererDropdown.GetText = () => gs.Renderer == "Gl" ? + "OpenGL" : gs.Renderer == "Cg" ? "Cg Toolkit" : "OpenGL"; + var windowModeDropdown = display.Get("MODE_DROPDOWN"); windowModeDropdown.OnMouseDown = _ => ShowWindowModeDropdown(windowModeDropdown, gs); windowModeDropdown.GetText = () => gs.Mode == WindowMode.Windowed ? @@ -138,6 +150,29 @@ namespace OpenRA.Mods.RA.Widgets.Logic return true; } + + public static bool ShowSoundTickDropdown(DropDownButtonWidget dropdown, SoundSettings audio) + { + var options = new Dictionary() + { + { "Extreme", SoundCashTicks.Extreme }, + { "Normal", SoundCashTicks.Normal }, + { "Disabled", SoundCashTicks.Disabled }, + }; + + Func setupItem = (o, itemTemplate) => + { + var item = ScrollItemWidget.Setup(itemTemplate, + () => audio.SoundCashTickType == options[o], + () => audio.SoundCashTickType = options[o]); + item.Get("LABEL").GetText = () => o; + return item; + }; + + dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options.Keys, setupItem); + return true; + } + public static bool ShowWindowModeDropdown(DropDownButtonWidget dropdown, GraphicSettings s) { var options = new Dictionary() @@ -159,5 +194,26 @@ namespace OpenRA.Mods.RA.Widgets.Logic dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options.Keys, setupItem); return true; } + + public static bool ShowRendererDropdown(DropDownButtonWidget dropdown, GraphicSettings s) + { + var options = new Dictionary() + { + { "OpenGL", "Gl" }, + { "Cg Toolkit", "Cg" }, + }; + + Func setupItem = (o, itemTemplate) => + { + var item = ScrollItemWidget.Setup(itemTemplate, + () => s.Renderer == options[o], + () => s.Renderer = options[o]); + item.Get("LABEL").GetText = () => o; + return item; + }; + + dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options.Keys, setupItem); + return true; + } } } diff --git a/OpenRA.Utility/Command.cs b/OpenRA.Utility/Command.cs index 0f89cf2ea3..6fc2d02acc 100644 --- a/OpenRA.Utility/Command.cs +++ b/OpenRA.Utility/Command.cs @@ -398,5 +398,28 @@ namespace OpenRA.Utility ShpWriter.Write(destStream, srcImage.Width, srcImage.Height, srcImage.Frames.Select( im => im.Image.Select(px => (byte)remap[px]).ToArray() )); } + + public static void TransposeShp(string[] args) + { + var srcImage = ShpReader.Load(args[1]); + + var srcFrames = srcImage.Frames.ToArray(); + var destFrames = srcImage.Frames.ToArray(); + + for( var z = 3; z < args.Length - 2; z += 3 ) + { + var start = int.Parse(args[z]); + var m = int.Parse(args[z+1]); + var n = int.Parse(args[z+2]); + + for( var i = 0; i < m; i++ ) + for( var j = 0; j < n; j++ ) + destFrames[ start + i*n + j ] = srcFrames[ start + j*m + i ]; + } + + using( var destStream = File.Create(args[2]) ) + ShpWriter.Write(destStream, srcImage.Width, srcImage.Height, + destFrames.Select(f => f.Image)); + } } } diff --git a/OpenRA.Utility/Program.cs b/OpenRA.Utility/Program.cs index 06e256fddb..d46783c004 100644 --- a/OpenRA.Utility/Program.cs +++ b/OpenRA.Utility/Program.cs @@ -28,6 +28,7 @@ namespace OpenRA.Utility { "--tmp-png", Command.ConvertTmpToPng }, { "--remap", Command.RemapShp }, { "--r8", Command.ConvertR8ToPng }, + { "--transpose", Command.TransposeShp }, }; if (args.Length == 0) { PrintUsage(); return; } @@ -61,6 +62,7 @@ namespace OpenRA.Utility Console.WriteLine(" --tmp-png MOD[,MOD]* THEATER FILES Extract terrain tiles to PNG"); Console.WriteLine(" --remap SRCMOD:PAL DESTMOD:PAL SRCSHP DESTSHP Remap SHPs to another palette"); Console.WriteLine(" --r8 R8FILE PALETTE START END FILENAME [--transparent] [--infrantry] [--vehicle] [--projectile] [--building] [--wall] [--tileset] Convert Dune 2000 DATA.R8 to PNGs choosing start- and endframe as well as type for correct offset to append multiple frames to one PNG named by filename optionally setting up transparency."); + Console.WriteLine(" --transpose SRCSHP DESTSHP START N M [START N M ...] Transpose the N*M block of frames starting at START."); } static string GetNamedArg(string[] args, string arg) diff --git a/mods/cnc/chrome/ingame.yaml b/mods/cnc/chrome/ingame.yaml index aad60f205c..6e25a16d36 100644 --- a/mods/cnc/chrome/ingame.yaml +++ b/mods/cnc/chrome/ingame.yaml @@ -197,7 +197,7 @@ Container@PLAYER_WIDGETS: ToggleButton@BUILDING: Width:30 Height:30 - Key: y + Key: q TooltipText: Buildings TooltipContainer:TOOLTIP_CONTAINER ClickSound:button.aud @@ -211,7 +211,7 @@ Container@PLAYER_WIDGETS: X:35 Width:30 Height:30 - Key: u + Key: w TooltipText: Defense TooltipContainer:TOOLTIP_CONTAINER ClickSound:button.aud @@ -225,7 +225,7 @@ Container@PLAYER_WIDGETS: X:70 Width:30 Height:30 - Key: i + Key: e TooltipText: Infantry TooltipContainer:TOOLTIP_CONTAINER ClickSound:button.aud @@ -239,7 +239,7 @@ Container@PLAYER_WIDGETS: X:105 Width:30 Height:30 - Key: o + Key: r TooltipText: Vehicles TooltipContainer:TOOLTIP_CONTAINER ClickSound:button.aud @@ -253,7 +253,7 @@ Container@PLAYER_WIDGETS: X:140 Width:30 Height:30 - Key: p + Key: t TooltipText: Aircraft TooltipContainer:TOOLTIP_CONTAINER ClickSound:button.aud diff --git a/mods/cnc/chrome/settings.yaml b/mods/cnc/chrome/settings.yaml index 469b2cdbbc..7ce9ba4fe3 100644 --- a/mods/cnc/chrome/settings.yaml +++ b/mods/cnc/chrome/settings.yaml @@ -14,7 +14,7 @@ Container@SETTINGS_PANEL: Text:Settings Background@GENERAL_CONTROLS: Width:740 - Height:250 + Height:290 Background:panel-black Children: Label@TITLE: @@ -142,49 +142,56 @@ Container@SETTINGS_PANEL: Height:20 Font:Regular Text:Enable Pixel Doubling + Checkbox@SHOW_SHELLMAP: + X:375 + Y:120 + Width:200 + Height:20 + Font:Regular + Text:Show Shellmap Label@AUDIO_TITLE: X:375 - Y:130 + Y:160 Width:340 Font:Bold Text:Sound Align:Center Label@SOUND_LABEL: X:375 - Y:145 + Y:175 Width:95 Height:25 Align:Right Text:Sound Volume: Slider@SOUND_SLIDER: X:475 - Y:150 + Y:180 Width:240 Height:20 Ticks:5 Label@MUSIC_LABEL: X:375 - Y:175 + Y:205 Width:95 Height:25 Align:Right Text:Music Volume: Slider@MUSIC_SLIDER: X:475 - Y:180 + Y:210 Width:240 Height:20 Ticks:5 Checkbox@SHELLMAP_MUSIC: X:375 - Y:210 + Y:240 Width:200 Height:20 Font:Regular Text:Shellmap Music Background@INPUT_CONTROLS: Width:740 - Height:250 + Height:290 Background:panel-black Visible:false Children: @@ -260,20 +267,20 @@ Container@SETTINGS_PANEL: Font:Regular Text:Shift-Enter Toggles Team Chat Button@GENERAL_BUTTON: - Y:249 + Y:289 Width:140 Height:35 Text:General Button@INPUT_BUTTON: X:150 - Y:249 + Y:289 Width:140 Height:35 Text:Input Button@BACK_BUTTON: Key:escape X:600 - Y:249 + Y:289 Width:140 Height:35 Text:Back diff --git a/mods/cnc/rules/aircraft.yaml b/mods/cnc/rules/aircraft.yaml index 0c94089d89..1298bfc45a 100644 --- a/mods/cnc/rules/aircraft.yaml +++ b/mods/cnc/rules/aircraft.yaml @@ -11,6 +11,8 @@ TRAN: Prerequisites: hpad BuiltAt: hpad Owner: gdi,nod + Selectable: + Bounds: 41,41 Helicopter: LandWhenIdle: true ROT: 5 @@ -49,6 +51,8 @@ HELI: Prerequisites: hpad, hq BuiltAt: hpad Owner: nod + Selectable: + Bounds: 30,24 Helicopter: ROT: 4 Speed: 20 @@ -93,6 +97,8 @@ ORCA: Prerequisites: hpad, hq BuiltAt: hpad Owner: gdi + Selectable: + Bounds: 30,24 Helicopter: ROT: 4 Speed: 20 diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index 34971a1c73..87dc0be720 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -69,8 +69,9 @@ ^Helicopter: AppearsOnRadar: UseLocation: yes - TargetableUnit: + TargetableAircraft: TargetTypes: Air + GroundedTargetTypes: Ground SelectionDecorations: Selectable: Voice: VehicleVoice diff --git a/mods/cnc/rules/structures.yaml b/mods/cnc/rules/structures.yaml index 9f241ed85a..5029ed6479 100644 --- a/mods/cnc/rules/structures.yaml +++ b/mods/cnc/rules/structures.yaml @@ -12,7 +12,9 @@ FACT: Footprint: xxx xxx Dimensions: 3,2 Health: - HP: 1750 + HP: 2000 + Armor: + Type: Heavy RevealsShroud: Range: 10 Bib: @@ -240,7 +242,7 @@ AFLD: Footprint: xxxx xxxx Dimensions: 4,2 Health: - HP: 1000 + HP: 1750 RevealsShroud: Range: 7 Bib: @@ -282,7 +284,7 @@ WEAP: Footprint: ___ xxx === Dimensions: 3,3 Health: - HP: 1000 + HP: 1750 RevealsShroud: Range: 4 Bib: @@ -362,7 +364,7 @@ NUK2: Footprint: xx xx Dimensions: 2,2 Health: - HP: 600 + HP: 650 RevealsShroud: Range: 4 Bib: @@ -409,7 +411,7 @@ HPAD: Footprint: xx xx Dimensions: 2,2 Health: - HP: 800 + HP: 400 RevealsShroud: Range: 5 Exit@1: @@ -561,7 +563,7 @@ CYCL: Prerequisites: fact Owner: nod Health: - HP: 300 + HP: 100 Armor: Type: Light @@ -581,7 +583,7 @@ SBAG: Prerequisites: fact Owner: gdi Health: - HP: 250 + HP: 100 Armor: Type: Light @@ -601,7 +603,7 @@ BRIK: Prerequisites: vehicleproduction Owner: gdi,nod Health: - HP: 1000 + HP: 250 Armor: Type: Heavy Wall: @@ -620,7 +622,7 @@ GUN: Description: Anti-Armor base defense.\n Strong vs Tanks\n Weak vs Infantry, Aircraft Buildable: Queue: Defense - BuildPaletteOrder: 40 + BuildPaletteOrder: 45 Prerequisites: barracks Owner: gdi,nod Building: @@ -678,7 +680,6 @@ SAM: PrimaryWeapon: SAMMissile WithMuzzleFlash: AutoTarget: - -AutoTargetIgnore: -RenderBuilding: RenderRangeCircle: @@ -692,7 +693,7 @@ GTWR: Description: Basic defensive structure.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks, Aircraft Buildable: Queue: Defense - BuildPaletteOrder: 50 + BuildPaletteOrder: 40 Prerequisites: barracks Owner: gdi,nod Building: diff --git a/mods/cnc/rules/vehicles.yaml b/mods/cnc/rules/vehicles.yaml index fb4fe3cc89..703ff87418 100644 --- a/mods/cnc/rules/vehicles.yaml +++ b/mods/cnc/rules/vehicles.yaml @@ -19,7 +19,7 @@ MCV: Armor: Type: Light RevealsShroud: - Range: 4 + Range: 8 Transforms: IntoActor: fact Offset:-1,-1 @@ -84,7 +84,7 @@ APC: Owner: gdi Mobile: ROT: 5 - Speed: 11 + Speed: 10 Health: HP: 200 Armor: @@ -186,7 +186,7 @@ BGGY: Owner: nod Mobile: ROT: 10 - Speed: 12 + Speed: 16 Health: HP: 140 Armor: @@ -218,7 +218,14 @@ BIKE: Owner: nod Mobile: ROT: 10 - Speed: 13 + Speed: 20 + TerrainSpeeds: + Clear: 42 + Rough: 25 + Road: 100 + Tiberium: 25 + BlueTiberium: 25 + Beach: 25 Health: HP: 120 Armor: @@ -248,7 +255,7 @@ JEEP: Owner: gdi Mobile: ROT: 10 - Speed: 10 + Speed: 14 Health: HP: 150 Armor: @@ -314,7 +321,7 @@ MTNK: Prerequisites: hq Owner: gdi Mobile: - Speed: 9 + Speed: 7 Health: HP: 400 Armor: @@ -373,6 +380,8 @@ HTNK: AutoTarget: SelfHealing: Ticks: 10 + HealIfBelow: 50% + DamageCooldown: 200 LeavesHusk: HuskActor: HTNK.Husk Explodes: @@ -384,7 +393,7 @@ HTNK: MSAM: Inherits: ^Tank Valued: - Cost: 800 + Cost: 1200 Tooltip: Name: Rocket Launcher Icon: msamicnh @@ -400,7 +409,7 @@ MSAM: Armor: Type: Light RevealsShroud: - Range: 6 + Range: 10 Turreted: ROT: 255 AttackFrontal: diff --git a/mods/cnc/sequences/misc.yaml b/mods/cnc/sequences/misc.yaml index be7448f41c..272df55451 100644 --- a/mods/cnc/sequences/misc.yaml +++ b/mods/cnc/sequences/misc.yaml @@ -94,6 +94,10 @@ explosion: 6w: atomsfx Start: 0 Length: * + chemball: chemball + Start: 0 + Length: * + rank: rank: Start: 0 diff --git a/mods/cnc/weapons.yaml b/mods/cnc/weapons.yaml index 407d61bb45..a7e222c044 100644 --- a/mods/cnc/weapons.yaml +++ b/mods/cnc/weapons.yaml @@ -530,18 +530,18 @@ MammothMissiles: Damage: 75 227mm: - ROF: 80 - Range: 10 - MinRange: 2 - Burst: 6 - BurstDelay: 1 + ROF: 45 + Range: 15 + MinRange: 4 + Burst: 8 + BurstDelay: 3 Report: ROCKET1 ValidTargets: Ground Projectile: Bullet Arm: 5 High: yes Shadow: yes - Inaccuracy: 30 + Inaccuracy: 60 Angle: 0.1 Image: DRAGON ROT: 5 @@ -551,8 +551,8 @@ MammothMissiles: Spread: 10 Versus: None: 30% - Wood: 75% - Light: 75% + Wood: 50% + Light: 100% Heavy: 50% InfDeath: 3 Explosion: 4 @@ -597,11 +597,11 @@ ArtilleryShell: MinRange: 2 Report: TNKFIRE2 Projectile: Bullet - ContrailLength: 30 Speed: 12 High: yes Angle: .09 Inaccuracy: 30 + ContrailLength: 30 Image: 120MM Warhead: Damage: 150 @@ -612,7 +612,7 @@ ArtilleryShell: Light: 60% Heavy: 25% InfDeath: 2 - Explosion: 5 + Explosion: 8 SmudgeType: Crater ImpactSound: XPLOSML2 @@ -815,4 +815,4 @@ APCGun: Versus: Light: 100% Explosion: 5 - Damage: 6 \ No newline at end of file + Damage: 12 diff --git a/mods/ra/chrome/create-server.yaml b/mods/ra/chrome/create-server.yaml index 2fd4466391..3a81044bd2 100644 --- a/mods/ra/chrome/create-server.yaml +++ b/mods/ra/chrome/create-server.yaml @@ -60,6 +60,12 @@ Background@CREATESERVER_BG: Width:300 Height:20 Text:Advertise game Online + Checkbox@UPNP_CHECKBOX: + X:165 + Y:165 + Width:300 + Height:20 + Text:Allow UPnP port forwarding Button@CREATE_BUTTON: X:130 Y:PARENT_BOTTOM - 45 diff --git a/mods/ra/chrome/settings.yaml b/mods/ra/chrome/settings.yaml index 0b122a18c3..e69d5b72a5 100644 --- a/mods/ra/chrome/settings.yaml +++ b/mods/ra/chrome/settings.yaml @@ -136,6 +136,17 @@ Background@SETTINGS_MENU: Width:250 Height:20 Ticks:5 + Label@SOUND_TICK_TYPE_LABEL: + X:0 + Y:70 + Text: Cash ticks + DropDownButton@CASH_TICK_TYPE: + X:100 + Y:60 + Width:250 + Height:20 + Font:Regular + Text:Extreme Container@DISPLAY_PANE: X:37 Y:100 @@ -143,22 +154,35 @@ Background@SETTINGS_MENU: Height:PARENT_BOTTOM - 100 Visible: false Children: - Label@MODE_LABEL: + Label@RENDERER_LABEL: X:0 Y:0 + Width:75 + Height:25 + Text:Renderer: + DropDownButton@GRAPHICS_RENDERER: + X:80 + Y:1 + Width:120 + Height:25 + Font:Regular + Text:OpenGL + Label@MODE_LABEL: + X:0 + Y:30 Width:45 Height:25 Text:Mode: DropDownButton@MODE_DROPDOWN: X:50 - Y:1 + Y:30 Width:170 Height:25 Font:Regular Text:Windowed Container@WINDOW_RESOLUTION: - X:220 - Y:0 + X:225 + Y:30 Children: Label@At: Text:@ @@ -186,14 +210,14 @@ Background@SETTINGS_MENU: Height:25 MaxLength:5 Label@VIDEO_DESC: - Y:25 + Y:60 Width:PARENT_RIGHT Height:25 Font:Tiny Align:Center - Text:Mode/Resolution changes will be applied after the game is restarted + Text:Renderer/Mode/Resolution changes will be applied after the game is restarted. Checkbox@PIXELDOUBLE_CHECKBOX: - Y:50 + Y:90 Width:200 Height:20 Font:Regular diff --git a/mods/ra/maps/bomber-john.oramap b/mods/ra/maps/bomber-john.oramap new file mode 100644 index 0000000000..1305507ac3 Binary files /dev/null and b/mods/ra/maps/bomber-john.oramap differ diff --git a/mods/ra/maps/free-coasts.oramap b/mods/ra/maps/free-coasts.oramap index 63e7a944d6..a01f03d1e4 100644 Binary files a/mods/ra/maps/free-coasts.oramap and b/mods/ra/maps/free-coasts.oramap differ diff --git a/mods/ra/maps/ice-woods.oramap b/mods/ra/maps/ice-woods.oramap new file mode 100644 index 0000000000..4f6919a4bb Binary files /dev/null and b/mods/ra/maps/ice-woods.oramap differ diff --git a/mods/ra/maps/nishnekolymsk.oramap b/mods/ra/maps/nishnekolymsk.oramap new file mode 100644 index 0000000000..9e5839b855 Binary files /dev/null and b/mods/ra/maps/nishnekolymsk.oramap differ diff --git a/mods/ra/rules/civilian.yaml b/mods/ra/rules/civilian.yaml index 77e7de79b8..b660f6b7b2 100644 --- a/mods/ra/rules/civilian.yaml +++ b/mods/ra/rules/civilian.yaml @@ -89,11 +89,9 @@ V18: Inherits: ^CivField BARL: - Inherits: ^Building + Inherits: ^CivBuilding Selectable: Priority: 0 - -RepairableBuilding: - -GivesBuildableArea: Health: HP: 10 Explodes: @@ -101,11 +99,9 @@ BARL: Name: Explosive Barrel BRL3: - Inherits: ^Building + Inherits: ^CivBuilding Selectable: Priority: 0 - -RepairableBuilding: - -GivesBuildableArea: Health: HP: 10 Explodes: diff --git a/mods/ra/rules/structures.yaml b/mods/ra/rules/structures.yaml index 5e2af0a339..8a9ba07358 100644 --- a/mods/ra/rules/structures.yaml +++ b/mods/ra/rules/structures.yaml @@ -497,36 +497,142 @@ PBOX.SHOK: HBOX: Inherits: ^Building -# Buildable: -# Queue: Defense -# BuildPaletteOrder: 30 -# Prerequisites: tent -# Owner: allies -# Hotkey: c - Valued: - Cost: 600 Tooltip: - Name: Camo Pillbox - Description: Hidden defensive structure.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks, Aircraft + Name: Camo Pillbox (Unarmed) Building: Power: -15 -GivesBuildableArea: + Valued: + Cost: 600 Health: HP: 600 Armor: Type: Wood RevealsShroud: Range: 6 + Cloak: + InitialDelay: 125 + CloakDelay: 60 + CloakSound: appear1.aud + UncloakSound: appear1.aud + IronCurtainable: + -AcceptsSupplies: + Turreted: + ROT: 255 + Cargo: + Types: Infantry + MaxWeight: 1 + PipCount: 1 + -EmitInfantryOnSell: +# EmitCargoOnSell: + TransformOnPassenger@e1: + PassengerTypes: e1 + OnEnter: HBOX.e1 + OnExit: HBOX + SkipMakeAnims: true +# TransformOnPassenger@e2: +# PassengerTypes: e2 +# OnEnter: HBOX.e2 +# OnExit: HBOX +# SkipMakeAnims: true + TransformOnPassenger@e3: + PassengerTypes: e3 + OnEnter: HBOX.e3 + OnExit: HBOX + SkipMakeAnims: true + TransformOnPassenger@e4: + PassengerTypes: e4 + OnEnter: HBOX.e4 + OnExit: HBOX + SkipMakeAnims: true + TransformOnPassenger@e7: + PassengerTypes: e7 + OnEnter: HBOX.e7 + OnExit: HBOX + SkipMakeAnims: true + TransformOnPassenger@SHOK: + PassengerTypes: shok + OnEnter: HBOX.shok + OnExit: HBOX + SkipMakeAnims: true + +HBOX.E1: + Inherits: HBOX + Buildable: + Queue: Defense + BuildPaletteOrder: 20 + Prerequisites: tent + Owner: allies + Hotkey: p + Tooltip: + Name: Camo Pillbox (Guns) + Description: Hidden defensive structure.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks, Aircraft + Icon: HBOXICON + RenderBuilding: + Image: HBOX + RenderRangeCircle: + AutoTarget: AttackTurreted: PrimaryWeapon: Vulcan PrimaryLocalOffset: 0,-11,0,0,0 - AutoTarget: - IronCurtainable: - RenderRangeCircle: - -AcceptsSupplies: WithMuzzleFlash: - Turreted: - ROT: 255 + Cargo: + InitialUnits: e1 + +#HBOX.E2: +# Inherits: HBOX +# Tooltip: +# Name: Camo Pillbox (Grenades) +# RenderBuilding: +# Image: HBOX + +HBOX.E3: + Inherits: HBOX + Tooltip: + Name: Camo Pillbox (Rockets) + RenderBuilding: + Image: HBOX + RenderRangeCircle: + AutoTarget: + AttackTurreted: + PrimaryWeapon: Dragon + PrimaryLocalOffset: 0,-11,0,0,0 + +HBOX.E4: + Inherits: HBOX + Tooltip: + Name: Camo Pillbox (Flamethrower) + RenderBuilding: + Image: HBOX + RenderRangeCircle: + AutoTarget: + AttackTurreted: + PrimaryWeapon: Flamer + PrimaryLocalOffset: 0,-11,0,0,0 + +HBOX.E7: + Inherits: HBOX + Tooltip: + Name: Camo Pillbox (Tanya) + RenderBuilding: + Image: HBOX + RenderRangeCircle: + AutoTarget: + AttackTurreted: + PrimaryWeapon: Colt45 + PrimaryLocalOffset: 0,-11,0,0,0 + +HBOX.SHOK: + Inherits: HBOX + Tooltip: + Name: Camo Pillbox (Tesla) + RenderBuilding: + Image: HBOX + RenderRangeCircle: + AutoTarget: + AttackTurreted: + PrimaryWeapon: PortaTesla + PrimaryLocalOffset: 0,-11,0,0,0 GUN: Inherits: ^Building diff --git a/mods/ra/rules/system.yaml b/mods/ra/rules/system.yaml index e928d34cbf..a25b700feb 100644 --- a/mods/ra/rules/system.yaml +++ b/mods/ra/rules/system.yaml @@ -47,7 +47,7 @@ Player: barr: 1% tent: 1% weap: 1% - pbox: 7% + pbox.e1: 7% gun: 7% tsla: 5% ftur: 10% @@ -77,7 +77,7 @@ Player: tent: 1% barr: 1% weap: 1% - pbox: 7% + pbox.e1: 7% gun: 7% ftur: 10% tsla: 5% @@ -98,15 +98,44 @@ Player: 2tnk: 25% 3tnk: 50% SquadSize: 10 + HackyAI@OptiAI: + Name:Eisenhower AI + BuildingFractions: + proc: 25.1% + powr: 35% + tent: 0.1% + barr: 0.1% + weap: 0.1% + fix: 0.1% + dome: 0.1% + atek: 0.1% + stek: 0.1% + UnitsToBuild: + e1: 50% + e2: 1% + e3: 10% + medi: 0.01% + e7: 0.01% + apc: 10% + jeep: 10% + ftrk: 25% + 1tnk: 25% + 2tnk: 50% + 3tnk: 75% + 4tnk: 100% + arty: 30% + v2rl: 30% + SquadSize: 10 HackyAI@ZhukovAI: Name:Zhukov AI BuildingFractions: - proc: 30% + proc: 20% powr: 35% tent: 1% barr: 1% weap: 1% - pbox: 7% + pbox.e1: 4% + hbox.e1: 3% gun: 10% ftur: 10% tsla: 5% @@ -117,16 +146,16 @@ Player: atek: 1% stek: 1% UnitsToBuild: - e1: 40% - e3: 10% + e1: 4% + e3: 1% harv: 10% ftrk: 5% v2rl: 40% arty: 40% - 1tnk: 20% - 2tnk: 20% - 3tnk: 20% - SquadSize: 30 + 1tnk: 15% + 2tnk: 15% + 3tnk: 15% + SquadSize: 25 HackyAI@RommelAI: Name:Rommel AI BuildingFractions: @@ -135,7 +164,7 @@ Player: tent: 1% barr: 1% weap: 1% - pbox: 7% + pbox.e1: 7% gun: 7% ftur: 10% tsla: 5% diff --git a/mono b/mono deleted file mode 100644 index e69de29bb2..0000000000