Compare commits
81 Commits
playtest-2
...
release-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd8dc18bad | ||
|
|
dd50004d91 | ||
|
|
b4033c609e | ||
|
|
9800b3513e | ||
|
|
047f94613b | ||
|
|
74c69f835c | ||
|
|
bea6523f5e | ||
|
|
38dab424fb | ||
|
|
605f3b65a9 | ||
|
|
1f7a483fd6 | ||
|
|
d82549ec00 | ||
|
|
d003885bca | ||
|
|
f907403ca7 | ||
|
|
29c13a4c26 | ||
|
|
379ece7592 | ||
|
|
5b815ca2cf | ||
|
|
391c378118 | ||
|
|
bd8aeb3cae | ||
|
|
bab4cac2d7 | ||
|
|
98664ce3e5 | ||
|
|
57065de55e | ||
|
|
2dd0e05423 | ||
|
|
9b158b459c | ||
|
|
80b06d1dd2 | ||
|
|
a33321390f | ||
|
|
1607173769 | ||
|
|
bf957f09ff | ||
|
|
dbc8b0a99a | ||
|
|
799b07cd1e | ||
|
|
c83b8b712e | ||
|
|
c7adfd43a0 | ||
|
|
55c1b6a663 | ||
|
|
2693f9de1c | ||
|
|
dc684e244e | ||
|
|
8334e9adeb | ||
|
|
e55f2f3bee | ||
|
|
59ee9bcb96 | ||
|
|
b3ba4f5ca2 | ||
|
|
e628d756fb | ||
|
|
4f55edad74 | ||
|
|
43a30f6497 | ||
|
|
a84815a832 | ||
|
|
d3dae5c34f | ||
|
|
8a666b973f | ||
|
|
4a039166e4 | ||
|
|
25a63530d3 | ||
|
|
5349db0384 | ||
|
|
60244d4910 | ||
|
|
a7251b7e95 | ||
|
|
6e56349a26 | ||
|
|
cb553fbe15 | ||
|
|
955f39620b | ||
|
|
af5402d41c | ||
|
|
aaa1484643 | ||
|
|
fb7b79e8fc | ||
|
|
1f0d98344b | ||
|
|
37155ec9dd | ||
|
|
5e3f0721ce | ||
|
|
90a7d1f3e9 | ||
|
|
3971b99363 | ||
|
|
b99c4903a6 | ||
|
|
f7e5cce0ab | ||
|
|
19983e9e7f | ||
|
|
b84bd1762b | ||
|
|
9623bbebc8 | ||
|
|
7becb92a00 | ||
|
|
a812b937f3 | ||
|
|
8bbced1582 | ||
|
|
a4eb40efe4 | ||
|
|
214d7325bd | ||
|
|
be09392c35 | ||
|
|
f6e892a2dd | ||
|
|
cdd70e7daf | ||
|
|
4cf98effa4 | ||
|
|
1d8048c133 | ||
|
|
ecbedad308 | ||
|
|
31c99d8071 | ||
|
|
303a2be612 | ||
|
|
ece2f8494e | ||
|
|
23f9998c7e | ||
|
|
fcab4e9eed |
@@ -489,6 +489,11 @@ namespace OpenRA
|
||||
return int.TryParse(s, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out i);
|
||||
}
|
||||
|
||||
public static bool TryParseInt64Invariant(string s, out long i)
|
||||
{
|
||||
return long.TryParse(s, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out i);
|
||||
}
|
||||
|
||||
public static bool IsTraitEnabled(this object trait)
|
||||
{
|
||||
return trait as IDisabledTrait == null || !(trait as IDisabledTrait).IsTraitDisabled;
|
||||
|
||||
@@ -14,6 +14,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -71,9 +72,8 @@ namespace OpenRA
|
||||
|
||||
static string TimestampedFilename(bool includemilliseconds = false)
|
||||
{
|
||||
return includemilliseconds
|
||||
? DateTime.UtcNow.ToString("OpenRA-yyyy-MM-ddTHHmmssfffZ")
|
||||
: DateTime.UtcNow.ToString("OpenRA-yyyy-MM-ddTHHmmssZ");
|
||||
var format = includemilliseconds ? "yyyy-MM-ddTHHmmssfffZ" : "yyyy-MM-ddTHHmmssZ";
|
||||
return "OpenRA-" + DateTime.UtcNow.ToString(format, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
static void JoinInner(OrderManager om)
|
||||
@@ -96,7 +96,7 @@ namespace OpenRA
|
||||
|
||||
// More accurate replacement for Environment.TickCount
|
||||
static Stopwatch stopwatch = Stopwatch.StartNew();
|
||||
public static int RunTime { get { return (int)stopwatch.ElapsedMilliseconds; } }
|
||||
public static long RunTime { get { return stopwatch.ElapsedMilliseconds; } }
|
||||
|
||||
public static int RenderFrame = 0;
|
||||
public static int NetFrameNumber { get { return OrderManager.NetFrameNumber; } }
|
||||
@@ -260,7 +260,6 @@ namespace OpenRA
|
||||
|
||||
Log.AddChannel("perf", "perf.log");
|
||||
Log.AddChannel("debug", "debug.log");
|
||||
Log.AddChannel("sync", "syncreport.log");
|
||||
Log.AddChannel("server", "server.log");
|
||||
Log.AddChannel("sound", "sound.log");
|
||||
Log.AddChannel("graphics", "graphics.log");
|
||||
@@ -736,7 +735,7 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
else
|
||||
Thread.Sleep(nextUpdate - now);
|
||||
Thread.Sleep((int)(nextUpdate - now));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
public static int TicksSinceLastMove = 0;
|
||||
public static long TicksSinceLastMove = 0;
|
||||
public static int2 LastMousePos;
|
||||
|
||||
float ClosestTo(float[] collection, float target)
|
||||
|
||||
@@ -150,9 +150,10 @@ namespace OpenRA
|
||||
foreach (var kv in yaml)
|
||||
maps[kv.Key].UpdateRemoteSearch(MapStatus.DownloadAvailable, kv.Value, mapDetailsReceived);
|
||||
}
|
||||
catch
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Write("debug", "Can't parse remote map search data:\n{0}", data);
|
||||
Log.Write("debug", "Exception: {0}", e);
|
||||
if (queryFailed != null)
|
||||
queryFailed();
|
||||
}
|
||||
|
||||
@@ -367,7 +367,15 @@ namespace OpenRA
|
||||
spawns[j / 2] = new CPos(r.spawnpoints[j], r.spawnpoints[j + 1]);
|
||||
newData.SpawnPoints = spawns;
|
||||
newData.GridType = r.map_grid_type;
|
||||
newData.Preview = new Bitmap(new MemoryStream(Convert.FromBase64String(r.minimap)));
|
||||
try
|
||||
{
|
||||
newData.Preview = new Bitmap(new MemoryStream(Convert.FromBase64String(r.minimap)));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Write("debug", "Failed parsing mapserver minimap response: {0}", e);
|
||||
newData.Preview = null;
|
||||
}
|
||||
|
||||
var playersString = Encoding.UTF8.GetString(Convert.FromBase64String(r.players_block));
|
||||
newData.Players = new MapPlayers(MiniYaml.FromString(playersString));
|
||||
@@ -389,7 +397,10 @@ namespace OpenRA
|
||||
return Pair.New(rules, flagged);
|
||||
});
|
||||
}
|
||||
catch (Exception) { }
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Write("debug", "Failed parsing mapserver response: {0}", e);
|
||||
}
|
||||
|
||||
// Commit updated data before running the callbacks
|
||||
innerData = newData;
|
||||
|
||||
@@ -64,7 +64,8 @@ namespace OpenRA
|
||||
|
||||
public static Order Deserialize(World world, BinaryReader r)
|
||||
{
|
||||
switch (r.ReadByte())
|
||||
var magic = r.ReadByte();
|
||||
switch (magic)
|
||||
{
|
||||
case 0xFF:
|
||||
{
|
||||
@@ -98,7 +99,10 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
{
|
||||
Log.Write("debug", "Received unknown order with magic {0}", magic);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace OpenRA.Network
|
||||
public int LocalFrameNumber;
|
||||
public int FramesAhead = 0;
|
||||
|
||||
public int LastTickTime = Game.RunTime;
|
||||
public long LastTickTime = Game.RunTime;
|
||||
|
||||
public bool GameStarted { get { return NetFrameNumber != 0; } }
|
||||
public IConnection Connection { get; private set; }
|
||||
|
||||
@@ -135,9 +135,9 @@ namespace OpenRA.Network
|
||||
public class ClientPing
|
||||
{
|
||||
public int Index;
|
||||
public int Latency = -1;
|
||||
public int LatencyJitter = -1;
|
||||
public int[] LatencyHistory = { };
|
||||
public long Latency = -1;
|
||||
public long LatencyJitter = -1;
|
||||
public long[] LatencyHistory = { };
|
||||
|
||||
public static ClientPing Deserialize(MiniYaml data)
|
||||
{
|
||||
|
||||
@@ -18,6 +18,7 @@ using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
using System.Globalization;
|
||||
using NamesValuesPair = Pair<string[], object[]>;
|
||||
|
||||
class SyncReport
|
||||
@@ -92,11 +93,15 @@ namespace OpenRA.Network
|
||||
|
||||
internal void DumpSyncReport(int frame, IEnumerable<FrameData.ClientOrder> orders)
|
||||
{
|
||||
var reportName = "syncreport-" + DateTime.UtcNow.ToString("yyyy-MM-ddTHHmmssZ", CultureInfo.InvariantCulture) + ".log";
|
||||
Log.AddChannel("sync", reportName);
|
||||
|
||||
foreach (var r in syncReports)
|
||||
{
|
||||
if (r.Frame == frame)
|
||||
{
|
||||
var mod = Game.ModData.Manifest.Metadata;
|
||||
Log.AddChannel("sync", "syncreport.log");
|
||||
Log.Write("sync", "Player: {0} ({1} {2} {3})", Game.Settings.Player.Name, Platform.CurrentPlatform, Environment.OSVersion, Platform.RuntimeVersion);
|
||||
Log.Write("sync", "Game ID: {0} (Mod: {1} at Version {2})", orderManager.LobbyInfo.GlobalSettings.GameUid, mod.Title, mod.Version);
|
||||
Log.Write("sync", "Sync for net frame {0} -------------", r.Frame);
|
||||
|
||||
@@ -39,8 +39,8 @@ namespace OpenRA
|
||||
var kernelName = p.StandardOutput.ReadToEnd();
|
||||
if (kernelName.Contains("Darwin"))
|
||||
return PlatformType.OSX;
|
||||
else
|
||||
return PlatformType.Linux;
|
||||
|
||||
return PlatformType.Linux;
|
||||
}
|
||||
catch { }
|
||||
|
||||
@@ -82,7 +82,6 @@ namespace OpenRA
|
||||
case PlatformType.OSX:
|
||||
dir += "/Library/Application Support/OpenRA";
|
||||
break;
|
||||
case PlatformType.Linux:
|
||||
default:
|
||||
dir += "/.openra";
|
||||
break;
|
||||
@@ -101,12 +100,15 @@ namespace OpenRA
|
||||
{
|
||||
path = path.TrimEnd(new char[] { ' ', '\t' });
|
||||
|
||||
// paths starting with ^ are relative to the support dir
|
||||
if (path.StartsWith("^"))
|
||||
// Paths starting with ^ are relative to the support dir
|
||||
if (path.StartsWith("^", StringComparison.Ordinal))
|
||||
path = SupportDir + path.Substring(1);
|
||||
|
||||
// paths starting with . are relative to the game dir
|
||||
if (path.StartsWith("./") || path.StartsWith(".\\"))
|
||||
// Paths starting with . are relative to the game dir
|
||||
if (path == ".")
|
||||
return GameDir;
|
||||
|
||||
if (path.StartsWith("./", StringComparison.Ordinal) || path.StartsWith(".\\", StringComparison.Ordinal))
|
||||
path = GameDir + path.Substring(2);
|
||||
|
||||
return path;
|
||||
@@ -121,10 +123,10 @@ namespace OpenRA
|
||||
/// <summary>Replace the full path prefix with the special notation characters ^ or .</summary>
|
||||
public static string UnresolvePath(string path)
|
||||
{
|
||||
if (path.StartsWith(SupportDir))
|
||||
if (path.StartsWith(SupportDir, StringComparison.Ordinal))
|
||||
path = "^" + path.Substring(SupportDir.Length);
|
||||
|
||||
if (path.StartsWith(GameDir))
|
||||
if (path.StartsWith(GameDir, StringComparison.Ordinal))
|
||||
path = "./" + path.Substring(GameDir.Length);
|
||||
|
||||
return path;
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace OpenRA.Primitives
|
||||
{
|
||||
readonly List<DelayedAction> actions = new List<DelayedAction>();
|
||||
|
||||
public void Add(Action a, int desiredTime)
|
||||
public void Add(Action a, long desiredTime)
|
||||
{
|
||||
if (a == null)
|
||||
throw new ArgumentNullException("a");
|
||||
@@ -34,7 +34,7 @@ namespace OpenRA.Primitives
|
||||
}
|
||||
}
|
||||
|
||||
public void PerformActions(int currentTime)
|
||||
public void PerformActions(long currentTime)
|
||||
{
|
||||
DelayedAction[] pendingActions;
|
||||
lock (actions)
|
||||
@@ -67,10 +67,10 @@ namespace OpenRA.Primitives
|
||||
|
||||
struct DelayedAction : IComparable<DelayedAction>
|
||||
{
|
||||
public readonly int Time;
|
||||
public readonly long Time;
|
||||
public readonly Action Action;
|
||||
|
||||
public DelayedAction(Action action, int time)
|
||||
public DelayedAction(Action action, long time)
|
||||
{
|
||||
Action = action;
|
||||
Time = time;
|
||||
|
||||
@@ -26,9 +26,9 @@ namespace OpenRA.Server
|
||||
public int Frame = 0;
|
||||
public int MostRecentFrame = 0;
|
||||
|
||||
public int TimeSinceLastResponse { get { return Game.RunTime - lastReceivedTime; } }
|
||||
public long TimeSinceLastResponse { get { return Game.RunTime - lastReceivedTime; } }
|
||||
public bool TimeoutMessageShown = false;
|
||||
int lastReceivedTime = 0;
|
||||
long lastReceivedTime = 0;
|
||||
|
||||
/* client data */
|
||||
public int PlayerIndex;
|
||||
|
||||
@@ -152,6 +152,7 @@ namespace OpenRA.Server
|
||||
Map = settings.Map,
|
||||
ServerName = settings.Name,
|
||||
EnableSingleplayer = settings.EnableSingleplayer || !dedicated,
|
||||
GameUid = Guid.NewGuid().ToString()
|
||||
}
|
||||
};
|
||||
|
||||
@@ -185,17 +186,21 @@ namespace OpenRA.Server
|
||||
foreach (var s in checkRead)
|
||||
{
|
||||
if (s == listener.Server)
|
||||
{
|
||||
AcceptConnection();
|
||||
else if (PreConns.Count > 0)
|
||||
{
|
||||
var p = PreConns.SingleOrDefault(c => c.Socket == s);
|
||||
if (p != null) p.ReadData(this);
|
||||
continue;
|
||||
}
|
||||
else if (Conns.Count > 0)
|
||||
|
||||
var preConn = PreConns.SingleOrDefault(c => c.Socket == s);
|
||||
if (preConn != null)
|
||||
{
|
||||
var conn = Conns.SingleOrDefault(c => c.Socket == s);
|
||||
if (conn != null) conn.ReadData(this);
|
||||
preConn.ReadData(this);
|
||||
continue;
|
||||
}
|
||||
|
||||
var conn = Conns.SingleOrDefault(c => c.Socket == s);
|
||||
if (conn != null)
|
||||
conn.ReadData(this);
|
||||
}
|
||||
|
||||
foreach (var t in serverTraits.WithInterface<ITick>())
|
||||
@@ -501,8 +506,8 @@ namespace OpenRA.Server
|
||||
break;
|
||||
case "Pong":
|
||||
{
|
||||
int pingSent;
|
||||
if (!OpenRA.Exts.TryParseIntegerInvariant(so.Data, out pingSent))
|
||||
long pingSent;
|
||||
if (!OpenRA.Exts.TryParseInt64Invariant(so.Data, out pingSent))
|
||||
{
|
||||
Log.Write("server", "Invalid order pong payload: {0}", so.Data);
|
||||
break;
|
||||
@@ -563,6 +568,7 @@ namespace OpenRA.Server
|
||||
DispatchOrdersToClients(toDrop, 0, new ServerOrder("Disconnected", "").Serialize());
|
||||
|
||||
LobbyInfo.Clients.RemoveAll(c => c.Index == toDrop.PlayerIndex);
|
||||
LobbyInfo.ClientPings.RemoveAll(p => p.Index == toDrop.PlayerIndex);
|
||||
|
||||
// Client was the server admin
|
||||
// TODO: Reassign admin for game in progress via an order
|
||||
|
||||
@@ -76,6 +76,7 @@ namespace OpenRA
|
||||
public void Initialize(ISoundLoader[] loaders, IReadOnlyFileSystem fileSystem)
|
||||
{
|
||||
sounds = new Cache<string, ISoundSource>(s => LoadSound(loaders, fileSystem, s));
|
||||
currentSounds = new Dictionary<uint, ISound>();
|
||||
music = null;
|
||||
currentMusic = null;
|
||||
video = null;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
@@ -48,7 +49,8 @@ namespace OpenRA
|
||||
|
||||
static void FatalError(Exception e)
|
||||
{
|
||||
Log.AddChannel("exception", "exception.log");
|
||||
var exceptionName = "exception-" + DateTime.UtcNow.ToString("yyyy-MM-ddTHHmmssZ", CultureInfo.InvariantCulture) + ".log";
|
||||
Log.AddChannel("exception", exceptionName);
|
||||
|
||||
if (Game.ModData != null)
|
||||
{
|
||||
|
||||
@@ -91,11 +91,13 @@ namespace OpenRA
|
||||
var ax = Math.Abs(x);
|
||||
|
||||
// Find the closest angle that satisfies y = x*tan(theta)
|
||||
var bestVal = int.MaxValue;
|
||||
// Uses a long to store bestVal to eliminate integer overflow issues in the common cases
|
||||
// (may still fail for unrealistically large ax and ay)
|
||||
var bestVal = long.MaxValue;
|
||||
var bestAngle = 0;
|
||||
for (var i = 0; i < 256; i += stride)
|
||||
{
|
||||
var val = Math.Abs(1024 * ay - ax * TanTable[i]);
|
||||
var val = Math.Abs(1024 * ay - (long)ax * TanTable[i]);
|
||||
if (val < bestVal)
|
||||
{
|
||||
bestVal = val;
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace OpenRA.Widgets
|
||||
{
|
||||
public static Widget Root = new RootWidget();
|
||||
|
||||
public static int LastTickTime = Game.RunTime;
|
||||
public static long LastTickTime = Game.RunTime;
|
||||
|
||||
static readonly Stack<Widget> WindowList = new Stack<Widget>();
|
||||
|
||||
|
||||
@@ -964,8 +964,12 @@ namespace OpenRA.Mods.Common.AI
|
||||
if (!mcv.IsIdle)
|
||||
continue;
|
||||
|
||||
// If we lack a base, we need to make sure we don't restrict deployment of the MCV to the base!
|
||||
var restrictToBase =
|
||||
Info.RestrictMCVDeploymentFallbackToBase &&
|
||||
CountBuildingByCommonName(Info.BuildingCommonNames.ConstructionYard, Player) > 0;
|
||||
var factType = mcv.Info.TraitInfo<TransformsInfo>().IntoActor;
|
||||
var desiredLocation = ChooseBuildLocation(factType, Info.RestrictMCVDeploymentFallbackToBase, BuildingType.Building);
|
||||
var desiredLocation = ChooseBuildLocation(factType, restrictToBase, BuildingType.Building);
|
||||
if (desiredLocation == null)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Activities;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Activities
|
||||
{
|
||||
public class AllowYieldingReservation : Activity
|
||||
{
|
||||
readonly Aircraft aircraft;
|
||||
|
||||
public AllowYieldingReservation(Actor self)
|
||||
{
|
||||
aircraft = self.Trait<Aircraft>();
|
||||
}
|
||||
|
||||
public override Activity Tick(Actor self)
|
||||
{
|
||||
aircraft.AllowYieldingReservation();
|
||||
return NextActivity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ namespace OpenRA.Mods.Common.Activities
|
||||
|
||||
// TODO: This should check whether there is ammo left that is actually suitable for the target
|
||||
if (ammoPools.All(x => !x.Info.SelfReloads && !x.HasAmmo()))
|
||||
return ActivityUtils.SequenceActivities(new ReturnToBase(self), this);
|
||||
return ActivityUtils.SequenceActivities(new ReturnToBase(self, aircraft.Info.AbortOnResupply), this);
|
||||
|
||||
if (attackPlane != null)
|
||||
attackPlane.DoAttack(self, target);
|
||||
|
||||
@@ -60,14 +60,14 @@ namespace OpenRA.Mods.Common.Activities
|
||||
{
|
||||
var newTarget = Target.FromCell(self.World, self.World.Map.CellContaining(target.CenterPosition));
|
||||
|
||||
self.CancelActivity();
|
||||
Cancel(self);
|
||||
self.SetTargetLine(newTarget, Color.Green);
|
||||
return ActivityUtils.SequenceActivities(new HeliFly(self, newTarget));
|
||||
return new HeliFly(self, newTarget);
|
||||
}
|
||||
|
||||
// If any AmmoPool is depleted and no weapon is valid against target, return to helipad to reload and then resume the activity
|
||||
if (ammoPools.Any(x => !x.Info.SelfReloads && !x.HasAmmo()) && !attackHeli.HasAnyValidWeapons(target))
|
||||
return ActivityUtils.SequenceActivities(new HeliReturnToBase(self), this);
|
||||
return ActivityUtils.SequenceActivities(new HeliReturnToBase(self, helicopter.Info.AbortOnResupply), this);
|
||||
|
||||
var dist = target.CenterPosition - self.CenterPosition;
|
||||
|
||||
|
||||
@@ -19,10 +19,14 @@ namespace OpenRA.Mods.Common.Activities
|
||||
public class HeliReturnToBase : Activity
|
||||
{
|
||||
readonly Aircraft heli;
|
||||
readonly bool alwaysLand;
|
||||
readonly bool abortOnResupply;
|
||||
|
||||
public HeliReturnToBase(Actor self)
|
||||
public HeliReturnToBase(Actor self, bool abortOnResupply, bool alwaysLand = true)
|
||||
{
|
||||
heli = self.Trait<Aircraft>();
|
||||
this.alwaysLand = alwaysLand;
|
||||
this.abortOnResupply = abortOnResupply;
|
||||
}
|
||||
|
||||
public Actor ChooseHelipad(Actor self)
|
||||
@@ -68,17 +72,36 @@ namespace OpenRA.Mods.Common.Activities
|
||||
}
|
||||
}
|
||||
|
||||
heli.MakeReservation(dest);
|
||||
|
||||
var exit = dest.Info.TraitInfos<ExitInfo>().FirstOrDefault();
|
||||
var offset = (exit != null) ? exit.SpawnOffset : WVec.Zero;
|
||||
|
||||
if (ShouldLandAtBuilding(self, dest))
|
||||
{
|
||||
heli.MakeReservation(dest);
|
||||
|
||||
return ActivityUtils.SequenceActivities(
|
||||
new HeliFly(self, Target.FromPos(dest.CenterPosition + offset)),
|
||||
new Turn(self, initialFacing),
|
||||
new HeliLand(self, false),
|
||||
new ResupplyAircraft(self),
|
||||
!abortOnResupply ? NextActivity : null);
|
||||
}
|
||||
|
||||
return ActivityUtils.SequenceActivities(
|
||||
new HeliFly(self, Target.FromPos(dest.CenterPosition + offset)),
|
||||
new Turn(self, initialFacing),
|
||||
new HeliLand(self, false),
|
||||
new ResupplyAircraft(self),
|
||||
NextActivity);
|
||||
}
|
||||
|
||||
bool ShouldLandAtBuilding(Actor self, Actor dest)
|
||||
{
|
||||
if (alwaysLand)
|
||||
return true;
|
||||
|
||||
if (heli.Info.RepairBuildings.Contains(dest.Info.Name) && self.GetDamageState() != DamageState.Undamaged)
|
||||
return true;
|
||||
|
||||
return heli.Info.RearmBuildings.Contains(dest.Info.Name) && self.TraitsImplementing<AmmoPool>()
|
||||
.Any(p => !p.Info.SelfReloads && !p.FullAmmo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,16 +42,18 @@ namespace OpenRA.Mods.Common.Activities
|
||||
{
|
||||
inner = ActivityUtils.SequenceActivities(
|
||||
aircraft.GetResupplyActivities(host)
|
||||
.Append(new CallFunc(() => aircraft.MayYieldReservation = true))
|
||||
.Append(new AllowYieldingReservation(self))
|
||||
.Append(new WaitFor(() => NextActivity != null || aircraft.ReservedActor == null))
|
||||
.ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Helicopters should take off from their helipad immediately after resupplying.
|
||||
// HACK: NextActivity needs to be appended here because otherwise TakeOff does stupid things.
|
||||
// HACK: Append NextActivity to TakeOff to avoid moving to the Rallypoint (if NextActivity is non-null).
|
||||
inner = ActivityUtils.SequenceActivities(
|
||||
aircraft.GetResupplyActivities(host).Append(new TakeOff(self)).Append(NextActivity).ToArray());
|
||||
aircraft.GetResupplyActivities(host)
|
||||
.Append(new AllowYieldingReservation(self))
|
||||
.Append(new TakeOff(self)).Append(NextActivity).ToArray());
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -22,13 +22,17 @@ namespace OpenRA.Mods.Common.Activities
|
||||
{
|
||||
readonly Aircraft plane;
|
||||
readonly AircraftInfo planeInfo;
|
||||
readonly bool alwaysLand;
|
||||
readonly bool abortOnResupply;
|
||||
bool isCalculated;
|
||||
Actor dest;
|
||||
WPos w1, w2, w3;
|
||||
|
||||
public ReturnToBase(Actor self, Actor dest = null)
|
||||
public ReturnToBase(Actor self, bool abortOnResupply, Actor dest = null, bool alwaysLand = true)
|
||||
{
|
||||
this.dest = dest;
|
||||
this.alwaysLand = alwaysLand;
|
||||
this.abortOnResupply = abortOnResupply;
|
||||
plane = self.Trait<Aircraft>();
|
||||
planeInfo = self.Info.TraitInfo<AircraftInfo>();
|
||||
}
|
||||
@@ -51,8 +55,6 @@ namespace OpenRA.Mods.Common.Activities
|
||||
if (dest == null)
|
||||
return;
|
||||
|
||||
plane.MakeReservation(dest);
|
||||
|
||||
var landPos = dest.CenterPosition;
|
||||
var altitude = planeInfo.CruiseAltitude.Length;
|
||||
|
||||
@@ -94,6 +96,18 @@ namespace OpenRA.Mods.Common.Activities
|
||||
isCalculated = true;
|
||||
}
|
||||
|
||||
bool ShouldLandAtBuilding(Actor self, Actor dest)
|
||||
{
|
||||
if (alwaysLand)
|
||||
return true;
|
||||
|
||||
if (planeInfo.RepairBuildings.Contains(dest.Info.Name) && self.GetDamageState() != DamageState.Undamaged)
|
||||
return true;
|
||||
|
||||
return planeInfo.RearmBuildings.Contains(dest.Info.Name) && self.TraitsImplementing<AmmoPool>()
|
||||
.Any(p => !p.Info.SelfReloads && !p.FullAmmo());
|
||||
}
|
||||
|
||||
public override Activity Tick(Actor self)
|
||||
{
|
||||
if (IsCanceled || self.IsDead)
|
||||
@@ -112,7 +126,11 @@ namespace OpenRA.Mods.Common.Activities
|
||||
new FlyCircleTimed(self, plane.Info.NumberOfTicksToVerifyAvailableAirport),
|
||||
this);
|
||||
else
|
||||
{
|
||||
// Prevent an infinite loop in case we'd return to the activity that called ReturnToBase in the first place. Go idle instead.
|
||||
Cancel(self);
|
||||
return NextActivity;
|
||||
}
|
||||
}
|
||||
|
||||
List<Activity> landingProcedures = new List<Activity>();
|
||||
@@ -124,9 +142,17 @@ namespace OpenRA.Mods.Common.Activities
|
||||
|
||||
// Fix a problem when the airplane is send to resupply near the airport
|
||||
landingProcedures.Add(new Fly(self, Target.FromPos(w3), WDist.Zero, new WDist(turnRadius / 2)));
|
||||
landingProcedures.Add(new Land(self, Target.FromActor(dest)));
|
||||
landingProcedures.Add(new ResupplyAircraft(self));
|
||||
landingProcedures.Add(NextActivity);
|
||||
|
||||
if (ShouldLandAtBuilding(self, dest))
|
||||
{
|
||||
plane.MakeReservation(dest);
|
||||
|
||||
landingProcedures.Add(new Land(self, Target.FromActor(dest)));
|
||||
landingProcedures.Add(new ResupplyAircraft(self));
|
||||
}
|
||||
|
||||
if (!abortOnResupply)
|
||||
landingProcedures.Add(NextActivity);
|
||||
|
||||
return ActivityUtils.SequenceActivities(landingProcedures.ToArray());
|
||||
}
|
||||
|
||||
@@ -28,9 +28,6 @@ namespace OpenRA.Mods.Common.Activities
|
||||
|
||||
public override Activity Tick(Actor self)
|
||||
{
|
||||
if (NextActivity == null)
|
||||
self.CancelActivity();
|
||||
|
||||
aircraft.UnReserve();
|
||||
|
||||
var host = aircraft.GetActorBelow();
|
||||
|
||||
@@ -59,9 +59,8 @@ namespace OpenRA.Mods.Common.Activities
|
||||
if (!pool.GiveAmmo())
|
||||
continue;
|
||||
|
||||
var wsb = hostBuilding.Trait<WithSpriteBody>();
|
||||
if (wsb.DefaultAnimation.HasSequence("active"))
|
||||
wsb.PlayCustomAnimation(hostBuilding, "active", () => wsb.CancelCustomAnimation(hostBuilding));
|
||||
foreach (var host in hostBuilding.TraitsImplementing<INotifyRearm>())
|
||||
host.Rearming(hostBuilding, self);
|
||||
|
||||
var sound = pool.Info.RearmSound;
|
||||
if (sound != null)
|
||||
|
||||
@@ -19,15 +19,22 @@ namespace OpenRA.Mods.Common.Activities
|
||||
public class Repair : Activity
|
||||
{
|
||||
readonly RepairsUnitsInfo repairsUnits;
|
||||
readonly Actor host;
|
||||
readonly Target host;
|
||||
readonly WDist closeEnough;
|
||||
|
||||
int remainingTicks;
|
||||
Health health;
|
||||
bool played = false;
|
||||
|
||||
public Repair(Actor host)
|
||||
public Repair(Actor self, Actor host)
|
||||
: this(self, host, WDist.Zero) { }
|
||||
|
||||
public Repair(Actor self, Actor host, WDist closeEnough)
|
||||
{
|
||||
this.host = host;
|
||||
this.host = Target.FromActor(host);
|
||||
this.closeEnough = closeEnough;
|
||||
repairsUnits = host.Info.TraitInfo<RepairsUnitsInfo>();
|
||||
health = self.TraitOrDefault<Health>();
|
||||
}
|
||||
|
||||
public override Activity Tick(Actor self)
|
||||
@@ -40,18 +47,17 @@ namespace OpenRA.Mods.Common.Activities
|
||||
return this;
|
||||
}
|
||||
|
||||
if (host == null || !host.IsInWorld)
|
||||
if (host.Type == TargetType.Invalid || health == null)
|
||||
return NextActivity;
|
||||
|
||||
health = self.TraitOrDefault<Health>();
|
||||
if (health == null)
|
||||
if (closeEnough.LengthSquared > 0 && !host.IsInRange(self.CenterPosition, closeEnough))
|
||||
return NextActivity;
|
||||
|
||||
if (health.DamageState == DamageState.Undamaged)
|
||||
{
|
||||
if (host.Owner != self.Owner)
|
||||
if (host.Actor.Owner != self.Owner)
|
||||
{
|
||||
var exp = host.Owner.PlayerActor.TraitOrDefault<PlayerExperience>();
|
||||
var exp = host.Actor.Owner.PlayerActor.TraitOrDefault<PlayerExperience>();
|
||||
if (exp != null)
|
||||
exp.GiveExperience(repairsUnits.PlayerExperience);
|
||||
}
|
||||
@@ -80,8 +86,8 @@ namespace OpenRA.Mods.Common.Activities
|
||||
|
||||
self.InflictDamage(self, new Damage(-hpToRepair));
|
||||
|
||||
foreach (var depot in host.TraitsImplementing<INotifyRepair>())
|
||||
depot.Repairing(host, self);
|
||||
foreach (var depot in host.Actor.TraitsImplementing<INotifyRepair>())
|
||||
depot.Repairing(host.Actor, self);
|
||||
|
||||
remainingTicks = repairsUnits.Interval;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace OpenRA.Mods.Common.Effects
|
||||
|
||||
public IEnumerable<IRenderable> RenderAboveShroud(WorldRenderer wr)
|
||||
{
|
||||
if (wr.World.FogObscures(pos))
|
||||
if (wr.World.FogObscures(pos) || wr.World.ShroudObscures(pos))
|
||||
yield break;
|
||||
|
||||
// Arbitrary large value used for the z-offset to try and ensure the text displays above everything else.
|
||||
|
||||
@@ -77,7 +77,13 @@ namespace OpenRA.Mods.Common.LoadScreens
|
||||
// Load a replay directly
|
||||
if (!string.IsNullOrEmpty(Launch.Replay))
|
||||
{
|
||||
var replayMeta = ReplayMetadata.Read(Launch.Replay);
|
||||
ReplayMetadata replayMeta = null;
|
||||
try
|
||||
{
|
||||
replayMeta = ReplayMetadata.Read(Launch.Replay);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (ReplayUtils.PromptConfirmReplayCompatibility(replayMeta, Game.LoadShellMap))
|
||||
Game.JoinReplay(Launch.Replay);
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Activities\Air\AllowYieldingReservation.cs" />
|
||||
<Compile Include="Activities\Air\FallToEarth.cs" />
|
||||
<Compile Include="Activities\Air\Fly.cs" />
|
||||
<Compile Include="Activities\Air\FlyAttack.cs" />
|
||||
@@ -453,6 +454,7 @@
|
||||
<Compile Include="Traits\Render\WithParachute.cs" />
|
||||
<Compile Include="Traits\Render\WithRangeCircle.cs" />
|
||||
<Compile Include="Traits\Render\WithRankDecoration.cs" />
|
||||
<Compile Include="Traits\Render\WithRearmAnimation.cs" />
|
||||
<Compile Include="Traits\Render\WithRepairAnimation.cs" />
|
||||
<Compile Include="Traits\Render\WithRepairOverlay.cs" />
|
||||
<Compile Include="Traits\Render\WithResources.cs" />
|
||||
@@ -559,6 +561,7 @@
|
||||
<Compile Include="UtilityCommands\ExtractSettingsDocsCommand.cs" />
|
||||
<Compile Include="UtilityCommands\ExtractTraitDocsCommand.cs" />
|
||||
<Compile Include="Widgets\Logic\Ingame\MusicControllerLogic.cs" />
|
||||
<Compile Include="Widgets\UnitCommandWidget.cs" />
|
||||
<Compile Include="WorldExtensions.cs" />
|
||||
<Compile Include="UtilityCommands\GetMapHashCommand.cs" />
|
||||
<Compile Include="UtilityCommands\Glob.cs" />
|
||||
|
||||
@@ -19,7 +19,9 @@ namespace OpenRA.Mods.Common.Orders
|
||||
public IEnumerable<Order> Order(World world, CPos cell, int2 worldPixel, MouseInput mi)
|
||||
{
|
||||
world.CancelInputMode();
|
||||
yield return new Order("PlaceBeacon", world.LocalPlayer.PlayerActor, false) { TargetLocation = cell, SuppressVisualFeedback = true };
|
||||
|
||||
if (mi.Button == MouseButton.Left)
|
||||
yield return new Order("PlaceBeacon", world.LocalPlayer.PlayerActor, false) { TargetLocation = cell, SuppressVisualFeedback = true };
|
||||
}
|
||||
|
||||
public virtual void Tick(World world) { }
|
||||
|
||||
@@ -86,6 +86,8 @@ namespace OpenRA.Mods.Common.Projectiles
|
||||
{
|
||||
if (hitanim != null)
|
||||
hitanim.PlayThen(info.HitAnimSequence, () => animationComplete = true);
|
||||
else
|
||||
animationComplete = true;
|
||||
|
||||
args.Weapon.Impact(Target.FromPos(target), args.SourceActor, args.DamageModifiers);
|
||||
doneDamage = true;
|
||||
|
||||
@@ -396,7 +396,7 @@ namespace OpenRA.Mods.Common.Projectiles
|
||||
if ((tp.Actor.CenterPosition - pos).HorizontalLengthSquared > tp.Trait.Range.LengthSquared)
|
||||
return false;
|
||||
|
||||
if (!tp.Actor.Owner.Stances[args.SourceActor.Owner].HasStance(tp.Trait.DeflectionStances))
|
||||
if (!tp.Trait.DeflectionStances.HasStance(tp.Actor.Owner.Stances[args.SourceActor.Owner]))
|
||||
return false;
|
||||
|
||||
return tp.Actor.World.SharedRandom.Next(100 / tp.Trait.Chance) == 0;
|
||||
|
||||
@@ -42,9 +42,9 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
public void ReturnToBase(Actor airfield = null)
|
||||
{
|
||||
if (isPlane)
|
||||
Self.QueueActivity(new ReturnToBase(Self, airfield));
|
||||
Self.QueueActivity(new ReturnToBase(Self, false, airfield));
|
||||
else
|
||||
Self.QueueActivity(new HeliReturnToBase(Self));
|
||||
Self.QueueActivity(new HeliReturnToBase(Self, false));
|
||||
}
|
||||
|
||||
[ScriptActorPropertyActivity]
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
public void Attack(Actor targetActor, bool allowMove = true, bool forceAttack = false)
|
||||
{
|
||||
var target = Target.FromActor(targetActor);
|
||||
if (!target.IsValidFor(Self) || target.Type == TargetType.FrozenActor)
|
||||
if (!target.IsValidFor(Self))
|
||||
Log.Write("lua", "{1} is an invalid target for {0}!", Self, targetActor);
|
||||
|
||||
if (!targetActor.Info.HasTraitInfo<FrozenUnderFogInfo>() && !Self.Owner.CanTargetActor(targetActor))
|
||||
@@ -98,5 +98,11 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
|
||||
attackBase.AttackTarget(target, true, allowMove, forceAttack);
|
||||
}
|
||||
|
||||
[Desc("Checks if the targeted actor is a valid target for this actor.")]
|
||||
public bool CanTarget(Actor targetActor)
|
||||
{
|
||||
return Target.FromActor(targetActor).IsValidFor(Self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,6 +407,10 @@ namespace OpenRA.Mods.Common.Server
|
||||
server.SendMessage(server.TwoHumansRequiredText);
|
||||
else if (server.Map.Players.Players.Where(p => p.Value.Playable).All(p => !p.Value.AllowBots))
|
||||
server.SendMessage("Bots have been disabled on this map.");
|
||||
|
||||
var briefing = MissionBriefingOrDefault(server);
|
||||
if (briefing != null)
|
||||
server.SendMessage(briefing);
|
||||
};
|
||||
|
||||
Action queryFailed = () =>
|
||||
@@ -842,6 +846,15 @@ namespace OpenRA.Mods.Common.Server
|
||||
return validator.MakeValid(askColor.RGB, server.Random, terrainColors, playerColors, onError);
|
||||
}
|
||||
|
||||
static string MissionBriefingOrDefault(S server)
|
||||
{
|
||||
var missionData = server.Map.Rules.Actors["world"].TraitInfoOrDefault<MissionDataInfo>();
|
||||
if (missionData != null && !string.IsNullOrEmpty(missionData.Briefing))
|
||||
return missionData.Briefing.Replace("\\n", "\n");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ClientJoined(S server, Connection conn)
|
||||
{
|
||||
var client = server.GetClient(conn);
|
||||
@@ -849,6 +862,13 @@ namespace OpenRA.Mods.Common.Server
|
||||
// Validate whether color is allowed and get an alternative if it isn't
|
||||
if (client.Slot == null || !server.LobbyInfo.Slots[client.Slot].LockColor)
|
||||
client.Color = SanitizePlayerColor(server, client.Color, client.Index);
|
||||
|
||||
// Report any custom map details
|
||||
// HACK: this isn't the best place for this to live, but if we move it somewhere else
|
||||
// then we need a larger hack to hook the map change event.
|
||||
var briefing = MissionBriefingOrDefault(server);
|
||||
if (briefing != null)
|
||||
server.SendOrderTo(conn, "Message", briefing);
|
||||
}
|
||||
|
||||
public PlayerReference PlayerReferenceForSlot(S server, Session.Slot slot)
|
||||
|
||||
@@ -35,8 +35,10 @@ namespace OpenRA.Mods.Common.Server
|
||||
foreach (var kv in server.LobbyInfo.GlobalSettings.LobbyOptions)
|
||||
{
|
||||
Session.LobbyOptionState def;
|
||||
LobbyOption option;
|
||||
if (!defaults.LobbyOptions.TryGetValue(kv.Key, out def) || kv.Value.Value != def.Value)
|
||||
server.SendOrderTo(conn, "Message", options[kv.Key].Name + ": " + kv.Value.Value);
|
||||
if (options.TryGetValue(kv.Key, out option))
|
||||
server.SendOrderTo(conn, "Message", option.Name + ": " + kv.Value.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace OpenRA.Mods.Common.Server
|
||||
public void GameStarted(S server) { PingMasterServer(server); }
|
||||
public void GameEnded(S server) { PingMasterServer(server); }
|
||||
|
||||
int lastPing = 0;
|
||||
long lastPing = 0;
|
||||
bool isInitialPing = true;
|
||||
|
||||
volatile bool isBusy;
|
||||
|
||||
@@ -24,8 +24,8 @@ namespace OpenRA.Mods.Common.Server
|
||||
// TickTimeout is in microseconds
|
||||
public int TickTimeout { get { return PingInterval * 100; } }
|
||||
|
||||
int lastPing = 0;
|
||||
int lastConnReport = 0;
|
||||
long lastPing = 0;
|
||||
long lastConnReport = 0;
|
||||
bool isInitialPing = true;
|
||||
|
||||
public void Tick(S server)
|
||||
|
||||
@@ -69,6 +69,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Does this actor need to turn before landing?")]
|
||||
public readonly bool TurnToLand = false;
|
||||
|
||||
[Desc("Does this actor cancel its previous activity after resupplying?")]
|
||||
public readonly bool AbortOnResupply = true;
|
||||
|
||||
public readonly WDist LandAltitude = WDist.Zero;
|
||||
|
||||
[Desc("How fast this actor ascends or descends when using horizontal take off/landing.")]
|
||||
@@ -111,7 +114,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public CPos TopLeft { get { return self.World.Map.CellContaining(CenterPosition); } }
|
||||
public int TurnSpeed { get { return Info.TurnSpeed; } }
|
||||
public Actor ReservedActor { get; private set; }
|
||||
public bool MayYieldReservation;
|
||||
public bool MayYieldReservation { get; private set; }
|
||||
|
||||
bool airborne;
|
||||
bool cruising;
|
||||
@@ -282,6 +285,14 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
}
|
||||
|
||||
public void AllowYieldingReservation()
|
||||
{
|
||||
if (reservation == null)
|
||||
return;
|
||||
|
||||
MayYieldReservation = true;
|
||||
}
|
||||
|
||||
public void UnReserve()
|
||||
{
|
||||
if (reservation == null)
|
||||
@@ -341,7 +352,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (Info.RearmBuildings.Contains(name))
|
||||
yield return new Rearm(self);
|
||||
if (Info.RepairBuildings.Contains(name))
|
||||
yield return new Repair(a);
|
||||
yield return new Repair(self, a);
|
||||
}
|
||||
|
||||
public void ModifyDeathActorInit(Actor self, TypeDictionary init)
|
||||
@@ -551,9 +562,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (Reservable.IsReserved(order.TargetActor))
|
||||
{
|
||||
if (IsPlane)
|
||||
self.QueueActivity(new ReturnToBase(self));
|
||||
self.QueueActivity(new ReturnToBase(self, Info.AbortOnResupply));
|
||||
else
|
||||
self.QueueActivity(new HeliReturnToBase(self));
|
||||
self.QueueActivity(new HeliReturnToBase(self, Info.AbortOnResupply));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -562,7 +573,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (IsPlane)
|
||||
{
|
||||
self.QueueActivity(order.Queued, ActivityUtils.SequenceActivities(
|
||||
new ReturnToBase(self, order.TargetActor),
|
||||
new ReturnToBase(self, Info.AbortOnResupply, order.TargetActor),
|
||||
new ResupplyAircraft(self)));
|
||||
}
|
||||
else
|
||||
@@ -610,11 +621,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
UnReserve();
|
||||
self.CancelActivity();
|
||||
if (IsPlane)
|
||||
self.QueueActivity(new ReturnToBase(self));
|
||||
self.QueueActivity(new ReturnToBase(self, Info.AbortOnResupply, null, false));
|
||||
else
|
||||
self.QueueActivity(new HeliReturnToBase(self));
|
||||
|
||||
self.QueueActivity(new ResupplyAircraft(self));
|
||||
self.QueueActivity(new HeliReturnToBase(self, Info.AbortOnResupply, false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,8 +47,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
var cp = self.CenterPosition;
|
||||
var bombTarget = Target.FromPos(cp - new WVec(0, 0, cp.Z));
|
||||
var bombHeight = self.World.Map.DistanceAboveTerrain(self.CenterPosition);
|
||||
var bombTarget = Target.FromPos(self.CenterPosition - new WVec(WDist.Zero, WDist.Zero, bombHeight));
|
||||
var wasInAttackRange = inAttackRange;
|
||||
var wasFacingTarget = facingTarget;
|
||||
|
||||
@@ -77,9 +77,11 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (!target.IsInRange(self.CenterPosition, a.MaxRange()))
|
||||
continue;
|
||||
|
||||
var t = Target.FromPos(cp - new WVec(0, a.MaxRange().Length / 2, cp.Z).Rotate(WRot.FromFacing(f)));
|
||||
inAttackRange = true;
|
||||
a.CheckFire(self, facing.Value, t);
|
||||
|
||||
var gunPos = self.CenterPosition - new WVec(0, a.MaxRange().Length / 2, 0).Rotate(WRot.FromFacing(f));
|
||||
var gunHeight = self.World.Map.DistanceAboveTerrain(gunPos);
|
||||
a.CheckFire(self, facing.Value, Target.FromPos(gunPos - new WVec(WDist.Zero, WDist.Zero, gunHeight)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var airfield = ReturnToBase.ChooseAirfield(self, true);
|
||||
if (airfield != null)
|
||||
{
|
||||
self.QueueActivity(new ReturnToBase(self, airfield));
|
||||
self.QueueActivity(new ReturnToBase(self, aircraftInfo.AbortOnResupply, airfield));
|
||||
self.QueueActivity(new ResupplyAircraft(self));
|
||||
}
|
||||
else
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
|
||||
Color GetColor()
|
||||
{
|
||||
return self.EffectiveOwner != null ? self.EffectiveOwner.Owner.Color.RGB : self.Owner.Color.RGB;
|
||||
return self.EffectiveOwner != null && self.EffectiveOwner.Disguised ? self.EffectiveOwner.Owner.Color.RGB : self.Owner.Color.RGB;
|
||||
}
|
||||
|
||||
IEnumerable<IRenderable> IRenderAboveShroudWhenSelected.RenderAboveShroud(Actor self, WorldRenderer wr)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
@@ -27,16 +28,16 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
public readonly WDist FallbackRange = WDist.Zero;
|
||||
|
||||
// Computed range
|
||||
WDist range;
|
||||
Lazy<WDist> range;
|
||||
|
||||
public IEnumerable<IRenderable> Render(WorldRenderer wr, World w, ActorInfo ai, WPos centerPosition)
|
||||
{
|
||||
if (range == WDist.Zero)
|
||||
if (range == null || range.Value == WDist.Zero)
|
||||
return SpriteRenderable.None;
|
||||
|
||||
var localRange = new RangeCircleRenderable(
|
||||
centerPosition,
|
||||
range,
|
||||
range.Value,
|
||||
0,
|
||||
Color.FromArgb(128, Color.Yellow),
|
||||
Color.FromArgb(96, Color.Black));
|
||||
@@ -52,12 +53,16 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
|
||||
public void RulesetLoaded(Ruleset rules, ActorInfo ai)
|
||||
{
|
||||
var armaments = ai.TraitInfos<ArmamentInfo>().Where(a => a.UpgradeMinEnabledLevel == 0);
|
||||
// ArmamentInfo.ModifiedRange is set by RulesetLoaded, and may not have been initialized yet.
|
||||
// Defer this lookup until we really need it to ensure we get the correct value.
|
||||
range = Exts.Lazy(() =>
|
||||
{
|
||||
var armaments = ai.TraitInfos<ArmamentInfo>().Where(a => a.UpgradeMinEnabledLevel == 0);
|
||||
if (!armaments.Any())
|
||||
return FallbackRange;
|
||||
|
||||
if (armaments.Any())
|
||||
range = armaments.Select(a => a.ModifiedRange).Max();
|
||||
else
|
||||
range = FallbackRange;
|
||||
return armaments.Max(a => a.ModifiedRange);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
public override object Create(ActorInitializer init) { return new WithDecoration(init.Self, this); }
|
||||
}
|
||||
|
||||
public class WithDecoration : UpgradableTrait<WithDecorationInfo>, ITick, IRender, IRenderAboveShroudWhenSelected
|
||||
public class WithDecoration : UpgradableTrait<WithDecorationInfo>, ITick, IRenderAboveShroud, IRenderAboveShroudWhenSelected
|
||||
{
|
||||
protected readonly Animation Anim;
|
||||
|
||||
@@ -69,9 +69,19 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
Anim.PlayRepeating(info.Sequence);
|
||||
}
|
||||
|
||||
public virtual bool ShouldRender(Actor self) { return true; }
|
||||
protected virtual bool ShouldRender(Actor self)
|
||||
{
|
||||
if (self.World.RenderPlayer != null)
|
||||
{
|
||||
var stance = self.Owner.Stances[self.World.RenderPlayer];
|
||||
if (!Info.ValidStances.HasStance(stance))
|
||||
return false;
|
||||
}
|
||||
|
||||
IEnumerable<IRenderable> IRender.Render(Actor self, WorldRenderer wr)
|
||||
return true;
|
||||
}
|
||||
|
||||
IEnumerable<IRenderable> IRenderAboveShroud.RenderAboveShroud(Actor self, WorldRenderer wr)
|
||||
{
|
||||
return !Info.RequiresSelection ? RenderInner(self, wr) : SpriteRenderable.None;
|
||||
}
|
||||
@@ -86,13 +96,6 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
if (IsTraitDisabled || self.IsDead || !self.IsInWorld || Anim == null)
|
||||
return Enumerable.Empty<IRenderable>();
|
||||
|
||||
if (self.World.RenderPlayer != null)
|
||||
{
|
||||
var stance = self.Owner.Stances[self.World.RenderPlayer];
|
||||
if (!Info.ValidStances.HasStance(stance))
|
||||
return Enumerable.Empty<IRenderable>();
|
||||
}
|
||||
|
||||
if (!ShouldRender(self) || self.World.FogObscures(self))
|
||||
return Enumerable.Empty<IRenderable>();
|
||||
|
||||
|
||||
57
OpenRA.Mods.Common/Traits/Render/WithRearmAnimation.cs
Normal file
57
OpenRA.Mods.Common/Traits/Render/WithRearmAnimation.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits.Render
|
||||
{
|
||||
[Desc("Replaces the building animation when it rearms a unit.")]
|
||||
public class WithRearmAnimationInfo : ITraitInfo, Requires<WithSpriteBodyInfo>
|
||||
{
|
||||
[Desc("Sequence name to use")]
|
||||
[SequenceReference] public readonly string Sequence = "active";
|
||||
|
||||
public readonly bool PauseOnLowPower = false;
|
||||
|
||||
public object Create(ActorInitializer init) { return new WithRearmAnimation(init.Self, this); }
|
||||
}
|
||||
|
||||
public class WithRearmAnimation : INotifyRearm, INotifyBuildComplete, INotifySold
|
||||
{
|
||||
readonly WithRearmAnimationInfo info;
|
||||
readonly WithSpriteBody spriteBody;
|
||||
bool buildComplete;
|
||||
|
||||
public WithRearmAnimation(Actor self, WithRearmAnimationInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
spriteBody = self.TraitOrDefault<WithSpriteBody>();
|
||||
}
|
||||
|
||||
void INotifyRearm.Rearming(Actor self, Actor target)
|
||||
{
|
||||
if (buildComplete && spriteBody != null && !(info.PauseOnLowPower && self.IsDisabled()))
|
||||
spriteBody.PlayCustomAnimation(self, info.Sequence, () => spriteBody.CancelCustomAnimation(self));
|
||||
}
|
||||
|
||||
public void BuildingComplete(Actor self)
|
||||
{
|
||||
buildComplete = true;
|
||||
}
|
||||
|
||||
public void Selling(Actor self)
|
||||
{
|
||||
buildComplete = false;
|
||||
}
|
||||
|
||||
public void Sold(Actor self) { }
|
||||
}
|
||||
}
|
||||
@@ -69,33 +69,20 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
pipImages.PlayFetchIndex(Info.GroupSequence, () => (int)group);
|
||||
|
||||
var bounds = self.VisualBounds;
|
||||
var halfSize = (0.5f * pipImages.Image.Size.XY).ToInt2();
|
||||
|
||||
var boundsOffset = new int2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom) / 2;
|
||||
var sizeOffset = -halfSize;
|
||||
var boundsOffset = 0.5f * new float2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom);
|
||||
if (Info.ReferencePoint.HasFlag(ReferencePoints.Top))
|
||||
{
|
||||
boundsOffset -= new int2(0, bounds.Height / 2);
|
||||
sizeOffset += new int2(0, halfSize.Y);
|
||||
}
|
||||
else if (Info.ReferencePoint.HasFlag(ReferencePoints.Bottom))
|
||||
{
|
||||
boundsOffset += new int2(0, bounds.Height / 2);
|
||||
sizeOffset -= new int2(0, halfSize.Y);
|
||||
}
|
||||
boundsOffset -= new float2(0, 0.5f * bounds.Height);
|
||||
|
||||
if (Info.ReferencePoint.HasFlag(ReferencePoints.Bottom))
|
||||
boundsOffset += new float2(0, 0.5f * bounds.Height);
|
||||
|
||||
if (Info.ReferencePoint.HasFlag(ReferencePoints.Left))
|
||||
{
|
||||
boundsOffset -= new int2(bounds.Width / 2, 0);
|
||||
sizeOffset += new int2(halfSize.X, 0);
|
||||
}
|
||||
else if (Info.ReferencePoint.HasFlag(ReferencePoints.Right))
|
||||
{
|
||||
boundsOffset += new int2(bounds.Width / 2, 0);
|
||||
sizeOffset -= new int2(halfSize.X, 0);
|
||||
}
|
||||
boundsOffset -= new float2(0.5f * bounds.Width, 0);
|
||||
|
||||
var pxPos = wr.Viewport.WorldToViewPx(wr.ScreenPxPosition(self.CenterPosition) + boundsOffset) + sizeOffset;
|
||||
if (Info.ReferencePoint.HasFlag(ReferencePoints.Right))
|
||||
boundsOffset += new float2(0.5f * bounds.Width, 0);
|
||||
|
||||
var pxPos = wr.Viewport.WorldToViewPx(wr.ScreenPxPosition(self.CenterPosition) + boundsOffset.ToInt2()) - (0.5f * pipImages.Image.Size.XY).ToInt2();
|
||||
yield return new UISpriteRenderable(pipImages.Image, self.CenterPosition, pxPos, 0, palette, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (CanRearmAt(order.TargetActor) && CanRearm())
|
||||
self.QueueActivity(new Rearm(self));
|
||||
|
||||
self.QueueActivity(new Repair(order.TargetActor));
|
||||
self.QueueActivity(new Repair(self, order.TargetActor));
|
||||
|
||||
var rp = order.TargetActor.TraitOrDefault<RallyPoint>();
|
||||
if (rp != null)
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
self.CancelActivity();
|
||||
self.QueueActivity(movement.MoveWithinRange(target, info.CloseEnough));
|
||||
self.QueueActivity(new Repair(order.TargetActor));
|
||||
self.QueueActivity(new Repair(self, order.TargetActor, info.CloseEnough));
|
||||
|
||||
self.SetTargetLine(target, Color.Green, false);
|
||||
}
|
||||
|
||||
@@ -127,11 +127,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
var isAllied = self.Owner.IsAlliedWith(self.World.RenderPlayer);
|
||||
Game.Sound.Play(isAllied ? Info.LaunchSound : Info.IncomingSound);
|
||||
|
||||
var speech = isAllied ? Info.LaunchSpeechNotification : Info.IncomingSpeechNotification;
|
||||
Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", speech, self.Owner.Faction.InternalName);
|
||||
PlayLaunchSounds();
|
||||
|
||||
Actor distanceTestActor = null;
|
||||
for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++)
|
||||
|
||||
@@ -92,19 +92,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public override void Activate(Actor self, Order order, SupportPowerManager manager)
|
||||
{
|
||||
base.Activate(self, order, manager);
|
||||
|
||||
if (self.Owner.IsAlliedWith(self.World.RenderPlayer))
|
||||
{
|
||||
Game.Sound.Play(Info.LaunchSound);
|
||||
Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech",
|
||||
Info.LaunchSpeechNotification, self.Owner.Faction.InternalName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Game.Sound.Play(Info.IncomingSound);
|
||||
Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech",
|
||||
Info.IncomingSpeechNotification, self.Owner.Faction.InternalName);
|
||||
}
|
||||
PlayLaunchSounds();
|
||||
|
||||
if (!string.IsNullOrEmpty(info.ActivationSequence))
|
||||
{
|
||||
|
||||
@@ -120,5 +120,17 @@ namespace OpenRA.Mods.Common.Traits
|
||||
Info.RadarPingDuration);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void PlayLaunchSounds()
|
||||
{
|
||||
var renderPlayer = Self.World.RenderPlayer;
|
||||
var isAllied = Self.Owner.IsAlliedWith(renderPlayer);
|
||||
Game.Sound.Play(isAllied ? Info.LaunchSound : Info.IncomingSound);
|
||||
|
||||
// IsAlliedWith returns true if renderPlayer is null, so we are safe here.
|
||||
var toPlayer = isAllied ? renderPlayer ?? Self.Owner : renderPlayer;
|
||||
var speech = isAllied ? Info.LaunchSpeechNotification : Info.IncomingSpeechNotification;
|
||||
Game.Sound.PlayNotification(Self.World.Map.Rules, toPlayer, "Speech", speech, toPlayer.Faction.InternalName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if ((!dirty.ContainsKey(loc) || dirty[loc].Sprite == null) && !tiles.ContainsKey(loc))
|
||||
{
|
||||
// No smudge; create a new one
|
||||
var st = smudges.Keys.Random(world.SharedRandom);
|
||||
var st = smudges.Keys.Random(Game.CosmeticRandom);
|
||||
dirty[loc] = new Smudge { Type = st, Depth = 0, Sprite = smudges[st][0] };
|
||||
}
|
||||
else
|
||||
|
||||
@@ -139,4 +139,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
void ModifyActorPreviewInit(Actor self, TypeDictionary inits);
|
||||
}
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface INotifyRearm { void Rearming(Actor host, Actor other); }
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
.Select(m => new Map(modData, m.Package)));
|
||||
}
|
||||
else
|
||||
maps.Add(new Map(modData, modData.ModFiles.OpenPackage(args[1])));
|
||||
maps.Add(new Map(modData, modData.ModFiles.OpenPackage(args[1], new FileSystem.Folder("."))));
|
||||
|
||||
foreach (var testMap in maps)
|
||||
{
|
||||
|
||||
@@ -358,6 +358,15 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
}
|
||||
}
|
||||
|
||||
// Add a warning to add WithRearmAnimation to actors that might need it.
|
||||
// Update rule added during prep-1609 stable period, date needs fixing after release.
|
||||
if (engineVersion < 20160918 && depth == 2)
|
||||
{
|
||||
if (node.Key == "RearmBuildings")
|
||||
foreach (var host in node.Value.Value.Split(','))
|
||||
Console.WriteLine("Actor type `{0}` is denoted as a RearmBuilding. Consider adding the `WithRearmAnimation` trait to it.".F(host));
|
||||
}
|
||||
|
||||
UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +131,21 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
|
||||
CloseChat();
|
||||
chatText.IsDisabled = () => world.IsReplay;
|
||||
|
||||
var keyListener = chatChrome.Get<LogicKeyListenerWidget>("KEY_LISTENER");
|
||||
keyListener.OnKeyPress = e =>
|
||||
{
|
||||
if (e.Event == KeyInputEvent.Up || !chatText.IsDisabled())
|
||||
return false;
|
||||
|
||||
if ((e.Key == Keycode.RETURN || e.Key == Keycode.KP_ENTER || e.Key == Keycode.ESCAPE) && e.Modifiers == Modifiers.None)
|
||||
{
|
||||
CloseChat();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
bool SwitchTeamChat()
|
||||
@@ -151,7 +166,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
chatText.Text = "";
|
||||
chatChrome.Visible = true;
|
||||
chatScrollPanel.ScrollToBottom();
|
||||
chatText.TakeKeyboardFocus();
|
||||
if (!chatText.IsDisabled())
|
||||
chatText.TakeKeyboardFocus();
|
||||
chatOverlay.Visible = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -171,6 +171,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
(p.PlayerActor.TraitOrDefault<PlayerStatistics>() ?? new PlayerStatistics(p.PlayerActor)).EarnedSamples.Select(s => (float)s)));
|
||||
|
||||
playerStatsPanel.AddChild(template);
|
||||
playerStatsPanel.ScrollToTop();
|
||||
}
|
||||
|
||||
void DisplayStats(Func<Player, ScrollItemWidget> createItem)
|
||||
|
||||
@@ -96,6 +96,18 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
DetectContentDisks();
|
||||
}
|
||||
|
||||
static bool IsValidDrive(DriveInfo d)
|
||||
{
|
||||
if (d.DriveType == DriveType.CDRom && d.IsReady)
|
||||
return true;
|
||||
|
||||
// HACK: the "TFD" DVD is detected as a fixed udf-formatted drive on OSX
|
||||
if (d.DriveType == DriveType.Fixed && d.DriveFormat == "udf")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DetectContentDisks()
|
||||
{
|
||||
var message = "Detecting drives";
|
||||
@@ -105,7 +117,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
new Task(() =>
|
||||
{
|
||||
var volumes = DriveInfo.GetDrives()
|
||||
.Where(v => v.DriveType == DriveType.CDRom && v.IsReady)
|
||||
.Where(IsValidDrive)
|
||||
.Select(v => v.RootDirectory.FullName);
|
||||
|
||||
foreach (var kv in sources)
|
||||
|
||||
@@ -24,7 +24,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
onCancel = DoNothing;
|
||||
|
||||
if (replayMeta == null)
|
||||
return IncompatibleReplayDialog("outdated engine", null, onCancel);
|
||||
{
|
||||
ConfirmationDialogs.ButtonPrompt("Incompatible Replay", "Replay metadata could not be read.", onCancel: onCancel);
|
||||
return false;
|
||||
}
|
||||
|
||||
var version = replayMeta.GameInfo.Version;
|
||||
if (version == null)
|
||||
|
||||
@@ -165,7 +165,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
var windowModeDropdown = panel.Get<DropDownButtonWidget>("MODE_DROPDOWN");
|
||||
windowModeDropdown.OnMouseDown = _ => ShowWindowModeDropdown(windowModeDropdown, ds);
|
||||
windowModeDropdown.GetText = () => ds.Mode == WindowMode.Windowed ?
|
||||
"Windowed" : ds.Mode == WindowMode.Fullscreen ? "Fullscreen" : "Pseudo-Fullscreen";
|
||||
"Windowed" : ds.Mode == WindowMode.Fullscreen ? "Fullscreen (Legacy)" : "Fullscreen";
|
||||
|
||||
var statusBarsDropDown = panel.Get<DropDownButtonWidget>("STATUS_BAR_DROPDOWN");
|
||||
statusBarsDropDown.OnMouseDown = _ => ShowStatusBarsDropdown(statusBarsDropDown, gs);
|
||||
@@ -748,8 +748,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
var options = new Dictionary<string, WindowMode>()
|
||||
{
|
||||
{ "Pseudo-Fullscreen", WindowMode.PseudoFullscreen },
|
||||
{ "Fullscreen", WindowMode.Fullscreen },
|
||||
{ "Fullscreen", WindowMode.PseudoFullscreen },
|
||||
{ "Fullscreen (Legacy)", WindowMode.Fullscreen },
|
||||
{ "Windowed", WindowMode.Windowed },
|
||||
};
|
||||
|
||||
|
||||
178
OpenRA.Mods.Common/Widgets/UnitCommandWidget.cs
Normal file
178
OpenRA.Mods.Common/Widgets/UnitCommandWidget.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Orders;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Orders;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
/// <summary> Contains all functions that are unit-specific. </summary>
|
||||
public class UnitCommandWidget : Widget
|
||||
{
|
||||
readonly World world;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public UnitCommandWidget(World world, WorldRenderer worldRenderer)
|
||||
{
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public override string GetCursor(int2 pos) { return null; }
|
||||
public override Rectangle GetEventBounds() { return Rectangle.Empty; }
|
||||
|
||||
public override bool HandleKeyPress(KeyInput e)
|
||||
{
|
||||
if (world == null)
|
||||
return false;
|
||||
|
||||
// Put all functions that are valid for observers/spectators inside WorldCommandWidget.
|
||||
if (world.LocalPlayer == null || world.LocalPlayer.Spectating)
|
||||
return false;
|
||||
|
||||
if (world.IsGameOver || !world.Selection.Actors.Any())
|
||||
return false;
|
||||
|
||||
return ProcessInput(e);
|
||||
}
|
||||
|
||||
bool ProcessInput(KeyInput e)
|
||||
{
|
||||
if (e.Event == KeyInputEvent.Down)
|
||||
{
|
||||
var key = Hotkey.FromKeyInput(e);
|
||||
var ks = Game.Settings.Keys;
|
||||
|
||||
if (key == ks.AttackMoveKey)
|
||||
return PerformAttackMove();
|
||||
|
||||
if (key == ks.StopKey)
|
||||
return PerformStop();
|
||||
|
||||
if (key == ks.ScatterKey)
|
||||
return PerformScatter();
|
||||
|
||||
if (key == ks.DeployKey)
|
||||
return PerformDeploy();
|
||||
|
||||
if (key == ks.StanceCycleKey)
|
||||
return PerformStanceCycle();
|
||||
|
||||
if (key == ks.GuardKey)
|
||||
return PerformGuard();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: take ALL this garbage and route it through the OrderTargeter stuff.
|
||||
bool PerformAttackMove()
|
||||
{
|
||||
var actors = world.Selection.Actors
|
||||
.Where(a => a.Owner == world.LocalPlayer)
|
||||
.ToArray();
|
||||
|
||||
if (actors.Any(a => a.Info.HasTraitInfo<AttackMoveInfo>() && a.Info.HasTraitInfo<AutoTargetInfo>()))
|
||||
world.OrderGenerator = new GenericSelectTarget(actors,
|
||||
"AttackMove", "attackmove", Game.Settings.Game.MouseButtonPreference.Action);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PerformKeyboardOrderOnSelection(Func<Actor, Order> f)
|
||||
{
|
||||
var orders = world.Selection.Actors
|
||||
.Where(a => a.Owner == world.LocalPlayer && !a.Disposed)
|
||||
.Select(f)
|
||||
.ToArray();
|
||||
|
||||
foreach (var o in orders)
|
||||
world.IssueOrder(o);
|
||||
|
||||
world.PlayVoiceForOrders(orders);
|
||||
}
|
||||
|
||||
bool PerformStop()
|
||||
{
|
||||
PerformKeyboardOrderOnSelection(a => new Order("Stop", a, false));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformScatter()
|
||||
{
|
||||
PerformKeyboardOrderOnSelection(a => new Order("Scatter", a, false));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformDeploy()
|
||||
{
|
||||
// HACK: multiple orders here
|
||||
PerformKeyboardOrderOnSelection(a => new Order("ReturnToBase", a, false));
|
||||
PerformKeyboardOrderOnSelection(a => new Order("DeployTransform", a, false));
|
||||
PerformKeyboardOrderOnSelection(a => new Order("Unload", a, false));
|
||||
PerformKeyboardOrderOnSelection(a => new Order("Detonate", a, false));
|
||||
PerformKeyboardOrderOnSelection(a => new Order("DeployToUpgrade", a, false));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformStanceCycle()
|
||||
{
|
||||
var actor = world.Selection.Actors
|
||||
.Where(a => a.Owner == world.LocalPlayer && !a.Disposed)
|
||||
.Select(a => Pair.New(a, a.TraitOrDefault<AutoTarget>()))
|
||||
.FirstOrDefault(a => a.Second != null);
|
||||
|
||||
if (actor.First == null)
|
||||
return true;
|
||||
|
||||
var ati = actor.First.Info.TraitInfoOrDefault<AutoTargetInfo>();
|
||||
if (ati == null || !ati.EnableStances)
|
||||
return false;
|
||||
|
||||
var stances = Enum<UnitStance>.GetValues();
|
||||
var nextStance = stances.Concat(stances)
|
||||
.SkipWhile(s => s != actor.Second.PredictedStance)
|
||||
.Skip(1)
|
||||
.First();
|
||||
|
||||
PerformKeyboardOrderOnSelection(a =>
|
||||
{
|
||||
var at = a.TraitOrDefault<AutoTarget>();
|
||||
if (at != null)
|
||||
at.PredictedStance = nextStance;
|
||||
|
||||
return new Order("SetUnitStance", a, false) { ExtraData = (uint)nextStance };
|
||||
});
|
||||
|
||||
Game.Debug("Unit stance set to: {0}".F(nextStance));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformGuard()
|
||||
{
|
||||
var actors = world.Selection.Actors
|
||||
.Where(a => !a.Disposed && a.Owner == world.LocalPlayer);
|
||||
|
||||
if (actors.Any(a => a.Info.HasTraitInfo<GuardInfo>() && a.Info.HasTraitInfo<AutoTargetInfo>()))
|
||||
world.OrderGenerator = new GuardOrderGenerator(actors,
|
||||
"Guard", "guard", Game.Settings.Game.MouseButtonPreference.Action);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,7 +124,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
tooltipContainer.Value.RemoveTooltip();
|
||||
}
|
||||
|
||||
int lastScrollTime = 0;
|
||||
long lastScrollTime = 0;
|
||||
public override void Draw()
|
||||
{
|
||||
if (IsJoystickScrolling)
|
||||
|
||||
@@ -13,15 +13,13 @@ using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Orders;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Orders;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
/// <summary> Contains all functions that are valid for players and observers/spectators. </summary>
|
||||
public class WorldCommandWidget : Widget
|
||||
{
|
||||
readonly World world;
|
||||
@@ -65,133 +63,11 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
if (key == ks.ToSelectionKey)
|
||||
return ToSelection();
|
||||
|
||||
// Put all functions that are valid for observers/spectators above this line.
|
||||
if (world.LocalPlayer == null || world.LocalPlayer.Spectating)
|
||||
return false;
|
||||
|
||||
// Put all functions that aren't unit-specific before this line!
|
||||
if (!world.Selection.Actors.Any() || world.IsGameOver)
|
||||
return false;
|
||||
|
||||
if (key == ks.AttackMoveKey)
|
||||
return PerformAttackMove();
|
||||
|
||||
if (key == ks.StopKey)
|
||||
return PerformStop();
|
||||
|
||||
if (key == ks.ScatterKey)
|
||||
return PerformScatter();
|
||||
|
||||
if (key == ks.DeployKey)
|
||||
return PerformDeploy();
|
||||
|
||||
if (key == ks.StanceCycleKey)
|
||||
return PerformStanceCycle();
|
||||
|
||||
if (key == ks.GuardKey)
|
||||
return PerformGuard();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: take ALL this garbage and route it through the OrderTargeter stuff.
|
||||
bool PerformAttackMove()
|
||||
{
|
||||
var actors = world.Selection.Actors
|
||||
.Where(a => a.Owner == world.LocalPlayer)
|
||||
.ToArray();
|
||||
|
||||
if (actors.Any(a => a.Info.HasTraitInfo<AttackMoveInfo>() && a.Info.HasTraitInfo<AutoTargetInfo>()))
|
||||
world.OrderGenerator = new GenericSelectTarget(actors,
|
||||
"AttackMove", "attackmove", Game.Settings.Game.MouseButtonPreference.Action);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PerformKeyboardOrderOnSelection(Func<Actor, Order> f)
|
||||
{
|
||||
var orders = world.Selection.Actors
|
||||
.Where(a => a.Owner == world.LocalPlayer && !a.Disposed)
|
||||
.Select(f)
|
||||
.ToArray();
|
||||
|
||||
foreach (var o in orders)
|
||||
world.IssueOrder(o);
|
||||
|
||||
world.PlayVoiceForOrders(orders);
|
||||
}
|
||||
|
||||
bool PerformStop()
|
||||
{
|
||||
PerformKeyboardOrderOnSelection(a => new Order("Stop", a, false));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformScatter()
|
||||
{
|
||||
PerformKeyboardOrderOnSelection(a => new Order("Scatter", a, false));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformDeploy()
|
||||
{
|
||||
// HACK: multiple orders here
|
||||
PerformKeyboardOrderOnSelection(a => new Order("ReturnToBase", a, false));
|
||||
PerformKeyboardOrderOnSelection(a => new Order("DeployTransform", a, false));
|
||||
PerformKeyboardOrderOnSelection(a => new Order("Unload", a, false));
|
||||
PerformKeyboardOrderOnSelection(a => new Order("Detonate", a, false));
|
||||
PerformKeyboardOrderOnSelection(a => new Order("DeployToUpgrade", a, false));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformStanceCycle()
|
||||
{
|
||||
var actor = world.Selection.Actors
|
||||
.Where(a => a.Owner == world.LocalPlayer && !a.Disposed)
|
||||
.Select(a => Pair.New(a, a.TraitOrDefault<AutoTarget>()))
|
||||
.FirstOrDefault(a => a.Second != null);
|
||||
|
||||
if (actor.First == null)
|
||||
return true;
|
||||
|
||||
var ati = actor.First.Info.TraitInfoOrDefault<AutoTargetInfo>();
|
||||
if (ati == null || !ati.EnableStances)
|
||||
return false;
|
||||
|
||||
var stances = Enum<UnitStance>.GetValues();
|
||||
var nextStance = stances.Concat(stances)
|
||||
.SkipWhile(s => s != actor.Second.PredictedStance)
|
||||
.Skip(1)
|
||||
.First();
|
||||
|
||||
PerformKeyboardOrderOnSelection(a =>
|
||||
{
|
||||
var at = a.TraitOrDefault<AutoTarget>();
|
||||
if (at != null)
|
||||
at.PredictedStance = nextStance;
|
||||
|
||||
return new Order("SetUnitStance", a, false) { ExtraData = (uint)nextStance };
|
||||
});
|
||||
|
||||
Game.Debug("Unit stance set to: {0}".F(nextStance));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformGuard()
|
||||
{
|
||||
var actors = world.Selection.Actors
|
||||
.Where(a => !a.Disposed && a.Owner == world.LocalPlayer);
|
||||
|
||||
if (actors.Any(a => a.Info.HasTraitInfo<GuardInfo>() && a.Info.HasTraitInfo<AutoTargetInfo>()))
|
||||
world.OrderGenerator = new GuardOrderGenerator(actors,
|
||||
"Guard", "guard", Game.Settings.Game.MouseButtonPreference.Action);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CycleBases()
|
||||
{
|
||||
var player = world.RenderPlayer ?? world.LocalPlayer;
|
||||
|
||||
@@ -31,9 +31,9 @@ namespace OpenRA.Mods.D2k.Traits.Render
|
||||
carryable = self.Trait<Carryable>();
|
||||
}
|
||||
|
||||
public override bool ShouldRender(Actor self)
|
||||
protected override bool ShouldRender(Actor self)
|
||||
{
|
||||
return carryable.Reserved;
|
||||
return carryable.Reserved && base.ShouldRender(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace OpenRA.Mods.RA.Activities
|
||||
new MoveAdjacentTo(self, Target.FromActor(rearmTarget)),
|
||||
movement.MoveTo(self.World.Map.CellContaining(rearmTarget.CenterPosition), rearmTarget),
|
||||
new Rearm(self),
|
||||
new Repair(rearmTarget),
|
||||
new Repair(self, rearmTarget),
|
||||
this);
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
<Compile Include="Scripting\Properties\ParatroopersProperties.cs" />
|
||||
<Compile Include="Traits\Render\WithDisguisingInfantryBody.cs" />
|
||||
<Compile Include="ImportRedAlertLegacyMapCommand.cs" />
|
||||
<Compile Include="Traits\Infiltration\InfiltrateForDecoration.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj">
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Traits.Render;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA.Traits
|
||||
{
|
||||
[Desc("Reveals a decoration sprite to the indicated players when infiltrated.")]
|
||||
class InfiltrateForDecorationInfo : WithDecorationInfo
|
||||
{
|
||||
public override object Create(ActorInitializer init) { return new InfiltrateForDecoration(init.Self, this); }
|
||||
}
|
||||
|
||||
class InfiltrateForDecoration : WithDecoration, INotifyInfiltrated
|
||||
{
|
||||
readonly HashSet<Player> infiltrators = new HashSet<Player>();
|
||||
|
||||
public InfiltrateForDecoration(Actor self, InfiltrateForDecorationInfo info)
|
||||
: base(self, info) { }
|
||||
|
||||
public void Infiltrated(Actor self, Actor infiltrator)
|
||||
{
|
||||
infiltrators.Add(infiltrator.Owner);
|
||||
}
|
||||
|
||||
protected override bool ShouldRender(Actor self)
|
||||
{
|
||||
return self.World.RenderPlayer == null || infiltrators.Any(i =>
|
||||
Info.ValidStances.HasStance(i.Stances[self.World.RenderPlayer]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,11 +153,7 @@ namespace OpenRA.Mods.RA.Traits
|
||||
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
var isAllied = self.Owner.IsAlliedWith(self.World.RenderPlayer);
|
||||
Game.Sound.Play(isAllied ? Info.LaunchSound : Info.IncomingSound);
|
||||
|
||||
var speech = isAllied ? Info.LaunchSpeechNotification : Info.IncomingSpeechNotification;
|
||||
Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", speech, self.Owner.Faction.InternalName);
|
||||
PlayLaunchSounds();
|
||||
|
||||
Actor distanceTestActor = null;
|
||||
|
||||
|
||||
@@ -55,20 +55,35 @@ namespace OpenRA.Platforms.Default
|
||||
// Clear error bit
|
||||
AL10.alGetError();
|
||||
|
||||
var devices = new List<string>();
|
||||
var next = ALC10.alcGetString(IntPtr.Zero, type);
|
||||
if (next == IntPtr.Zero || AL10.alGetError() != AL10.AL_NO_ERROR)
|
||||
// Returns a null separated list of strings, terminated by two nulls.
|
||||
var devicesPtr = ALC10.alcGetString(IntPtr.Zero, type);
|
||||
if (devicesPtr == IntPtr.Zero || AL10.alGetError() != AL10.AL_NO_ERROR)
|
||||
{
|
||||
Log.Write("sound", "Failed to query OpenAL device list using {0}", label);
|
||||
return new string[] { };
|
||||
return new string[0];
|
||||
}
|
||||
|
||||
do
|
||||
var devices = new List<string>();
|
||||
var buffer = new List<byte>();
|
||||
var offset = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var str = Marshal.PtrToStringAuto(next);
|
||||
next += Encoding.Default.GetByteCount(str) + 1;
|
||||
devices.Add(str);
|
||||
} while (Marshal.ReadByte(next) != 0);
|
||||
var b = Marshal.ReadByte(devicesPtr, offset++);
|
||||
if (b != 0)
|
||||
{
|
||||
buffer.Add(b);
|
||||
continue;
|
||||
}
|
||||
|
||||
// A null indicates termination of that string, so add that to our list.
|
||||
devices.Add(Encoding.Default.GetString(buffer.ToArray()));
|
||||
buffer.Clear();
|
||||
|
||||
// Two successive nulls indicates the end of the list.
|
||||
if (Marshal.ReadByte(devicesPtr, offset) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return devices.ToArray();
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ Container@CHAT_PANEL:
|
||||
ImageName: kick
|
||||
X: 6
|
||||
Y: 8
|
||||
LogicKeyListener@KEY_LISTENER:
|
||||
ScrollPanel@CHAT_SCROLLPANEL:
|
||||
Y: PARENT_BOTTOM - HEIGHT - 30
|
||||
Width: 550
|
||||
|
||||
@@ -57,7 +57,6 @@ Container@SKIRMISH_STATS:
|
||||
Y: 105
|
||||
Width: 482
|
||||
Height: 255
|
||||
TopBottomSpacing: 5
|
||||
ItemSpacing: 5
|
||||
Children:
|
||||
ScrollItem@TEAM_TEMPLATE:
|
||||
|
||||
@@ -40,6 +40,9 @@ Container@INGAME_ROOT:
|
||||
Width: WINDOW_RIGHT
|
||||
Height: WINDOW_BOTTOM
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
WorldCommand:
|
||||
Width: WINDOW_RIGHT
|
||||
Height: WINDOW_BOTTOM
|
||||
Container@PLAYER_ROOT:
|
||||
Container@MENU_ROOT:
|
||||
TooltipContainer@TOOLTIP_CONTAINER:
|
||||
@@ -232,7 +235,7 @@ Container@PLAYER_WIDGETS:
|
||||
LogicKeyListener@CONTROLGROUP_KEYHANDLER:
|
||||
Logic: ControlGroupLogic
|
||||
LogicTicker@SIDEBAR_TICKER:
|
||||
WorldCommand:
|
||||
UnitCommand:
|
||||
Width: WINDOW_RIGHT
|
||||
Height: WINDOW_BOTTOM
|
||||
Container@SUPPORT_POWERS:
|
||||
|
||||
@@ -41,6 +41,8 @@ OLDLST:
|
||||
Inherits: LST
|
||||
-WithRoof:
|
||||
-Selectable:
|
||||
SelectionDecorations:
|
||||
RenderSelectionBars: False
|
||||
RejectsOrders:
|
||||
Cargo:
|
||||
Types: disabled
|
||||
|
||||
@@ -41,6 +41,8 @@ OLDLST:
|
||||
Inherits: LST
|
||||
-WithRoof:
|
||||
-Selectable:
|
||||
SelectionDecorations:
|
||||
RenderSelectionBars: False
|
||||
RejectsOrders:
|
||||
Cargo:
|
||||
Types: disabled
|
||||
|
||||
@@ -100,6 +100,8 @@ OLDLST:
|
||||
Inherits: LST
|
||||
-WithRoof:
|
||||
-Selectable:
|
||||
SelectionDecorations:
|
||||
RenderSelectionBars: False
|
||||
RejectsOrders:
|
||||
Cargo:
|
||||
Types: disabled
|
||||
|
||||
@@ -109,6 +109,8 @@ OLDLST:
|
||||
Inherits: LST
|
||||
-WithRoof:
|
||||
-Selectable:
|
||||
SelectionDecorations:
|
||||
RenderSelectionBars: False
|
||||
RejectsOrders:
|
||||
Cargo:
|
||||
Types: disabled
|
||||
|
||||
@@ -60,6 +60,8 @@ OLDLST:
|
||||
Inherits: LST
|
||||
-WithRoof:
|
||||
-Selectable:
|
||||
SelectionDecorations:
|
||||
RenderSelectionBars: False
|
||||
RejectsOrders:
|
||||
Cargo:
|
||||
Types: disabled
|
||||
|
||||
@@ -14,7 +14,7 @@ Bounds: 4,1,58,50
|
||||
|
||||
Visibility: MissionSelector
|
||||
|
||||
Categories: Mission
|
||||
Categories: Campaign
|
||||
|
||||
LockPreview: True
|
||||
|
||||
@@ -155,319 +155,319 @@ Actors:
|
||||
Owner: Nod
|
||||
Actor36: brik
|
||||
Location: 58,19
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor37: brik
|
||||
Location: 57,19
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor38: brik
|
||||
Location: 56,19
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor39: brik
|
||||
Location: 55,19
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor40: brik
|
||||
Location: 54,19
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor41: brik
|
||||
Location: 53,19
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor42: brik
|
||||
Location: 52,19
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor43: brik
|
||||
Location: 47,19
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor44: brik
|
||||
Location: 46,19
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor45: brik
|
||||
Location: 45,19
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor46: brik
|
||||
Location: 44,19
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor47: brik
|
||||
Location: 43,19
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor48: brik
|
||||
Location: 42,19
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor49: brik
|
||||
Location: 41,19
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor50: brik
|
||||
Location: 40,19
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor51: brik
|
||||
Location: 58,18
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor52: brik
|
||||
Location: 57,18
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor53: brik
|
||||
Location: 53,18
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor54: brik
|
||||
Location: 52,18
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor55: brik
|
||||
Location: 47,18
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor56: brik
|
||||
Location: 46,18
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor57: brik
|
||||
Location: 41,18
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor58: brik
|
||||
Location: 40,18
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor59: brik
|
||||
Location: 58,17
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor60: brik
|
||||
Location: 40,17
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor61: brik
|
||||
Location: 58,16
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor62: brik
|
||||
Location: 40,16
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor63: brik
|
||||
Location: 58,15
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor64: brik
|
||||
Location: 40,15
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor65: brik
|
||||
Location: 58,14
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor66: brik
|
||||
Location: 41,14
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor67: brik
|
||||
Location: 40,14
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor68: brik
|
||||
Location: 58,13
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor69: brik
|
||||
Location: 41,13
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor70: brik
|
||||
Location: 40,13
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor71: brik
|
||||
Location: 58,12
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor72: brik
|
||||
Location: 58,11
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor73: brik
|
||||
Location: 57,11
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor74: brik
|
||||
Location: 58,10
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor75: brik
|
||||
Location: 57,10
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor76: brik
|
||||
Location: 41,10
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor77: brik
|
||||
Location: 40,10
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor78: brik
|
||||
Location: 41,9
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor79: brik
|
||||
Location: 40,9
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor80: cycl
|
||||
Location: 58,8
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor81: cycl
|
||||
Location: 54,8
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor82: cycl
|
||||
Location: 48,8
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor83: cycl
|
||||
Location: 47,8
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor84: cycl
|
||||
Location: 44,8
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor85: cycl
|
||||
Location: 43,8
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor86: brik
|
||||
Location: 40,8
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor87: brik
|
||||
Location: 61,7
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor88: brik
|
||||
Location: 60,7
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor89: cycl
|
||||
Location: 59,7
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor90: cycl
|
||||
Location: 58,7
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor91: cycl
|
||||
Location: 54,7
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor92: cycl
|
||||
Location: 53,7
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor93: cycl
|
||||
Location: 52,7
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor94: cycl
|
||||
Location: 51,7
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor95: cycl
|
||||
Location: 50,7
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor96: cycl
|
||||
Location: 49,7
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor97: cycl
|
||||
Location: 48,7
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor98: cycl
|
||||
Location: 43,7
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor99: brik
|
||||
Location: 40,7
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor100: brik
|
||||
Location: 61,6
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor101: brik
|
||||
Location: 60,6
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor102: cycl
|
||||
Location: 48,6
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor103: cycl
|
||||
Location: 43,6
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor104: brik
|
||||
Location: 40,6
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor105: brik
|
||||
Location: 61,5
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor106: cycl
|
||||
Location: 48,5
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor107: cycl
|
||||
Location: 47,5
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor108: cycl
|
||||
Location: 46,5
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor109: cycl
|
||||
Location: 45,5
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor110: cycl
|
||||
Location: 44,5
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor111: cycl
|
||||
Location: 43,5
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor112: brik
|
||||
Location: 40,5
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor113: brik
|
||||
Location: 61,4
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor114: brik
|
||||
Location: 40,4
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor115: brik
|
||||
Location: 61,3
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor116: brik
|
||||
Location: 60,3
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor117: brik
|
||||
Location: 41,3
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor118: brik
|
||||
Location: 40,3
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor119: brik
|
||||
Location: 61,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor120: brik
|
||||
Location: 60,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor121: brik
|
||||
Location: 59,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor122: brik
|
||||
Location: 58,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor123: brik
|
||||
Location: 57,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor124: brik
|
||||
Location: 56,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor125: brik
|
||||
Location: 55,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor126: brik
|
||||
Location: 54,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor127: brik
|
||||
Location: 53,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor128: brik
|
||||
Location: 52,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor129: brik
|
||||
Location: 51,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor130: brik
|
||||
Location: 50,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor131: brik
|
||||
Location: 49,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor132: brik
|
||||
Location: 48,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor133: brik
|
||||
Location: 47,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor134: brik
|
||||
Location: 46,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor135: brik
|
||||
Location: 45,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor136: brik
|
||||
Location: 44,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor137: brik
|
||||
Location: 43,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor138: brik
|
||||
Location: 42,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor139: brik
|
||||
Location: 41,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor140: brik
|
||||
Location: 40,2
|
||||
Owner: Neutral
|
||||
Owner: Nod
|
||||
Actor141: t01
|
||||
Location: 30,36
|
||||
Owner: Neutral
|
||||
|
||||
@@ -116,6 +116,8 @@ OLDLST:
|
||||
Inherits: LST
|
||||
-WithRoof:
|
||||
-Selectable:
|
||||
SelectionDecorations:
|
||||
RenderSelectionBars: False
|
||||
RejectsOrders:
|
||||
Cargo:
|
||||
Types: disabled
|
||||
|
||||
@@ -92,7 +92,7 @@ ProduceHarvester = function(building)
|
||||
end
|
||||
|
||||
ProduceInfantry = function(building)
|
||||
if building.IsDead then
|
||||
if building.IsDead or building.Owner ~= enemy then
|
||||
return
|
||||
elseif not CheckForHarvester() then
|
||||
Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceInfantry(building) end)
|
||||
@@ -116,7 +116,7 @@ ProduceInfantry = function(building)
|
||||
end
|
||||
|
||||
ProduceVehicle = function(building)
|
||||
if building.IsDead then
|
||||
if building.IsDead or building.Owner ~= enemy then
|
||||
return
|
||||
elseif not CheckForHarvester() then
|
||||
ProduceHarvester(building)
|
||||
|
||||
@@ -92,7 +92,7 @@ ProduceHarvester = function(building)
|
||||
end
|
||||
|
||||
ProduceInfantry = function(building)
|
||||
if building.IsDead then
|
||||
if building.IsDead or building.Owner ~= enemy then
|
||||
return
|
||||
elseif not CheckForHarvester() then
|
||||
Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceInfantry(building) end)
|
||||
@@ -116,7 +116,7 @@ ProduceInfantry = function(building)
|
||||
end
|
||||
|
||||
ProduceVehicle = function(building)
|
||||
if building.IsDead then
|
||||
if building.IsDead or building.Owner ~= enemy then
|
||||
return
|
||||
elseif not CheckForHarvester() then
|
||||
ProduceHarvester(building)
|
||||
|
||||
@@ -114,7 +114,7 @@ ProduceHarvester = function(building)
|
||||
end
|
||||
|
||||
ProduceInfantry = function(building)
|
||||
if building.IsDead then
|
||||
if building.IsDead or building.Owner ~= enemy then
|
||||
return
|
||||
elseif not CheckForHarvester() then
|
||||
Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceInfantry(building) end)
|
||||
@@ -138,7 +138,7 @@ ProduceInfantry = function(building)
|
||||
end
|
||||
|
||||
ProduceVehicle = function(building)
|
||||
if building.IsDead then
|
||||
if building.IsDead or building.Owner ~= enemy then
|
||||
return
|
||||
elseif not CheckForHarvester() then
|
||||
ProduceHarvester(building)
|
||||
|
||||
@@ -114,7 +114,7 @@ ProduceHarvester = function(building)
|
||||
end
|
||||
|
||||
ProduceInfantry = function(building)
|
||||
if building.IsDead then
|
||||
if building.IsDead or building.Owner ~= enemy then
|
||||
return
|
||||
elseif not CheckForHarvester() then
|
||||
Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceInfantry(building) end)
|
||||
@@ -138,7 +138,7 @@ ProduceInfantry = function(building)
|
||||
end
|
||||
|
||||
ProduceVehicle = function(building)
|
||||
if building.IsDead then
|
||||
if building.IsDead or building.Owner ~= enemy then
|
||||
return
|
||||
elseif not CheckForHarvester() then
|
||||
ProduceHarvester(building)
|
||||
|
||||
@@ -114,7 +114,7 @@ ProduceHarvester = function(building)
|
||||
end
|
||||
|
||||
ProduceInfantry = function(building)
|
||||
if building.IsDead then
|
||||
if building.IsDead or building.Owner ~= enemy then
|
||||
return
|
||||
elseif not CheckForHarvester() then
|
||||
Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceInfantry(building) end)
|
||||
@@ -138,7 +138,7 @@ ProduceInfantry = function(building)
|
||||
end
|
||||
|
||||
ProduceVehicle = function(building)
|
||||
if building.IsDead then
|
||||
if building.IsDead or building.Owner ~= enemy then
|
||||
return
|
||||
elseif not CheckForHarvester() then
|
||||
ProduceHarvester(building)
|
||||
|
||||
@@ -46,17 +46,12 @@ Player:
|
||||
silo: 0%
|
||||
fix: 1%
|
||||
hpad: 2%
|
||||
CapturingActorTypes: e6
|
||||
CapturableActorTypes: fact, fact.gdi, fact.nod, nuke, nuk2, proc, pyle, hand, afld, weap, hpad, hq, fix, eye, tmpl, obli, v19
|
||||
CheckCaptureTargetsForVisibility: false
|
||||
MaximumCaptureTargetOptions: 15
|
||||
UnitsToBuild:
|
||||
e1: 65%
|
||||
e2: 25%
|
||||
e3: 40%
|
||||
e4: 15%
|
||||
e5: 15%
|
||||
e6: 5%
|
||||
harv: 10%
|
||||
bggy: 5%
|
||||
bike: 40%
|
||||
@@ -72,7 +67,6 @@ Player:
|
||||
orca: 5%
|
||||
UnitLimits:
|
||||
harv: 8
|
||||
e6: 3
|
||||
SquadSize: 15
|
||||
SupportPowerDecision@Airstrike:
|
||||
OrderName: AirstrikePowerInfoOrder
|
||||
|
||||
@@ -165,8 +165,6 @@ ORCA:
|
||||
|
||||
C17:
|
||||
Inherits: ^Plane
|
||||
ParaDrop:
|
||||
LZRange: 1
|
||||
Tooltip:
|
||||
Name: Supply Aircraft
|
||||
Description: Drops vehicle reinforcements on Airstrips
|
||||
|
||||
@@ -194,7 +194,6 @@
|
||||
Beach: 80
|
||||
SelectionDecorations:
|
||||
WithSpriteControlGroupDecoration:
|
||||
ReferencePoint: Top
|
||||
Selectable:
|
||||
Bounds: 12,17,0,-6
|
||||
Targetable:
|
||||
@@ -591,6 +590,7 @@
|
||||
^CivField:
|
||||
Inherits: ^CivBuilding
|
||||
-Selectable:
|
||||
-SelectionDecorations:
|
||||
Tooltip:
|
||||
GenericName: Field
|
||||
-WithBuildingExplosion:
|
||||
|
||||
@@ -255,6 +255,7 @@ pips:
|
||||
Length: *
|
||||
groups: pdigits
|
||||
Length: *
|
||||
Offset: 9, 5
|
||||
pip-hazmat: pip-hazmat
|
||||
Length: *
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ dialog4: dialog.png
|
||||
lobby-bits: buttons.png
|
||||
spawn-unclaimed: 159,5,23,22
|
||||
spawn-claimed: 127,5,23,22
|
||||
admin: 187,5,7,5
|
||||
admin: 186,5,7,5
|
||||
colorpicker: 127,5,23,22
|
||||
huepicker: 194,0,7,15
|
||||
protected: 202,0,10,13
|
||||
|
||||
@@ -57,7 +57,6 @@ Container@SKIRMISH_STATS:
|
||||
Y: 105
|
||||
Width: 482
|
||||
Height: 265
|
||||
TopBottomSpacing: 5
|
||||
ItemSpacing: 5
|
||||
Children:
|
||||
ScrollItem@TEAM_TEMPLATE:
|
||||
|
||||
@@ -3,6 +3,9 @@ Container@PLAYER_WIDGETS:
|
||||
LogicKeyListener@CONTROLGROUP_KEYHANDLER:
|
||||
Logic: ControlGroupLogic
|
||||
LogicTicker@SIDEBAR_TICKER:
|
||||
UnitCommand:
|
||||
Width: WINDOW_RIGHT
|
||||
Height: WINDOW_BOTTOM
|
||||
Container@SUPPORT_POWERS:
|
||||
Logic: SupportPowerBinLogic
|
||||
X: 10
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
d2k: Dune 2000 (English)
|
||||
d2k-a: Dune 2000 (English)
|
||||
IDFiles:
|
||||
MUSIC/AMBUSH.AUD: bd5926a56a83bc0e49f96037e1f909014ac0772a
|
||||
SETUP/SETUP.Z: 937f5ceb1236bf3f3d4e43929305ffe5004078e7
|
||||
@@ -854,7 +854,7 @@ d2k: Dune 2000 (English)
|
||||
^Content/d2k/v2/SOUND.RS:
|
||||
Offset: 20559431
|
||||
Length: 1929247
|
||||
d2k-linux: Dune 2000 (English)
|
||||
d2k-a-linux: Dune 2000 (English)
|
||||
IDFiles:
|
||||
music/ambush.aud: bd5926a56a83bc0e49f96037e1f909014ac0772a
|
||||
setup/setup.z: 937f5ceb1236bf3f3d4e43929305ffe5004078e7
|
||||
1712
mods/d2k/installer/d2kb.yaml
Normal file
1712
mods/d2k/installer/d2kb.yaml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -206,7 +206,7 @@ ModContent:
|
||||
Packages:
|
||||
base: Base Game Files
|
||||
TestFiles: ^Content/d2k/v2/BLOXBASE.R8, ^Content/d2k/v2/BLOXBAT.R8, ^Content/d2k/v2/BLOXBGBS.R8, ^Content/d2k/v2/BLOXICE.R8, ^Content/d2k/v2/BLOXTREE.R8, ^Content/d2k/v2/BLOXWAST.R8, ^Content/d2k/v2/SOUND.RS, ^Content/d2k/v2/PALETTE.BIN, ^Content/d2k/v2/FONT.BIN, ^Content/d2k/v2/FONTCOL.FNT, ^Content/d2k/v2/FONTCOL.FPL
|
||||
Sources: d2k, d2k-linux, gruntmods
|
||||
Sources: d2k-a, d2k-a-linux, d2k-b, d2k-b-linux, gruntmods
|
||||
Required: true
|
||||
Download: basefiles
|
||||
patch: 1.06 Patch Content
|
||||
@@ -216,13 +216,14 @@ ModContent:
|
||||
Download: patch106
|
||||
music: Game Music
|
||||
TestFiles: ^Content/d2k/v2/Music/AMBUSH.AUD
|
||||
Sources: d2k, d2k-linux, gruntmods
|
||||
Sources: d2k-a, d2k-a-linux, d2k-b, d2k-b-linux, gruntmods
|
||||
movies: Campaign Briefings
|
||||
TestFiles: ^Content/d2k/v2/Movies/A_BR01_E.VQA
|
||||
Sources: d2k, d2k-linux
|
||||
Sources: d2k-a, d2k-a-linux, d2k-b, d2k-b-linux
|
||||
Downloads:
|
||||
d2k|installer/downloads.yaml
|
||||
Sources:
|
||||
d2k|installer/d2k.yaml
|
||||
d2k|installer/d2ka.yaml
|
||||
d2k|installer/d2kb.yaml
|
||||
d2k|installer/downloads.yaml
|
||||
d2k|installer/gruntmods.yaml
|
||||
|
||||
@@ -198,7 +198,7 @@ sardaukar:
|
||||
Buildable:
|
||||
Queue: Infantry
|
||||
BuildPaletteOrder: 80
|
||||
Prerequisites: ~barracks.harkonnen, palace, ~techlevel.high
|
||||
Prerequisites: ~barracks.harkonnen, upgrade.barracks, high_tech_factory, ~techlevel.high
|
||||
BuildDuration: 81
|
||||
BuildDurationModifier: 40
|
||||
Valued:
|
||||
|
||||
@@ -124,6 +124,7 @@ pips:
|
||||
groups: DATA.R8
|
||||
Start: 17
|
||||
Length: 10
|
||||
Offset: 3, 3
|
||||
pickup-indicator: DATA.R8
|
||||
Start: 112
|
||||
pip-empty: DATA.R8
|
||||
|
||||
@@ -37,6 +37,7 @@ Container@CHAT_PANEL:
|
||||
Height: 25
|
||||
Text: X
|
||||
Font: Bold
|
||||
LogicKeyListener@KEY_LISTENER:
|
||||
ScrollPanel@CHAT_SCROLLPANEL:
|
||||
Y: PARENT_BOTTOM - HEIGHT - 30
|
||||
Width: 550
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user