Merge remote-tracking branch 'openra/bleed' into bleed

This commit is contained in:
Matthias Mailänder
2012-06-18 16:38:17 +02:00
57 changed files with 797 additions and 188 deletions

9
.gitignore vendored
View File

@@ -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?

View File

@@ -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 ) );
}

View File

@@ -105,11 +105,14 @@ namespace OpenRA.FileFormats
levels.Add(new List<MiniYamlNode>());
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 };

View File

@@ -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();
}
}
}

View File

@@ -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" };

View File

@@ -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<MiniYamlNode>();
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 );

View File

@@ -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 };

View File

@@ -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;

View File

@@ -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;

View File

@@ -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":
{

View File

@@ -142,6 +142,7 @@
<Compile Include="Server\Server.cs" />
<Compile Include="Server\ServerOrder.cs" />
<Compile Include="Server\TraitInterfaces.cs" />
<Compile Include="Server\UPnP.cs" />
<Compile Include="Sound.cs" />
<Compile Include="Support\Arguments.cs" />
<Compile Include="Support\PerfHistory.cs" />

View File

@@ -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<ServerTrait>(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<INotifyServerStart>())
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<IStartGame>())
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;
}
}
}
}

137
OpenRA.Game/Server/UPnP.cs Normal file
View File

@@ -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("<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">"+
"<NewRemoteHost></NewRemoteHost><NewExternalPort>{0}</NewExternalPort>"+
"<NewProtocol>{1}</NewProtocol><NewInternalPort>{0}</NewInternalPort>" +
"<NewInternalClient>{2}</NewInternalClient><NewEnabled>1</NewEnabled>" +
"<NewPortMappingDescription>{3}</NewPortMappingDescription>"+
"<NewLeaseDuration>0</NewLeaseDuration></u:AddPortMapping>",
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("<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
"<NewRemoteHost></NewRemoteHost><NewExternalPort>{0}</NewExternalPort>"+
"<NewProtocol>{1}</NewProtocol></u:DeletePortMapping>", 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, "<u:GetExternalIPAddress xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
"</u:GetExternalIPAddress>", "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 = "<?xml version=\"1.0\"?>" +
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
"<s:Body>" +
soap +
"</s:Body>" +
"</s:Envelope>";
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;
}
}
}

View File

@@ -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;

View File

@@ -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<EvaAlertsInfo>();
if(cashtickallowed > 0) {
cashtickallowed = cashtickallowed - 1;
}
OreCapacity = self.World.ActorsWithTrait<IStoreOre>()
.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<EvaAlertsInfo>();
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<EvaAlertsInfo>();
if (
Game.Settings.Sound.SoundCashTickType == SoundCashTicks.Extreme ||
(Game.Settings.Sound.SoundCashTickType == SoundCashTicks.Normal && cashtickallowed == 0)
) {
Sound.PlayToPlayer(self.Owner, eva.CashTickDown);
cashtickallowed = 3;
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -183,7 +183,7 @@ namespace OpenRA
while (frameEndActions.Count != 0)
frameEndActions.Dequeue()(this);
Game.viewport.Tick();
}
public IEnumerable<Actor> Actors { get { return actors; } }

View File

@@ -15,31 +15,43 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Cnc
{
class RenderCargoInfo : ITraitInfo, Requires<CargoInfo>
public class RenderCargoInfo : ITraitInfo, Requires<CargoInfo>
{
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<Cargo>();
facing = self.TraitOrDefault<IFacing>();
move = self.Trait<IMove>();
Info = info;
}
public IEnumerable<Renderable> ModifyRender(Actor self, IEnumerable<Renderable> r)
{
foreach (var c in cargo.Passengers)
{
c.Trait<ITeleportable>().SetPxPosition( c, self.Trait<IHasLocation>().PxPosition );
if (facing != null && c.HasTrait<IFacing>())
c.Trait<IFacing>().Facing = facing.Facing;
c.Trait<ITeleportable>().SetPxPosition( c, move.PxPosition );
var cargoFacing = c.TraitOrDefault<IFacing>();
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)));
}
}
}

View File

@@ -86,6 +86,10 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic
Game.viewport.Zoom = graphicsSettings.PixelDouble ? 2 : 1;
};
var showShellmapCheckbox = generalPane.Get<CheckboxWidget>("SHOW_SHELLMAP");
showShellmapCheckbox.IsChecked = () => gameSettings.ShowShellmap;
showShellmapCheckbox.OnClick = () => gameSettings.ShowShellmap ^= true;
generalPane.Get("WINDOW_RESOLUTION").IsVisible = () => graphicsSettings.Mode == WindowMode.Windowed;
var windowWidth = generalPane.Get<TextFieldWidget>("WINDOW_WIDTH");
windowWidth.Text = graphicsSettings.WindowedSize.X.ToString();

View File

@@ -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<Capturable>();
if (capturable != null && capturable.CaptureInProgress && capturable.Captor.Owner.Stances[self.Owner] == Stance.Ally)
return NextActivity;
var sellable = target.TraitOrDefault<Sellable>();
if (sellable != null && sellable.Selling) return NextActivity;
if (sellable != null && sellable.Selling)
return NextActivity;
target.Trait<Capturable>().BeginCapture(target, self);
self.World.AddFrameEndTask(w => self.Destroy());

View File

@@ -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<INotifyCapture>())
t.OnCapture(self, captor, self.Owner, captor.Owner);
t.OnCapture(self, Captor, self.Owner, Captor.Owner);
foreach (var t in captor.World.ActorsWithTrait<INotifyOtherCaptured>())
t.Trait.OnActorCaptured(t.Actor, self, captor, self.Owner, captor.Owner);
foreach (var t in Captor.World.ActorsWithTrait<INotifyOtherCaptured>())
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<Cargo>();
if (cargo == null)
return;
foreach (var c in cargo.Passengers)
c.Owner = captor;
}
}
}

View File

@@ -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<Capturable>();
return c != null && ( !c.CaptureInProgress || c.Captor.Owner.Stances[self.Owner] != Stance.Ally );
}
}
class CaptureOrderTargeter : UnitTraitOrderTargeter<Capturable>
{
readonly string[] captureTypes;
public CaptureOrderTargeter(string[] captureTypes)
: base( "CaptureActor", 6, "enter", true, true )
readonly Func<Actor, bool> useEnterCursor;
public CaptureOrderTargeter(string[] captureTypes, Func<Actor, bool> 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;
}

View File

@@ -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)

View File

@@ -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;

View File

@@ -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

View File

@@ -13,7 +13,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
class ExplodesInfo : TraitInfo<Explodes>
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<ExplodesInfo>().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<IExplodeModifier>().All(a => a.ShouldExplode(self));
var info = self.Info.Traits.Get<ExplodesInfo>();
return shouldExplode ? info.Weapon : info.EmptyWeapon;
return shouldExplode ? Info.Weapon : Info.EmptyWeapon;
}
}
}

View File

@@ -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<GpsWatcher>();
if (gpsWatcher != null && (gpsWatcher.Granted || gpsWatcher.GrantedAllies))
return;
self.Owner.Shroud.ResetExploration();
}
}
}
}

View File

@@ -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; } }

View File

@@ -95,6 +95,14 @@ namespace OpenRA.Mods.RA.Move
public override Activity Tick( Actor self )
{
var mobile = self.Trait<Mobile>();
var info = self.Info.Traits.Get<MobileInfo>();
if (mobile.Altitude != info.Altitude)
{
if (mobile.Altitude < info.Altitude)
++mobile.Altitude;
return this;
}
if (destination == mobile.toCell)
return NextActivity;

View File

@@ -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) { }

View File

@@ -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) { }

View File

@@ -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<IMove>();
/* 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);
}

View File

@@ -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)

View File

@@ -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"));

View File

@@ -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<CheckboxWidget>("ADVERTISE_CHECKBOX");
advertiseCheckbox.IsChecked = () => advertiseOnline;
advertiseCheckbox.OnClick = () => advertiseOnline ^= true;
var UPnPCheckbox = panel.Get<CheckboxWidget>("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();

View File

@@ -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<SliderWidget>("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<DropDownButtonWidget>("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<DropDownButtonWidget>("GRAPHICS_RENDERER");
GraphicsRendererDropdown.OnMouseDown = _ => ShowRendererDropdown(GraphicsRendererDropdown, gs);
GraphicsRendererDropdown.GetText = () => gs.Renderer == "Gl" ?
"OpenGL" : gs.Renderer == "Cg" ? "Cg Toolkit" : "OpenGL";
var windowModeDropdown = display.Get<DropDownButtonWidget>("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<string, SoundCashTicks>()
{
{ "Extreme", SoundCashTicks.Extreme },
{ "Normal", SoundCashTicks.Normal },
{ "Disabled", SoundCashTicks.Disabled },
};
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (o, itemTemplate) =>
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => audio.SoundCashTickType == options[o],
() => audio.SoundCashTickType = options[o]);
item.Get<LabelWidget>("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<string, WindowMode>()
@@ -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<string, string>()
{
{ "OpenGL", "Gl" },
{ "Cg Toolkit", "Cg" },
};
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (o, itemTemplate) =>
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => s.Renderer == options[o],
() => s.Renderer = options[o]);
item.Get<LabelWidget>("LABEL").GetText = () => o;
return item;
};
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options.Keys, setupItem);
return true;
}
}
}

View File

@@ -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));
}
}
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -69,8 +69,9 @@
^Helicopter:
AppearsOnRadar:
UseLocation: yes
TargetableUnit:
TargetableAircraft:
TargetTypes: Air
GroundedTargetTypes: Ground
SelectionDecorations:
Selectable:
Voice: VehicleVoice

View File

@@ -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:

View File

@@ -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:

View File

@@ -94,6 +94,10 @@ explosion:
6w: atomsfx
Start: 0
Length: *
chemball: chemball
Start: 0
Length: *
rank:
rank:
Start: 0

View File

@@ -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
Damage: 12

View File

@@ -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

View File

@@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -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:

View File

@@ -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

View File

@@ -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%

0
mono
View File