Compare commits
60 Commits
playtest-2
...
release-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5943ecc943 | ||
|
|
c13e078ebd | ||
|
|
426c3d68a1 | ||
|
|
5b6b4ab4ee | ||
|
|
835a9e605a | ||
|
|
390fbe6718 | ||
|
|
1bf3d090d5 | ||
|
|
9e279b9987 | ||
|
|
4b6780bc67 | ||
|
|
0a9aa488db | ||
|
|
97ecfa548e | ||
|
|
82a98ae6e6 | ||
|
|
32b241eae7 | ||
|
|
e8084ade68 | ||
|
|
16c2cbf577 | ||
|
|
b51980a9ea | ||
|
|
631a61e219 | ||
|
|
5ff2546a5d | ||
|
|
bf2640308a | ||
|
|
63cee6a0e3 | ||
|
|
cab47c4975 | ||
|
|
6385c24875 | ||
|
|
1d1bafd836 | ||
|
|
5cacce3c93 | ||
|
|
798c92abeb | ||
|
|
9236a05d53 | ||
|
|
fc7b3d83e5 | ||
|
|
00ddf82e85 | ||
|
|
c10ebb455b | ||
|
|
d999a0534b | ||
|
|
02454fb764 | ||
|
|
df81857abe | ||
|
|
ae0745b542 | ||
|
|
89b5e34320 | ||
|
|
47cc6eda7e | ||
|
|
6da23ed3d7 | ||
|
|
3ee4613a98 | ||
|
|
616bfbe553 | ||
|
|
ecbcc9c8bb | ||
|
|
068ce9b215 | ||
|
|
170bcd500c | ||
|
|
6efb40c9a6 | ||
|
|
3c92c2d789 | ||
|
|
f823d20a86 | ||
|
|
ca4b97cd03 | ||
|
|
8b5e322891 | ||
|
|
70e1c4bb0c | ||
|
|
8690ad4e19 | ||
|
|
a01233c3df | ||
|
|
d0d55b3fc1 | ||
|
|
2a26a07ce6 | ||
|
|
65e088204f | ||
|
|
77e38ceea9 | ||
|
|
989d1475b9 | ||
|
|
580a96cda4 | ||
|
|
5a8964aabe | ||
|
|
c9dcf305c9 | ||
|
|
5489d6909c | ||
|
|
85412a6d98 | ||
|
|
b69806123a |
2
.gitignore
vendored
@@ -26,7 +26,7 @@ mods/*/*.pdb
|
||||
/*.exe
|
||||
/*.exe.config
|
||||
thirdparty/download/*
|
||||
*.mmdb.gz
|
||||
IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP
|
||||
|
||||
# backup files by various editors
|
||||
*~
|
||||
|
||||
6
AUTHORS
@@ -160,9 +160,6 @@ FreeType License.
|
||||
|
||||
Using OpenAL Soft distributed under the GNU LGPL.
|
||||
|
||||
Using MaxMind GeoIP2 .NET API distributed under
|
||||
the Apache 2.0 license.
|
||||
|
||||
Using SDL2-CS and OpenAL-CS created by Ethan
|
||||
Lee and released under the zlib license.
|
||||
|
||||
@@ -182,6 +179,9 @@ Krueger and distributed under the GNU GPL terms.
|
||||
Using rix0rrr.BeaconLib developed by Rico Huijbers
|
||||
distributed under MIT License.
|
||||
|
||||
This site or product includes IP2Location LITE data
|
||||
available from http://www.ip2location.com.
|
||||
|
||||
Finally, special thanks goes to the original teams
|
||||
at Westwood Studios and EA for creating the classic
|
||||
games which OpenRA aims to reimagine.
|
||||
|
||||
7
Makefile
@@ -40,7 +40,7 @@
|
||||
WHITELISTED_OPENRA_ASSEMBLIES = OpenRA.Game.exe OpenRA.Utility.exe OpenRA.Platforms.Default.dll OpenRA.Mods.Common.dll OpenRA.Mods.Cnc.dll OpenRA.Mods.D2k.dll OpenRA.Game.dll
|
||||
|
||||
# These are explicitly shipped alongside our core files by the packaging script
|
||||
WHITELISTED_THIRDPARTY_ASSEMBLIES = ICSharpCode.SharpZipLib.dll FuzzyLogicLibrary.dll MaxMind.Db.dll Eluant.dll rix0rrr.BeaconLib.dll Open.Nat.dll SDL2-CS.dll OpenAL-CS.dll
|
||||
WHITELISTED_THIRDPARTY_ASSEMBLIES = ICSharpCode.SharpZipLib.dll FuzzyLogicLibrary.dll Eluant.dll rix0rrr.BeaconLib.dll Open.Nat.dll SDL2-CS.dll OpenAL-CS.dll
|
||||
|
||||
# These are shipped in our custom minimal mono runtime and also available in the full system-installed .NET/mono stack
|
||||
# This list *must* be kept in sync with the files packaged by the AppImageSupport and OpenRALauncherOSX repositories
|
||||
@@ -157,10 +157,11 @@ ifeq ($(WIN32), $(filter $(WIN32),true yes y on 1))
|
||||
else
|
||||
@$(MSBUILD) -t:build -p:Configuration=Release
|
||||
endif
|
||||
@./fetch-geoip.sh
|
||||
|
||||
clean:
|
||||
@ $(MSBUILD) -t:clean
|
||||
@-$(RM_F) *.config
|
||||
@-$(RM_F) *.config IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP
|
||||
@-$(RM_F) *.exe *.dll *.dylib ./OpenRA*/*.dll *.pdb mods/**/*.dll mods/**/*.pdb *.resources
|
||||
@-$(RM_RF) ./*/bin ./*/obj
|
||||
@-$(RM_RF) ./thirdparty/download
|
||||
@@ -218,6 +219,7 @@ install-engine:
|
||||
@$(INSTALL_DATA) VERSION "$(DATA_INSTALL_DIR)/VERSION"
|
||||
@$(INSTALL_DATA) AUTHORS "$(DATA_INSTALL_DIR)/AUTHORS"
|
||||
@$(INSTALL_DATA) COPYING "$(DATA_INSTALL_DIR)/COPYING"
|
||||
@$(INSTALL_DATA) IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP "$(DATA_INSTALL_DIR)/IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP"
|
||||
|
||||
@$(CP_R) glsl "$(DATA_INSTALL_DIR)"
|
||||
@$(CP_R) lua "$(DATA_INSTALL_DIR)"
|
||||
@@ -227,7 +229,6 @@ install-engine:
|
||||
@$(INSTALL_PROGRAM) ICSharpCode.SharpZipLib.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) FuzzyLogicLibrary.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) Open.Nat.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) MaxMind.Db.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) rix0rrr.BeaconLib.dll "$(DATA_INSTALL_DIR)"
|
||||
|
||||
install-common-mod-files:
|
||||
|
||||
@@ -15,9 +15,16 @@ using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public enum GLProfile
|
||||
{
|
||||
Modern,
|
||||
Embedded,
|
||||
Legacy
|
||||
}
|
||||
|
||||
public interface IPlatform
|
||||
{
|
||||
IPlatformWindow CreateWindow(Size size, WindowMode windowMode, float scaleModifier, int batchSize, int videoDisplay);
|
||||
IPlatformWindow CreateWindow(Size size, WindowMode windowMode, float scaleModifier, int batchSize, int videoDisplay, GLProfile profile);
|
||||
ISoundEngine CreateSound(string device);
|
||||
IFont CreateFont(byte[] data);
|
||||
}
|
||||
@@ -60,6 +67,10 @@ namespace OpenRA
|
||||
void SetHardwareCursor(IHardwareCursor cursor);
|
||||
void SetRelativeMouseMode(bool mode);
|
||||
void SetScaleModifier(float scale);
|
||||
|
||||
GLProfile GLProfile { get; }
|
||||
|
||||
GLProfile[] SupportedGLProfiles { get; }
|
||||
}
|
||||
|
||||
public interface IGraphicsContext : IDisposable
|
||||
|
||||
@@ -19,6 +19,11 @@ namespace OpenRA.Graphics
|
||||
[Flags]
|
||||
public enum ScrollDirection { None = 0, Up = 1, Left = 2, Down = 4, Right = 8 }
|
||||
|
||||
public interface INotifyViewportZoomExtentsChanged
|
||||
{
|
||||
void ViewportZoomExtentsChanged(float minZoom, float maxZoom);
|
||||
}
|
||||
|
||||
public static class ViewportExts
|
||||
{
|
||||
public static bool Includes(this ScrollDirection d, ScrollDirection s)
|
||||
@@ -237,6 +242,9 @@ namespace OpenRA.Graphics
|
||||
Zoom = minZoom;
|
||||
else
|
||||
Zoom = Zoom.Clamp(minZoom, maxZoom);
|
||||
|
||||
foreach (var t in worldRenderer.World.WorldActor.TraitsImplementing<INotifyViewportZoomExtentsChanged>())
|
||||
t.ViewportZoomExtentsChanged(minZoom, maxZoom);
|
||||
}
|
||||
|
||||
public CPos ViewToWorld(int2 view)
|
||||
|
||||
@@ -11,63 +11,119 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using ICSharpCode.SharpZipLib.GZip;
|
||||
using MaxMind.Db;
|
||||
using System.Net.Sockets;
|
||||
using System.Numerics;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
public class GeoIP
|
||||
{
|
||||
public class GeoIP2Record
|
||||
class IP2LocationReader
|
||||
{
|
||||
[Constructor]
|
||||
public GeoIP2Record(GeoIP2Country country)
|
||||
public readonly DateTime Date;
|
||||
readonly Stream stream;
|
||||
readonly uint columnCount;
|
||||
readonly uint v4Count;
|
||||
readonly uint v4Offset;
|
||||
readonly uint v6Count;
|
||||
readonly uint v6Offset;
|
||||
|
||||
public IP2LocationReader(Stream source)
|
||||
{
|
||||
Country = country;
|
||||
// Copy stream data for reuse
|
||||
stream = new MemoryStream();
|
||||
source.CopyTo(stream);
|
||||
stream.Position = 0;
|
||||
|
||||
if (stream.ReadUInt8() != 1)
|
||||
throw new InvalidDataException("Only IP2Location type 1 databases are supported.");
|
||||
|
||||
columnCount = stream.ReadUInt8();
|
||||
var year = stream.ReadUInt8();
|
||||
var month = stream.ReadUInt8();
|
||||
var day = stream.ReadUInt8();
|
||||
Date = new DateTime(2000 + year, month, day);
|
||||
|
||||
v4Count = stream.ReadUInt32();
|
||||
v4Offset = stream.ReadUInt32();
|
||||
v6Count = stream.ReadUInt32();
|
||||
v6Offset = stream.ReadUInt32();
|
||||
}
|
||||
|
||||
public GeoIP2Country Country { get; set; }
|
||||
}
|
||||
|
||||
public class GeoIP2Country
|
||||
{
|
||||
[Constructor]
|
||||
public GeoIP2Country(GeoIP2CountryNames names)
|
||||
BigInteger AddressForIndex(long index, bool isIPv6)
|
||||
{
|
||||
Names = names;
|
||||
var start = isIPv6 ? v6Offset : v4Offset;
|
||||
var offset = isIPv6 ? 12 : 0;
|
||||
stream.Seek(start + index * (4 * columnCount + offset) - 1, SeekOrigin.Begin);
|
||||
return new BigInteger(stream.ReadBytes(isIPv6 ? 16 : 4).Append((byte)0).ToArray());
|
||||
}
|
||||
|
||||
public GeoIP2CountryNames Names { get; set; }
|
||||
}
|
||||
|
||||
public class GeoIP2CountryNames
|
||||
{
|
||||
[Constructor]
|
||||
public GeoIP2CountryNames(string en)
|
||||
string CountryForIndex(long index, bool isIPv6)
|
||||
{
|
||||
English = en;
|
||||
// Read file offset for country entry
|
||||
var start = isIPv6 ? v6Offset : v4Offset;
|
||||
var offset = isIPv6 ? 12 : 0;
|
||||
stream.Seek(start + index * (4 * columnCount + offset) + offset + 3, SeekOrigin.Begin);
|
||||
var countryOffset = stream.ReadUInt32();
|
||||
|
||||
// Read length-prefixed country name
|
||||
stream.Seek(countryOffset + 3, SeekOrigin.Begin);
|
||||
var length = stream.ReadUInt8();
|
||||
|
||||
// "-" is used to represent an unknown country in the database
|
||||
var country = stream.ReadASCII(length);
|
||||
return country != "-" ? country : null;
|
||||
}
|
||||
|
||||
public string English { get; set; }
|
||||
public string LookupCountry(IPAddress ip)
|
||||
{
|
||||
var isIPv6 = ip.AddressFamily == AddressFamily.InterNetworkV6;
|
||||
if (!isIPv6 && ip.AddressFamily != AddressFamily.InterNetwork)
|
||||
return null;
|
||||
|
||||
// Locate IP using a binary search
|
||||
// The IP2Location python parser has an additional
|
||||
// optimization that can jump directly to the row, but this adds
|
||||
// extra complexity that isn't obviously needed for our limited database size
|
||||
long low = 0;
|
||||
long high = isIPv6 ? v6Count : v4Count;
|
||||
|
||||
// Append an empty byte to force the data to be treated as unsigned
|
||||
var ipNumber = new BigInteger(ip.GetAddressBytes().Reverse().Append((byte)0).ToArray());
|
||||
while (low <= high)
|
||||
{
|
||||
var mid = (low + high) / 2;
|
||||
var min = AddressForIndex(mid, isIPv6);
|
||||
var max = AddressForIndex(mid + 1, isIPv6);
|
||||
if (min <= ipNumber && ipNumber < max)
|
||||
return CountryForIndex(mid, isIPv6);
|
||||
|
||||
if (ipNumber < min)
|
||||
high = mid - 1;
|
||||
else
|
||||
low = mid + 1;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Reader database;
|
||||
static IP2LocationReader database;
|
||||
|
||||
public static void Initialize(string databasePath)
|
||||
public static void Initialize(string databasePath = "IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP")
|
||||
{
|
||||
if (string.IsNullOrEmpty(databasePath))
|
||||
if (!File.Exists(databasePath))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
using (var fileStream = new FileStream(databasePath, FileMode.Open, FileAccess.Read))
|
||||
using (var z = new ZipFile(databasePath))
|
||||
{
|
||||
if (databasePath.EndsWith(".gz"))
|
||||
using (var gzipStream = new GZipInputStream(fileStream))
|
||||
database = new Reader(gzipStream);
|
||||
else
|
||||
database = new Reader(fileStream);
|
||||
var entry = z.FindEntry("IP2LOCATION-LITE-DB1.IPV6.BIN", false);
|
||||
database = new IP2LocationReader(z.GetInputStream(entry));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -82,9 +138,7 @@ namespace OpenRA.Network
|
||||
{
|
||||
try
|
||||
{
|
||||
var record = database.Find<GeoIP2Record>(ip);
|
||||
if (record != null)
|
||||
return record.Country.Names.English;
|
||||
return database.LookupCountry(ip);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -58,10 +58,6 @@
|
||||
<HintPath>..\thirdparty\download\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="MaxMind.Db">
|
||||
<HintPath>..\thirdparty\download\MaxMind.Db.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<ProjectReference Include="..\OpenRA.PostProcess\OpenRA.PostProcess.csproj">
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace OpenRA.Orders
|
||||
public virtual bool InputOverridesSelection(World world, int2 xy, MouseInput mi)
|
||||
{
|
||||
var actor = world.ScreenMap.ActorsAtMouse(xy)
|
||||
.Where(a => !a.Actor.IsDead)
|
||||
.Where(a => !a.Actor.IsDead && a.Actor.Info.HasTraitInfo<SelectableInfo>() && (a.Actor.Owner.IsAlliedWith(world.RenderPlayer) || !world.FogObscures(a.Actor)))
|
||||
.WithHighestSelectionPriority(xy, mi.Modifiers);
|
||||
|
||||
if (actor == null)
|
||||
|
||||
@@ -66,7 +66,10 @@ namespace OpenRA
|
||||
this.platform = platform;
|
||||
var resolution = GetResolution(graphicSettings);
|
||||
|
||||
Window = platform.CreateWindow(new Size(resolution.Width, resolution.Height), graphicSettings.Mode, graphicSettings.UIScale, graphicSettings.BatchSize, graphicSettings.VideoDisplay);
|
||||
Window = platform.CreateWindow(new Size(resolution.Width, resolution.Height),
|
||||
graphicSettings.Mode, graphicSettings.UIScale, graphicSettings.BatchSize,
|
||||
graphicSettings.VideoDisplay, graphicSettings.GLProfile);
|
||||
|
||||
Context = Window.Context;
|
||||
|
||||
TempBufferSize = graphicSettings.BatchSize;
|
||||
@@ -301,6 +304,8 @@ namespace OpenRA
|
||||
public Size NativeResolution { get { return Window.NativeWindowSize; } }
|
||||
public float WindowScale { get { return Window.EffectiveWindowScale; } }
|
||||
public float NativeWindowScale { get { return Window.NativeWindowScale; } }
|
||||
public GLProfile GLProfile { get { return Window.GLProfile; } }
|
||||
public GLProfile[] SupportedGLProfiles { get { return Window.SupportedGLProfiles; } }
|
||||
|
||||
public interface IBatchRenderer { void Flush(); }
|
||||
|
||||
|
||||
@@ -149,7 +149,8 @@ namespace OpenRA.Server
|
||||
|
||||
randomSeed = (int)DateTime.Now.ToBinary();
|
||||
|
||||
GeoIP.Initialize(settings.GeoIPDatabase);
|
||||
if (type != ServerType.Local && settings.EnableGeoIP)
|
||||
GeoIP.Initialize();
|
||||
|
||||
if (UPnP.Status == UPnPStatus.Enabled)
|
||||
UPnP.ForwardPort(Settings.ListenPort, Settings.ListenPort).Wait();
|
||||
@@ -348,7 +349,7 @@ namespace OpenRA.Server
|
||||
{
|
||||
Name = OpenRA.Settings.SanitizedPlayerName(handshake.Client.Name),
|
||||
IPAddress = ipAddress.ToString(),
|
||||
AnonymizedIPAddress = Settings.ShareAnonymizedIPs ? Session.AnonymizeIP(ipAddress) : null,
|
||||
AnonymizedIPAddress = Type != ServerType.Local && Settings.ShareAnonymizedIPs ? Session.AnonymizeIP(ipAddress) : null,
|
||||
Location = GeoIP.LookupCountry(ipAddress),
|
||||
Index = newConn.PlayerIndex,
|
||||
PreferredColor = handshake.Client.PreferredColor,
|
||||
@@ -498,10 +499,16 @@ namespace OpenRA.Server
|
||||
profile.ProfileName, profile.ProfileID);
|
||||
}
|
||||
else if (profile.KeyRevoked)
|
||||
{
|
||||
profile = null;
|
||||
Log.Write("server", "{0} failed to authenticate as {1} (key revoked)", newConn.Socket.RemoteEndPoint, handshake.Fingerprint);
|
||||
}
|
||||
else
|
||||
{
|
||||
profile = null;
|
||||
Log.Write("server", "{0} failed to authenticate as {1} (signature verification failed)",
|
||||
newConn.Socket.RemoteEndPoint, handshake.Fingerprint);
|
||||
}
|
||||
}
|
||||
else
|
||||
Log.Write("server", "{0} failed to authenticate as {1} (invalid server response: `{2}` is not `Player`)",
|
||||
|
||||
@@ -82,13 +82,12 @@ namespace OpenRA
|
||||
[Desc("Sets the timestamp format. Defaults to the ISO 8601 standard.")]
|
||||
public string TimestampFormat = "yyyy-MM-ddTHH:mm:ss";
|
||||
|
||||
[Desc("Path to a MaxMind GeoLite2 database to use for player geo-location.",
|
||||
"Database files can be downloaded from https://dev.maxmind.com/geoip/geoip2/geolite2/")]
|
||||
public string GeoIPDatabase = null;
|
||||
|
||||
[Desc("Allow clients to see anonymised IPs for other clients.")]
|
||||
public bool ShareAnonymizedIPs = true;
|
||||
|
||||
[Desc("Allow clients to see the country of other clients.")]
|
||||
public bool EnableGeoIP = true;
|
||||
|
||||
public ServerSettings Clone()
|
||||
{
|
||||
return (ServerSettings)MemberwiseClone();
|
||||
@@ -173,12 +172,15 @@ namespace OpenRA
|
||||
[Desc("Disable operating-system provided cursor rendering.")]
|
||||
public bool DisableHardwareCursors = false;
|
||||
|
||||
[Desc("Use OpenGL ES if both ES and regular OpenGL are available.")]
|
||||
public bool PreferGLES = false;
|
||||
|
||||
[Desc("Display index to use in a multi-monitor fullscreen setup.")]
|
||||
public int VideoDisplay = 0;
|
||||
|
||||
[Desc("Preferred OpenGL profile to use.",
|
||||
"Modern: OpenGL Core Profile 3.2 or greater.",
|
||||
"Embedded: OpenGL ES 3.0 or greater.",
|
||||
"Legacy: OpenGL 2.1 with framebuffer_object extension.")]
|
||||
public GLProfile GLProfile = GLProfile.Modern;
|
||||
|
||||
public int BatchSize = 8192;
|
||||
public int SheetSize = 2048;
|
||||
|
||||
@@ -228,7 +230,8 @@ namespace OpenRA
|
||||
public int MouseScrollDeadzone = 8;
|
||||
|
||||
public bool UseClassicMouseStyle = false;
|
||||
public bool ClassicMouseMiddleScroll = false;
|
||||
public bool UseAlternateScrollButton = false;
|
||||
|
||||
public StatusBarsType StatusBars = StatusBarsType.Standard;
|
||||
public TargetLinesType TargetLines = TargetLinesType.Manual;
|
||||
public bool UsePlayerStanceColors = false;
|
||||
|
||||
@@ -107,9 +107,11 @@ namespace OpenRA.Mods.Cnc.Activities
|
||||
if (minefield != null)
|
||||
{
|
||||
var positionable = (IPositionable)movement;
|
||||
var mobile = positionable as Mobile;
|
||||
minefield.RemoveAll(c => self.World.ActorMap.GetActorsAt(c)
|
||||
.Any(a => a.Info.Name == minelayer.Info.Mine.ToLowerInvariant()) ||
|
||||
(!positionable.CanEnterCell(c, null, BlockedByActor.Immovable) && !self.World.FogObscures(c)));
|
||||
.Any(a => a.Info.Name == minelayer.Info.Mine.ToLowerInvariant() && a.CanBeViewedByPlayer(self.Owner)) ||
|
||||
((!positionable.CanEnterCell(c, null, BlockedByActor.Immovable) || (mobile != null && !mobile.CanStayInCell(c)))
|
||||
&& self.Owner.Shroud.IsVisible(c)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,15 +25,11 @@ namespace OpenRA.Mods.Cnc
|
||||
Sprite[] border;
|
||||
float2 nodPos, gdiPos, evaPos;
|
||||
Rectangle bounds;
|
||||
|
||||
SpriteFont loadingFont, versionFont;
|
||||
string loadingText, versionText;
|
||||
float2 loadingPos, versionPos;
|
||||
string versionText;
|
||||
|
||||
Sheet lastSheet;
|
||||
int lastDensity;
|
||||
Size lastResolution;
|
||||
IReadOnlyDictionary<string, SpriteFont> lastFonts;
|
||||
|
||||
public override void Init(ModData modData, Dictionary<string, string> info)
|
||||
{
|
||||
@@ -82,20 +78,6 @@ namespace OpenRA.Mods.Cnc
|
||||
|
||||
var barY = bounds.Height - 78;
|
||||
|
||||
// The fonts dictionary may change when switching between the mod and content installer
|
||||
if (r.Fonts != lastFonts)
|
||||
{
|
||||
lastFonts = r.Fonts;
|
||||
|
||||
loadingFont = lastFonts["BigBold"];
|
||||
loadingText = Info["Text"];
|
||||
loadingPos = new float2((bounds.Width - loadingFont.Measure(loadingText).X) / 2, barY);
|
||||
|
||||
versionFont = lastFonts["Regular"];
|
||||
var versionSize = versionFont.Measure(versionText);
|
||||
versionPos = new float2(bounds.Width - 107 - versionSize.X / 2, 115 - versionSize.Y / 2);
|
||||
}
|
||||
|
||||
loadTick = ++loadTick % 8;
|
||||
|
||||
r.RgbaSpriteRenderer.DrawSprite(gdiLogo, gdiPos);
|
||||
@@ -104,11 +86,18 @@ namespace OpenRA.Mods.Cnc
|
||||
|
||||
WidgetUtils.DrawPanel(bounds, border);
|
||||
|
||||
if (loadingFont != null)
|
||||
if (r.Fonts != null)
|
||||
{
|
||||
var loadingFont = r.Fonts["BigBold"];
|
||||
var loadingText = Info["Text"];
|
||||
var loadingPos = new float2((bounds.Width - loadingFont.Measure(loadingText).X) / 2, barY);
|
||||
loadingFont.DrawText(loadingText, loadingPos, Color.Gray);
|
||||
|
||||
if (versionFont != null)
|
||||
var versionFont = r.Fonts["Regular"];
|
||||
var versionSize = versionFont.Measure(versionText);
|
||||
var versionPos = new float2(bounds.Width - 107 - versionSize.X / 2, 115 - versionSize.Y / 2);
|
||||
versionFont.DrawTextWithContrast(versionText, versionPos, Color.White, Color.Black, 2);
|
||||
}
|
||||
|
||||
for (var i = 0; i <= 8; i++)
|
||||
{
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
var movement = self.Trait<IPositionable>();
|
||||
|
||||
var minefield = GetMinefieldCells(minefieldStart, cell, Info.MinefieldDepth)
|
||||
.Where(c => movement.CanEnterCell(c, null, BlockedByActor.Immovable) || self.World.FogObscures(c))
|
||||
.Where(c => movement.CanEnterCell(c, null, BlockedByActor.Immovable) || !self.Owner.Shroud.IsVisible(c))
|
||||
.OrderBy(c => (c - minefieldStart).LengthSquared).ToList();
|
||||
|
||||
self.QueueActivity(order.Queued, new LayMines(self, minefield));
|
||||
@@ -231,13 +231,14 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
minelayers.Max(m => m.Info.TraitInfo<MinelayerInfo>().MinefieldDepth));
|
||||
|
||||
var movement = minelayer.Trait<IPositionable>();
|
||||
var mobile = movement as Mobile;
|
||||
var pal = wr.Palette(TileSet.TerrainPaletteInternalName);
|
||||
foreach (var c in minefield)
|
||||
{
|
||||
var tile = tileOk;
|
||||
if (world.FogObscures(c))
|
||||
tile = tileUnknown;
|
||||
else if (!movement.CanEnterCell(c, null, BlockedByActor.Immovable))
|
||||
else if (!movement.CanEnterCell(c, null, BlockedByActor.Immovable) || (mobile != null && !mobile.CanStayInCell(c)))
|
||||
tile = tileBlocked;
|
||||
|
||||
yield return new SpriteRenderable(tile, world.Map.CenterOfCell(c),
|
||||
|
||||
@@ -85,10 +85,13 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
||||
static string[] overlayActors = new string[]
|
||||
{
|
||||
// Fences
|
||||
"sbag", "cycl", "brik", "fenc", "wood", "wood",
|
||||
"sbag", "cycl", "brik", "fenc", "wood",
|
||||
|
||||
// Fields
|
||||
"v12", "v13", "v14", "v15", "v16", "v17", "v18"
|
||||
"v12", "v13", "v14", "v15", "v16", "v17", "v18",
|
||||
|
||||
// Crates
|
||||
"wcrate", "scrate"
|
||||
};
|
||||
|
||||
void UnpackOverlayData(MemoryStream ms)
|
||||
|
||||
@@ -73,10 +73,13 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
||||
static string[] overlayActors = new string[]
|
||||
{
|
||||
// Fences
|
||||
"sbag", "cycl", "brik", "fenc", "wood", "wood",
|
||||
"sbag", "cycl", "brik", "fenc", "wood",
|
||||
|
||||
// Fields
|
||||
"v12", "v13", "v14", "v15", "v16", "v17", "v18"
|
||||
"v12", "v13", "v14", "v15", "v16", "v17", "v18",
|
||||
|
||||
// Crates
|
||||
"wcrate", "scrate"
|
||||
};
|
||||
|
||||
void ReadOverlay(IniFile file)
|
||||
@@ -137,7 +140,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
||||
faction = "gdi";
|
||||
break;
|
||||
case "BadGuy":
|
||||
color = "red"; // TODO: use the grey unit color theme for missions
|
||||
color = "red";
|
||||
faction = "nod";
|
||||
break;
|
||||
case "Special":
|
||||
|
||||
@@ -305,7 +305,7 @@ namespace OpenRA.Mods.Common.Activities
|
||||
var newCell = path[path.Count - 1];
|
||||
path.RemoveAt(path.Count - 1);
|
||||
|
||||
return Pair.New(newCell, mobile.GetAvailableSubCell(nextCell, SubCell.Any, ignoreActor));
|
||||
return Pair.New(newCell, mobile.GetAvailableSubCell(nextCell, mobile.FromSubCell, ignoreActor));
|
||||
}
|
||||
else if (mobile.IsBlocking)
|
||||
{
|
||||
@@ -316,7 +316,7 @@ namespace OpenRA.Mods.Common.Activities
|
||||
if ((nextCell - newCell).Value.LengthSquared > 2)
|
||||
path.Add(mobile.ToCell);
|
||||
|
||||
return Pair.New(newCell.Value, mobile.GetAvailableSubCell(newCell.Value, SubCell.Any, ignoreActor));
|
||||
return Pair.New(newCell.Value, mobile.GetAvailableSubCell(newCell.Value, mobile.FromSubCell, ignoreActor));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ namespace OpenRA.Mods.Common.Activities
|
||||
hasWaited = false;
|
||||
path.RemoveAt(path.Count - 1);
|
||||
|
||||
return Pair.New(nextCell, mobile.GetAvailableSubCell(nextCell, SubCell.Any, ignoreActor));
|
||||
return Pair.New(nextCell, mobile.GetAvailableSubCell(nextCell, mobile.FromSubCell, ignoreActor));
|
||||
}
|
||||
|
||||
protected override void OnLastRun(Actor self)
|
||||
@@ -347,10 +347,15 @@ namespace OpenRA.Mods.Common.Activities
|
||||
}
|
||||
|
||||
public override void Cancel(Actor self, bool keepQueue = false)
|
||||
{
|
||||
Cancel(self, keepQueue, false);
|
||||
}
|
||||
|
||||
public void Cancel(Actor self, bool keepQueue, bool forceClearPath)
|
||||
{
|
||||
// We need to clear the path here in order to prevent MovePart queueing new instances of itself
|
||||
// when the unit is making a turn.
|
||||
if (path != null && mobile.CanStayInCell(mobile.ToCell))
|
||||
if (path != null && (forceClearPath || mobile.CanStayInCell(mobile.ToCell)))
|
||||
path.Clear();
|
||||
|
||||
base.Cancel(self, keepQueue);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Activities;
|
||||
@@ -77,7 +78,8 @@ namespace OpenRA.Mods.Common.Activities
|
||||
|
||||
protected virtual IEnumerable<CPos> CandidateMovementCells(Actor self)
|
||||
{
|
||||
return Util.AdjacentCells(self.World, Target);
|
||||
return Util.AdjacentCells(self.World, Target)
|
||||
.Where(c => Mobile.CanStayInCell(c));
|
||||
}
|
||||
|
||||
protected override void OnFirstRun(Actor self)
|
||||
@@ -119,15 +121,23 @@ namespace OpenRA.Mods.Common.Activities
|
||||
return TickChild(self);
|
||||
}
|
||||
|
||||
List<CPos> searchCells = new List<CPos>();
|
||||
int searchCellsTick = -1;
|
||||
|
||||
List<CPos> CalculatePathToTarget(Actor self, BlockedByActor check)
|
||||
{
|
||||
var targetCells = CandidateMovementCells(self);
|
||||
var searchCells = new List<CPos>();
|
||||
var loc = self.Location;
|
||||
|
||||
foreach (var cell in targetCells)
|
||||
if (domainIndex.IsPassable(loc, cell, Mobile.Info.LocomotorInfo) && Mobile.CanEnterCell(cell))
|
||||
searchCells.Add(cell);
|
||||
// PERF: Assume that CandidateMovementCells doesn't change within a tick to avoid repeated queries
|
||||
// when Move enumerates different BlockedByActor values
|
||||
if (searchCellsTick != self.World.WorldTick)
|
||||
{
|
||||
searchCells.Clear();
|
||||
searchCellsTick = self.World.WorldTick;
|
||||
foreach (var cell in CandidateMovementCells(self))
|
||||
if (domainIndex.IsPassable(loc, cell, Mobile.Info.LocomotorInfo) && Mobile.CanEnterCell(cell))
|
||||
searchCells.Add(cell);
|
||||
}
|
||||
|
||||
if (!searchCells.Any())
|
||||
return NoPath;
|
||||
|
||||
@@ -33,13 +33,13 @@ namespace OpenRA.Mods.Common.Activities
|
||||
{
|
||||
// We are now in range. Don't move any further!
|
||||
// HACK: This works around the pathfinder not returning the shortest path
|
||||
return AtCorrectRange(self.CenterPosition) && Mobile.CanInteractWithGroundLayer(self);
|
||||
return AtCorrectRange(self.CenterPosition) && Mobile.CanInteractWithGroundLayer(self) && Mobile.CanStayInCell(self.Location);
|
||||
}
|
||||
|
||||
protected override bool ShouldRepath(Actor self, CPos targetLocation)
|
||||
{
|
||||
return lastVisibleTargetLocation != targetLocation && (!AtCorrectRange(self.CenterPosition)
|
||||
|| !Mobile.CanInteractWithGroundLayer(self));
|
||||
|| !Mobile.CanInteractWithGroundLayer(self) || !Mobile.CanStayInCell(self.Location));
|
||||
}
|
||||
|
||||
protected override IEnumerable<CPos> CandidateMovementCells(Actor self)
|
||||
@@ -49,7 +49,7 @@ namespace OpenRA.Mods.Common.Activities
|
||||
var minCells = minRange.Length / 1024;
|
||||
|
||||
return map.FindTilesInAnnulus(lastVisibleTargetLocation, minCells, maxCells)
|
||||
.Where(c => AtCorrectRange(map.CenterOfSubCell(c, Mobile.FromSubCell)));
|
||||
.Where(c => Mobile.CanStayInCell(c) && AtCorrectRange(map.CenterOfSubCell(c, Mobile.FromSubCell)));
|
||||
}
|
||||
|
||||
bool AtCorrectRange(WPos origin)
|
||||
|
||||
@@ -165,6 +165,12 @@ namespace OpenRA.Mods.Common.Activities
|
||||
|
||||
public override void Cancel(Actor self, bool keepQueue = false)
|
||||
{
|
||||
// HACK: force move activities to ignore the transit-only cells when cancelling
|
||||
// The idle handler will take over and move them into a safe cell
|
||||
if (ChildActivity != null)
|
||||
foreach (var c in ChildActivity.ActivitiesImplementing<Move>())
|
||||
c.Cancel(self, false, true);
|
||||
|
||||
foreach (var t in transportCallers)
|
||||
t.MovementCancelled(self);
|
||||
|
||||
|
||||
@@ -62,16 +62,18 @@ namespace OpenRA.Mods.Common.Effects
|
||||
world.ScreenMap.Add(this, pos, anim.Image);
|
||||
initialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
anim.Tick();
|
||||
|
||||
anim.Tick();
|
||||
|
||||
pos = posFunc();
|
||||
world.ScreenMap.Update(this, pos, anim.Image);
|
||||
pos = posFunc();
|
||||
world.ScreenMap.Update(this, pos, anim.Image);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<IRenderable> Render(WorldRenderer wr)
|
||||
{
|
||||
if (!visibleThroughFog && world.FogObscures(pos))
|
||||
if (!initialized || (!visibleThroughFog && world.FogObscures(pos)))
|
||||
return SpriteRenderable.None;
|
||||
|
||||
return anim.Render(pos, wr.Palette(palette));
|
||||
|
||||
@@ -66,18 +66,17 @@ namespace OpenRA.Mods.Common.Graphics
|
||||
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
|
||||
public void Render(WorldRenderer wr)
|
||||
{
|
||||
var wcr = Game.Renderer.WorldRgbaColorRenderer;
|
||||
var center = wr.Screen3DPosition(centerPosition);
|
||||
var cr = Game.Renderer.RgbaColorRenderer;
|
||||
var center = wr.Viewport.WorldToViewPx(wr.Screen3DPosition(centerPosition));
|
||||
|
||||
for (var i = 0; i < trailCount; i++)
|
||||
{
|
||||
var angle = trailAngle - new WAngle(i * (trailSeparation.Angle <= 512 ? 1 : -1));
|
||||
var length = radius.Length * new WVec(angle.Cos(), angle.Sin(), 0) / 1024;
|
||||
var end = wr.Screen3DPosition(centerPosition + length);
|
||||
var end = wr.Viewport.WorldToViewPx(wr.Screen3DPosition(centerPosition + length));
|
||||
var alpha = color.A - i * color.A / trailCount;
|
||||
|
||||
wcr.DrawLine(center, end, 3, Color.FromArgb(alpha, contrastColor));
|
||||
wcr.DrawLine(center, end, 1, Color.FromArgb(alpha, color));
|
||||
cr.DrawLine(center, end, 3, Color.FromArgb(alpha, contrastColor));
|
||||
cr.DrawLine(center, end, 1, Color.FromArgb(alpha, color));
|
||||
}
|
||||
|
||||
RangeCircleAnnotationRenderable.DrawRangeCircle(wr, centerPosition, radius, 1, color, 3, contrastColor);
|
||||
|
||||
@@ -122,6 +122,7 @@ namespace OpenRA.Mods.Common.LoadScreens
|
||||
}
|
||||
|
||||
// Saved settings may have been invalidated by a hardware change
|
||||
Game.Settings.Graphics.GLProfile = Game.Renderer.GLProfile;
|
||||
Game.Settings.Graphics.VideoDisplay = Game.Renderer.CurrentDisplay;
|
||||
|
||||
// If a ModContent section is defined then we need to make sure that the
|
||||
|
||||
@@ -398,8 +398,17 @@ namespace OpenRA.Mods.Common.Server
|
||||
LoadMapSettings(server, server.LobbyInfo.GlobalSettings, server.Map.Rules);
|
||||
|
||||
// Reset client states
|
||||
var selectableFactions = server.Map.Rules.Actors["world"].TraitInfos<FactionInfo>()
|
||||
.Where(f => f.Selectable)
|
||||
.Select(f => f.InternalName)
|
||||
.ToList();
|
||||
|
||||
foreach (var c in server.LobbyInfo.Clients)
|
||||
{
|
||||
c.State = Session.ClientState.Invalid;
|
||||
if (!selectableFactions.Contains(c.Faction))
|
||||
c.Faction = "Random";
|
||||
}
|
||||
|
||||
// Reassign players into new slots based on their old slots:
|
||||
// - Observers remain as observers
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
if (reservedForAircraft.GetActorBelow() == self)
|
||||
{
|
||||
if (rallyPoint != null)
|
||||
if (rallyPoint != null && rallyPoint.Path.Count > 0)
|
||||
foreach (var cell in rallyPoint.Path)
|
||||
reservedFor.QueueActivity(reservedForAircraft.MoveTo(cell, 1, targetLineColor: Color.Green));
|
||||
else
|
||||
|
||||
@@ -35,9 +35,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
ConditionManager conditionManager;
|
||||
int conditionToken = ConditionManager.InvalidConditionToken;
|
||||
|
||||
string cachedTerrain;
|
||||
CPos cachedLocation;
|
||||
|
||||
public GrantConditionOnTerrain(ActorInitializer init, GrantConditionOnTerrainInfo info)
|
||||
{
|
||||
@@ -53,9 +51,10 @@ namespace OpenRA.Mods.Common.Traits
|
||||
void ITick.Tick(Actor self)
|
||||
{
|
||||
var loc = self.Location;
|
||||
if (conditionManager == null || loc == cachedLocation)
|
||||
if (conditionManager == null)
|
||||
return;
|
||||
|
||||
// The terrain type may change between ticks without the actor moving
|
||||
var currentTerrain = loc.Layer == 0 ? self.World.Map.GetTerrainInfo(loc).Type :
|
||||
tileSet[self.World.GetCustomMovementLayers()[loc.Layer].GetTerrainIndex(loc)].Type;
|
||||
|
||||
@@ -69,7 +68,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
|
||||
cachedTerrain = currentTerrain;
|
||||
cachedLocation = loc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -866,7 +866,13 @@ namespace OpenRA.Mods.Common.Traits
|
||||
void INotifyBecomingIdle.OnBecomingIdle(Actor self)
|
||||
{
|
||||
if (self.Location.Layer == 0)
|
||||
{
|
||||
// Make sure that units aren't left idling in a transit-only cell
|
||||
// HACK: activities should be making sure that this can't happen in the first place!
|
||||
if (!Locomotor.CanStayInCell(self.Location))
|
||||
self.QueueActivity(MoveTo(self.Location, evaluateNearestMovableCell: true));
|
||||
return;
|
||||
}
|
||||
|
||||
var cml = self.World.WorldActor.TraitsImplementing<ICustomMovementLayer>()
|
||||
.First(l => l.Index == self.Location.Layer);
|
||||
|
||||
@@ -276,7 +276,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public SubCell FreeSubCell(CPos cell, SubCell preferredSubCell = SubCell.Any, bool checkTransient = true)
|
||||
{
|
||||
if (preferredSubCell > SubCell.Any && !AnyActorsAt(cell, preferredSubCell, checkTransient))
|
||||
if (preferredSubCell != SubCell.Any && !AnyActorsAt(cell, preferredSubCell, checkTransient))
|
||||
return preferredSubCell;
|
||||
|
||||
if (!AnyActorsAt(cell))
|
||||
@@ -291,7 +291,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public SubCell FreeSubCell(CPos cell, SubCell preferredSubCell, Func<Actor, bool> checkIfBlocker)
|
||||
{
|
||||
if (preferredSubCell > SubCell.Any && !AnyActorsAt(cell, preferredSubCell, checkIfBlocker))
|
||||
if (preferredSubCell != SubCell.Any && !AnyActorsAt(cell, preferredSubCell, checkIfBlocker))
|
||||
return preferredSubCell;
|
||||
|
||||
if (!AnyActorsAt(cell))
|
||||
|
||||
@@ -133,12 +133,13 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (!preview.Bounds.IsEmpty)
|
||||
screenMap.Add(preview, preview.Bounds);
|
||||
|
||||
foreach (var kv in preview.Footprint)
|
||||
AddPreviewLocation(preview, kv.Key);
|
||||
|
||||
// Fallback to the actor's CenterPosition for the ActorMap if it has no Footprint
|
||||
if (!preview.Footprint.Any())
|
||||
AddPreviewLocation(preview, worldRenderer.World.Map.CellContaining(preview.CenterPosition));
|
||||
var footprint = preview.Footprint.Select(kv => kv.Key).ToArray();
|
||||
if (!footprint.Any())
|
||||
footprint = new[] { worldRenderer.World.Map.CellContaining(preview.CenterPosition) };
|
||||
|
||||
foreach (var cell in footprint)
|
||||
AddPreviewLocation(preview, cell);
|
||||
|
||||
if (!initialSetup)
|
||||
{
|
||||
@@ -154,16 +155,21 @@ namespace OpenRA.Mods.Common.Traits
|
||||
previews.Remove(preview);
|
||||
screenMap.Remove(preview);
|
||||
|
||||
foreach (var kv in preview.Footprint)
|
||||
// Fallback to the actor's CenterPosition for the ActorMap if it has no Footprint
|
||||
var footprint = preview.Footprint.Select(kv => kv.Key).ToArray();
|
||||
if (!footprint.Any())
|
||||
footprint = new[] { worldRenderer.World.Map.CellContaining(preview.CenterPosition) };
|
||||
|
||||
foreach (var cell in footprint)
|
||||
{
|
||||
List<EditorActorPreview> list;
|
||||
if (!cellMap.TryGetValue(kv.Key, out list))
|
||||
if (!cellMap.TryGetValue(cell, out list))
|
||||
continue;
|
||||
|
||||
list.Remove(preview);
|
||||
|
||||
if (!list.Any())
|
||||
cellMap.Remove(kv.Key);
|
||||
cellMap.Remove(cell);
|
||||
}
|
||||
|
||||
UpdateNeighbours(preview.Footprint);
|
||||
|
||||
@@ -304,7 +304,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
var otherActors = subCell == SubCell.FullCell ? world.ActorMap.GetActorsAt(cell) : world.ActorMap.GetActorsAt(cell, subCell);
|
||||
foreach (var otherActor in otherActors)
|
||||
if (IsBlockedBy(actor, otherActor, ignoreActor, check, cellFlag))
|
||||
if (IsBlockedBy(actor, otherActor, ignoreActor, cell, check, cellFlag))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -322,7 +322,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
if (check > BlockedByActor.None)
|
||||
{
|
||||
Func<Actor, bool> checkTransient = otherActor => IsBlockedBy(self, otherActor, ignoreActor, check, GetCache(cell).CellFlag);
|
||||
Func<Actor, bool> checkTransient = otherActor => IsBlockedBy(self, otherActor, ignoreActor, cell, check, GetCache(cell).CellFlag);
|
||||
|
||||
if (!sharesCell)
|
||||
return world.ActorMap.AnyActorsAt(cell, SubCell.FullCell, checkTransient) ? SubCell.Invalid : SubCell.FullCell;
|
||||
@@ -336,14 +336,14 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return world.ActorMap.FreeSubCell(cell, preferredSubCell);
|
||||
}
|
||||
|
||||
bool IsBlockedBy(Actor self, Actor otherActor, Actor ignoreActor, BlockedByActor check, CellFlag cellFlag)
|
||||
bool IsBlockedBy(Actor actor, Actor otherActor, Actor ignoreActor, CPos cell, BlockedByActor check, CellFlag cellFlag)
|
||||
{
|
||||
if (otherActor == ignoreActor)
|
||||
return false;
|
||||
|
||||
// If the check allows: We are not blocked by units that we can force to move out of the way.
|
||||
if (check <= BlockedByActor.Immovable && cellFlag.HasCellFlag(CellFlag.HasMovableActor) &&
|
||||
self.Owner.Stances[otherActor.Owner] == Stance.Ally)
|
||||
actor.Owner.Stances[otherActor.Owner] == Stance.Ally)
|
||||
{
|
||||
var mobile = otherActor.TraitOrDefault<Mobile>();
|
||||
if (mobile != null && !mobile.IsTraitDisabled && !mobile.IsTraitPaused && !mobile.IsImmovable)
|
||||
@@ -352,14 +352,22 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
// If the check allows: we are not blocked by moving units.
|
||||
if (check <= BlockedByActor.Stationary && cellFlag.HasCellFlag(CellFlag.HasMovingActor) &&
|
||||
IsMoving(self, otherActor))
|
||||
IsMoving(actor, otherActor))
|
||||
return false;
|
||||
|
||||
if (cellFlag.HasCellFlag(CellFlag.HasTemporaryBlocker))
|
||||
{
|
||||
// If there is a temporary blocker in our path, but we can remove it, we are not blocked.
|
||||
var temporaryBlocker = otherActor.TraitOrDefault<ITemporaryBlocker>();
|
||||
if (temporaryBlocker != null && temporaryBlocker.CanRemoveBlockage(otherActor, self))
|
||||
if (temporaryBlocker != null && temporaryBlocker.CanRemoveBlockage(otherActor, actor))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cellFlag.HasCellFlag(CellFlag.HasTransitOnlyActor))
|
||||
{
|
||||
// Transit only tiles should not block movement
|
||||
var building = otherActor.TraitOrDefault<Building>();
|
||||
if (building != null && building.TransitOnlyCells().Contains(cell))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -371,7 +379,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
// PERF: Avoid LINQ.
|
||||
var crushables = otherActor.TraitsImplementing<ICrushable>();
|
||||
foreach (var crushable in crushables)
|
||||
if (crushable.CrushableBy(otherActor, self, Info.Crushes))
|
||||
if (crushable.CrushableBy(otherActor, actor, Info.Crushes))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
@@ -70,245 +71,178 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public object Create(ActorInitializer init) { return new WeatherOverlay(init.World, this); }
|
||||
}
|
||||
|
||||
public class WeatherOverlay : ITick, IRenderAboveWorld
|
||||
public class WeatherOverlay : ITick, IRenderAboveWorld, INotifyViewportZoomExtentsChanged
|
||||
{
|
||||
readonly WeatherOverlayInfo info;
|
||||
readonly World world;
|
||||
struct Particle
|
||||
{
|
||||
public float PosX;
|
||||
public float PosY;
|
||||
public int Size;
|
||||
public float DirectionScatterX;
|
||||
public float Gravity;
|
||||
public float SwingOffset;
|
||||
public float SwingSpeed;
|
||||
public int SwingDirection;
|
||||
public float SwingAmplitude;
|
||||
public Color Color;
|
||||
public Color TailColor;
|
||||
public readonly float2 Pos;
|
||||
public readonly int Size;
|
||||
public readonly float DirectionScatterX;
|
||||
public readonly float Gravity;
|
||||
public readonly float SwingOffset;
|
||||
public readonly float SwingSpeed;
|
||||
public readonly int SwingDirection;
|
||||
public readonly float SwingAmplitude;
|
||||
public readonly Color Color;
|
||||
public readonly Color TailColor;
|
||||
|
||||
public Particle(WeatherOverlayInfo info, MersenneTwister r, Rectangle viewport)
|
||||
{
|
||||
var x = r.Next(viewport.Left, viewport.Right);
|
||||
var y = r.Next(viewport.Top, viewport.Bottom);
|
||||
|
||||
Pos = new int2(x, y);
|
||||
Size = r.Next(info.ParticleSize[0], info.ParticleSize[1] + 1);
|
||||
DirectionScatterX = info.ScatterDirection[0] + r.Next(info.ScatterDirection[1] - info.ScatterDirection[0]);
|
||||
Gravity = float2.Lerp(info.Gravity[0], info.Gravity[1], r.NextFloat());
|
||||
SwingOffset = float2.Lerp(info.SwingOffset[0], info.SwingOffset[1], r.NextFloat());
|
||||
SwingSpeed = float2.Lerp(info.SwingSpeed[0], info.SwingSpeed[1], r.NextFloat());
|
||||
SwingDirection = r.Next(2) == 0 ? 1 : -1;
|
||||
SwingAmplitude = float2.Lerp(info.SwingAmplitude[0], info.SwingAmplitude[1], r.NextFloat());
|
||||
Color = info.ParticleColors.Random(r);
|
||||
TailColor = Color.FromArgb(info.LineTailAlphaValue, Color.R, Color.G, Color.B);
|
||||
}
|
||||
|
||||
Particle(Particle source)
|
||||
{
|
||||
Pos = source.Pos;
|
||||
Size = source.Size;
|
||||
DirectionScatterX = source.DirectionScatterX;
|
||||
Gravity = source.Gravity;
|
||||
SwingOffset = source.SwingOffset;
|
||||
SwingSpeed = source.SwingSpeed;
|
||||
SwingDirection = source.SwingDirection;
|
||||
SwingAmplitude = source.SwingAmplitude;
|
||||
Color = source.Color;
|
||||
TailColor = source.TailColor;
|
||||
}
|
||||
|
||||
public Particle(Particle source, float2 pos)
|
||||
: this(source)
|
||||
{
|
||||
Pos = pos;
|
||||
}
|
||||
|
||||
public Particle(Particle source, float2 pos, int swingDirection, float swingOffset)
|
||||
: this(source)
|
||||
{
|
||||
Pos = pos;
|
||||
SwingDirection = swingDirection;
|
||||
SwingOffset = swingOffset;
|
||||
}
|
||||
}
|
||||
|
||||
readonly List<Particle> particleList = new List<Particle>();
|
||||
readonly int maxParticleCount;
|
||||
readonly WeatherOverlayInfo info;
|
||||
readonly World world;
|
||||
|
||||
enum ParticleCountFaderType { Hold, FadeIn, FadeOut }
|
||||
ParticleCountFaderType particleCountFader = ParticleCountFaderType.FadeIn;
|
||||
|
||||
float targetWindXOffset = 0f;
|
||||
float currentWindXOffset = 0f;
|
||||
int currentWindIndex = 0;
|
||||
long windTickCountdown = 1500;
|
||||
float2 antiScrollPrevTopLeft;
|
||||
float windStrength;
|
||||
int targetWindStrengthIndex;
|
||||
long windUpdateCountdown;
|
||||
Particle[] particles;
|
||||
Size viewportSize;
|
||||
|
||||
public WeatherOverlay(World world, WeatherOverlayInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
this.world = world;
|
||||
currentWindIndex = info.WindLevels.Length / 2;
|
||||
targetWindXOffset = info.WindLevels[0];
|
||||
maxParticleCount = CalculateParticleCount(Game.Renderer.Resolution.Width, Game.Renderer.Resolution.Height);
|
||||
targetWindStrengthIndex = info.ChangingWindLevel ? world.LocalRandom.Next(info.WindLevels.Length) : 0;
|
||||
windUpdateCountdown = world.LocalRandom.Next(info.WindTick[0], info.WindTick[1]);
|
||||
windStrength = info.WindLevels[targetWindStrengthIndex];
|
||||
}
|
||||
|
||||
int CalculateParticleCount(int x, int y)
|
||||
void INotifyViewportZoomExtentsChanged.ViewportZoomExtentsChanged(float minZoom, float maxZoom)
|
||||
{
|
||||
return (int)(x * y * info.ParticleDensityFactor / 10000);
|
||||
}
|
||||
// Track particles in a viewport fixed to the minimum zoom level
|
||||
var s = (1f / minZoom * new float2(Game.Renderer.NativeResolution)).ToInt2();
|
||||
viewportSize = new Size(s.X, s.Y);
|
||||
|
||||
void SpawnParticles(int count, int rangeY, int spawnChancePercent)
|
||||
{
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if (Game.CosmeticRandom.Next(100) < spawnChancePercent)
|
||||
{
|
||||
var tempColor = info.ParticleColors.Random(Game.CosmeticRandom);
|
||||
var tempColorTail = Color.FromArgb(info.LineTailAlphaValue, tempColor.R, tempColor.G, tempColor.B);
|
||||
var tempSwingDirection = Game.CosmeticRandom.Next(2) == 0 ? 1 : -1;
|
||||
|
||||
particleList.Add(
|
||||
new Particle
|
||||
{
|
||||
PosX = Game.CosmeticRandom.Next(Game.Renderer.Resolution.Width),
|
||||
PosY = Game.CosmeticRandom.Next(rangeY),
|
||||
Size = Game.CosmeticRandom.Next(info.ParticleSize[0], info.ParticleSize[1] + 1),
|
||||
DirectionScatterX = info.ScatterDirection[0] + Game.CosmeticRandom.Next(info.ScatterDirection[1] - info.ScatterDirection[0]),
|
||||
Gravity = float2.Lerp(info.Gravity[0], info.Gravity[1], Game.CosmeticRandom.NextFloat()),
|
||||
SwingOffset = float2.Lerp(info.SwingOffset[0], info.SwingOffset[1], Game.CosmeticRandom.NextFloat()),
|
||||
SwingSpeed = float2.Lerp(info.SwingSpeed[0], info.SwingSpeed[1], Game.CosmeticRandom.NextFloat()),
|
||||
SwingDirection = tempSwingDirection,
|
||||
SwingAmplitude = float2.Lerp(info.SwingAmplitude[0], info.SwingAmplitude[1], Game.CosmeticRandom.NextFloat()),
|
||||
Color = tempColor,
|
||||
TailColor = tempColorTail
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParticlesCountLogic(WorldRenderer wr)
|
||||
{
|
||||
// Logic to switch between the states of the particleCountFader
|
||||
if (particleCountFader == ParticleCountFaderType.Hold && particleList.Count < maxParticleCount)
|
||||
particleCountFader = ParticleCountFaderType.FadeIn;
|
||||
else if (particleCountFader == ParticleCountFaderType.FadeIn && particleList.Count >= maxParticleCount)
|
||||
particleCountFader = ParticleCountFaderType.Hold;
|
||||
else if (particleCountFader == ParticleCountFaderType.FadeOut && particleList.Count == 0)
|
||||
particleCountFader = ParticleCountFaderType.Hold;
|
||||
|
||||
// Do the fade functions
|
||||
if (particleCountFader == ParticleCountFaderType.FadeIn)
|
||||
FadeInParticleCount(wr);
|
||||
else if (particleCountFader == ParticleCountFaderType.FadeOut)
|
||||
FadeOutParticleCount(wr);
|
||||
}
|
||||
|
||||
void FadeInParticleCount(WorldRenderer wr)
|
||||
{
|
||||
SpawnParticles(1, 0, 100);
|
||||
|
||||
// Remove Particles, which are getting replaced from the top to the bottom by the "EdgeCheckReplace",
|
||||
// when scrolling down, as long as the FadeIn is not completed,
|
||||
// to avoid having particles at the top and bottom, but not in the middle of the screen.
|
||||
for (var i = 0; i < particleList.Count; i++)
|
||||
if (particleList[i].PosY < 0)
|
||||
particleList.RemoveAt(i);
|
||||
|
||||
// Add Particles when the weather is fading in and scrolling up, to fill areas above
|
||||
if (antiScrollPrevTopLeft.Y > wr.Viewport.TopLeft.Y)
|
||||
{
|
||||
// Get delta Y and limit to the max value
|
||||
var tempRangeY = antiScrollPrevTopLeft.Y - wr.Viewport.TopLeft.Y;
|
||||
var tempParticleCount = CalculateParticleCount(Game.Renderer.Resolution.Width, (int)tempRangeY);
|
||||
if (particleList.Count + tempParticleCount > maxParticleCount)
|
||||
tempParticleCount = maxParticleCount - particleList.Count;
|
||||
|
||||
SpawnParticles(tempParticleCount, (int)tempRangeY, 50);
|
||||
}
|
||||
}
|
||||
|
||||
void FadeOutParticleCount(WorldRenderer wr)
|
||||
{
|
||||
for (var i = 0; i < particleList.Count; i++)
|
||||
if (particleList[i].PosY > (Game.Renderer.Resolution.Height - particleList[i].Gravity))
|
||||
particleList.RemoveAt(i);
|
||||
}
|
||||
|
||||
void XAxisSwing(ref Particle tempParticle)
|
||||
{
|
||||
// Direction turn
|
||||
if (tempParticle.SwingOffset < -tempParticle.SwingAmplitude || tempParticle.SwingOffset > tempParticle.SwingAmplitude)
|
||||
tempParticle.SwingDirection *= -1;
|
||||
|
||||
// Perform the X-Axis-Swing
|
||||
tempParticle.SwingOffset += tempParticle.SwingDirection * tempParticle.SwingSpeed;
|
||||
// Randomly distribute particles within the initial viewport
|
||||
var particleCount = viewportSize.Width * viewportSize.Height * info.ParticleDensityFactor / 10000;
|
||||
particles = new Particle[particleCount];
|
||||
var rect = new Rectangle(int2.Zero, viewportSize);
|
||||
for (var i = 0; i < particles.Length; i++)
|
||||
particles[i] = new Particle(info, world.LocalRandom, rect);
|
||||
}
|
||||
|
||||
void ITick.Tick(Actor self)
|
||||
{
|
||||
windTickCountdown--;
|
||||
}
|
||||
if (!info.ChangingWindLevel || info.WindLevels.Length == 1)
|
||||
return;
|
||||
|
||||
void WindLogic(ref Particle tempParticle)
|
||||
{
|
||||
if (!info.ChangingWindLevel)
|
||||
targetWindXOffset = info.WindLevels[0];
|
||||
else if (windTickCountdown <= 0)
|
||||
if (--windUpdateCountdown <= 0)
|
||||
{
|
||||
windTickCountdown = Game.CosmeticRandom.Next(info.WindTick[0], info.WindTick[1]);
|
||||
if (Game.CosmeticRandom.Next(2) == 1 && currentWindIndex > 0)
|
||||
{
|
||||
currentWindIndex--;
|
||||
targetWindXOffset = info.WindLevels[currentWindIndex];
|
||||
}
|
||||
else if (currentWindIndex < info.WindLevels.Length - 1)
|
||||
{
|
||||
currentWindIndex++;
|
||||
targetWindXOffset = info.WindLevels[currentWindIndex];
|
||||
}
|
||||
windUpdateCountdown = self.World.LocalRandom.Next(info.WindTick[0], info.WindTick[1]);
|
||||
if (targetWindStrengthIndex > 0 && self.World.LocalRandom.Next(2) == 1)
|
||||
targetWindStrengthIndex--;
|
||||
else if (targetWindStrengthIndex < info.WindLevels.Length - 1)
|
||||
targetWindStrengthIndex++;
|
||||
}
|
||||
|
||||
// Fading the wind in little steps towards the TargetWindOffset
|
||||
var targetWindLevel = info.WindLevels[targetWindStrengthIndex];
|
||||
if (info.InstantWindChanges)
|
||||
currentWindXOffset = targetWindXOffset;
|
||||
else if (currentWindXOffset != targetWindXOffset)
|
||||
windStrength = targetWindLevel;
|
||||
else if (Math.Abs(windStrength - targetWindLevel) > 0.01f)
|
||||
{
|
||||
if (currentWindXOffset > targetWindXOffset)
|
||||
currentWindXOffset -= 0.00001f;
|
||||
else if (currentWindXOffset < targetWindXOffset)
|
||||
currentWindXOffset += 0.00001f;
|
||||
}
|
||||
}
|
||||
|
||||
void Movement(ref Particle tempParticle)
|
||||
{
|
||||
tempParticle.PosX += tempParticle.DirectionScatterX + tempParticle.SwingOffset + currentWindXOffset;
|
||||
tempParticle.PosY += tempParticle.Gravity;
|
||||
}
|
||||
|
||||
// AntiScroll keeps the particles in place when scrolling the viewport
|
||||
void AntiScroll(ref Particle tempParticle, WorldRenderer wr)
|
||||
{
|
||||
tempParticle.PosX += antiScrollPrevTopLeft.X - wr.Viewport.TopLeft.X;
|
||||
tempParticle.PosY += antiScrollPrevTopLeft.Y - wr.Viewport.TopLeft.Y;
|
||||
}
|
||||
|
||||
void EdgeCheckReplace(ref Particle tempParticle, WorldRenderer wr)
|
||||
{
|
||||
tempParticle.PosX %= Game.Renderer.Resolution.Width;
|
||||
if (tempParticle.PosX < 0)
|
||||
tempParticle.PosX += Game.Renderer.Resolution.Width;
|
||||
|
||||
tempParticle.PosY %= Game.Renderer.Resolution.Height;
|
||||
if (tempParticle.PosY < 0 && particleCountFader != ParticleCountFaderType.FadeIn)
|
||||
tempParticle.PosY += Game.Renderer.Resolution.Height;
|
||||
}
|
||||
|
||||
void UpdateWeatherOverlay(WorldRenderer wr)
|
||||
{
|
||||
if (!world.Paused)
|
||||
ParticlesCountLogic(wr);
|
||||
|
||||
for (var i = 0; i < particleList.Count; i++)
|
||||
{
|
||||
Particle tempParticle = particleList[i];
|
||||
|
||||
if (!world.Paused)
|
||||
{
|
||||
XAxisSwing(ref tempParticle);
|
||||
WindLogic(ref tempParticle);
|
||||
Movement(ref tempParticle);
|
||||
}
|
||||
|
||||
AntiScroll(ref tempParticle, wr);
|
||||
EdgeCheckReplace(ref tempParticle, wr);
|
||||
|
||||
particleList[i] = tempParticle;
|
||||
}
|
||||
|
||||
antiScrollPrevTopLeft = wr.Viewport.TopLeft;
|
||||
}
|
||||
|
||||
void DrawWeatherOverlay(WorldRenderer wr)
|
||||
{
|
||||
var topLeft = wr.Viewport.TopLeft;
|
||||
foreach (var item in particleList)
|
||||
{
|
||||
var tempPos = new float2(item.PosX + topLeft.X, item.PosY + topLeft.Y);
|
||||
|
||||
if (info.UseSquares)
|
||||
Game.Renderer.WorldRgbaColorRenderer.FillRect(tempPos, tempPos + new float2(item.Size, item.Size), item.Color);
|
||||
else
|
||||
{
|
||||
var tempPosTail = new float2(topLeft.X + item.PosX - currentWindXOffset, item.PosY - (item.Gravity * 2 / 3) + topLeft.Y);
|
||||
Game.Renderer.WorldRgbaColorRenderer.DrawLine(tempPos, tempPosTail, item.Size, item.TailColor);
|
||||
}
|
||||
if (windStrength > targetWindLevel)
|
||||
windStrength -= 0.01f;
|
||||
else if (windStrength < targetWindLevel)
|
||||
windStrength += 0.01f;
|
||||
}
|
||||
}
|
||||
|
||||
void IRenderAboveWorld.RenderAboveWorld(Actor self, WorldRenderer wr)
|
||||
{
|
||||
UpdateWeatherOverlay(wr);
|
||||
var center = wr.Viewport.CenterLocation;
|
||||
var viewport = new Rectangle(center - new int2(viewportSize) / 2, viewportSize);
|
||||
var wcr = Game.Renderer.WorldRgbaColorRenderer;
|
||||
|
||||
DrawWeatherOverlay(wr);
|
||||
for (var i = 0; i < particles.Length; i++)
|
||||
{
|
||||
// Simulate wind and gravity effects on the particle
|
||||
var p = particles[i];
|
||||
if (!world.Paused)
|
||||
{
|
||||
var swingDirection = p.SwingDirection;
|
||||
if (p.SwingOffset < -p.SwingAmplitude || p.SwingOffset > p.SwingAmplitude)
|
||||
swingDirection *= -1;
|
||||
|
||||
var swingOffset = p.SwingOffset + p.SwingDirection * p.SwingSpeed;
|
||||
var pos = p.Pos + new float2(p.DirectionScatterX + p.SwingOffset + windStrength, p.Gravity);
|
||||
particles[i] = p = new Particle(p, pos, swingDirection, swingOffset);
|
||||
}
|
||||
|
||||
// Move the particle back inside the viewport if necessary
|
||||
if (!viewport.Contains(p.Pos.ToInt2()))
|
||||
{
|
||||
var dx = (p.Pos.X - viewport.Left) % viewport.Width;
|
||||
var dy = (p.Pos.Y - viewport.Top) % viewport.Height;
|
||||
|
||||
if (dx < 0)
|
||||
dx += viewport.Width;
|
||||
|
||||
if (dy < 0)
|
||||
dy += viewport.Height;
|
||||
|
||||
particles[i] = p = new Particle(p, new float2(viewport.Left + dx, viewport.Top + dy));
|
||||
}
|
||||
|
||||
// Render the particle
|
||||
// We must provide a z coordinate to stop the GL near and far Z limits from culling the geometry
|
||||
var a = new float3(p.Pos.X, p.Pos.Y, p.Pos.Y);
|
||||
if (info.UseSquares)
|
||||
{
|
||||
var b = a + new float2(p.Size, p.Size);
|
||||
wcr.FillRect(a, b, p.Color);
|
||||
}
|
||||
else
|
||||
{
|
||||
var tail = p.Pos + new float2(-windStrength, -p.Gravity * 2 / 3);
|
||||
|
||||
var b = new float3(tail.X, tail.Y, tail.Y);
|
||||
wcr.DrawLine(a, b, p.Size, p.TailColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,14 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
Console.WriteLine("All settings can be changed by starting the game via a command-line parameter like `Game.Mod=ra`.");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("## Location");
|
||||
Console.WriteLine("* Windows: `My Documents\\OpenRA\\settings.yaml`");
|
||||
Console.WriteLine("* Windows: `%APPDATA%\\OpenRA\\settings.yaml`");
|
||||
Console.WriteLine("* Mac OS X: `~/Library/Application Support/OpenRA/settings.yaml`");
|
||||
Console.WriteLine("* Linux `~/.config/openra/settings.yaml`");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(
|
||||
"Older releases (before playtest-20190825) used different locations, " +
|
||||
"which newer versions may continue to use in some circumstances:");
|
||||
Console.WriteLine("* Windows: `%USERPROFILE%\\Documents\\OpenRA\\settings.yaml`");
|
||||
Console.WriteLine("* Linux `~/.openra/settings.yaml`");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(
|
||||
|
||||
@@ -72,7 +72,14 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
mixer.SetPaletteRange(validator.HsvSaturationRange[0], validator.HsvSaturationRange[1], validator.HsvValueRange[0], validator.HsvValueRange[1]);
|
||||
mixer.Set(initialColor);
|
||||
hueSlider.Value = HueFromColor(initialColor);
|
||||
onChange(mixer.Color);
|
||||
|
||||
// HACK: the value returned from the color mixer will generally not
|
||||
// be equal to the given initialColor due to its internal RGB -> HSL -> RGB
|
||||
// conversion. This conversion can sometimes convert a valid initial value
|
||||
// into an invalid (too close to terrain / another player) color.
|
||||
// We use the original colour here instead of the mixer color to make sure
|
||||
// that we keep the player's previous colour value if they don't change anything
|
||||
onChange(initialColor);
|
||||
|
||||
// Setup tab controls
|
||||
var mixerTab = widget.Get("MIXER_TAB");
|
||||
|
||||
@@ -77,12 +77,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
var mouseControlDescClassic = widget.Get("MOUSE_CONTROL_DESC_CLASSIC");
|
||||
mouseControlDescClassic.IsVisible = () => gs.UseClassicMouseStyle;
|
||||
|
||||
var classicScrollRight = mouseControlDescClassic.Get("DESC_SCROLL_RIGHT");
|
||||
classicScrollRight.IsVisible = () => !gs.ClassicMouseMiddleScroll;
|
||||
|
||||
var classicScrollMiddle = mouseControlDescClassic.Get("DESC_SCROLL_MIDDLE");
|
||||
classicScrollMiddle.IsVisible = () => gs.ClassicMouseMiddleScroll;
|
||||
|
||||
var mouseControlDescModern = widget.Get("MOUSE_CONTROL_DESC_MODERN");
|
||||
mouseControlDescModern.IsVisible = () => !gs.UseClassicMouseStyle;
|
||||
|
||||
@@ -92,6 +86,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
|
||||
foreach (var container in new[] { mouseControlDescClassic, mouseControlDescModern })
|
||||
{
|
||||
var classicScrollRight = container.Get("DESC_SCROLL_RIGHT");
|
||||
classicScrollRight.IsVisible = () => gs.UseClassicMouseStyle ^ gs.UseAlternateScrollButton;
|
||||
|
||||
var classicScrollMiddle = container.Get("DESC_SCROLL_MIDDLE");
|
||||
classicScrollMiddle.IsVisible = () => !gs.UseClassicMouseStyle ^ gs.UseAlternateScrollButton;
|
||||
|
||||
var zoomDesc = container.Get("DESC_ZOOM");
|
||||
zoomDesc.IsVisible = () => gs.ZoomModifier == Modifiers.None;
|
||||
|
||||
|
||||
@@ -322,8 +322,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
|
||||
if (client.Location != null)
|
||||
{
|
||||
var locationFont = Game.Renderer.Fonts[locationLabel.Font];
|
||||
var locationWidth = widget.Bounds.Width - 2 * locationLabel.Bounds.X;
|
||||
var location = WidgetUtils.TruncateText(client.Location, locationWidth, locationFont);
|
||||
locationLabel.IsVisible = () => true;
|
||||
locationLabel.GetText = () => client.Location;
|
||||
locationLabel.GetText = () => location;
|
||||
widget.Bounds.Height += locationLabel.Bounds.Height;
|
||||
ipLabel.Bounds.Y += locationLabel.Bounds.Height;
|
||||
adminLabel.Bounds.Y += locationLabel.Bounds.Height;
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
static readonly WindowMode OriginalGraphicsMode;
|
||||
static readonly int2 OriginalGraphicsWindowedSize;
|
||||
static readonly int2 OriginalGraphicsFullscreenSize;
|
||||
static readonly GLProfile OriginalGLProfile;
|
||||
static readonly bool OriginalServerDiscoverNatDevices;
|
||||
|
||||
readonly Dictionary<PanelType, Action> leavePanelActions = new Dictionary<PanelType, Action>();
|
||||
@@ -57,6 +58,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
OriginalVideoDisplay = original.Graphics.VideoDisplay;
|
||||
OriginalGraphicsWindowedSize = original.Graphics.WindowedSize;
|
||||
OriginalGraphicsFullscreenSize = original.Graphics.FullscreenSize;
|
||||
OriginalGLProfile = original.Graphics.GLProfile;
|
||||
OriginalServerDiscoverNatDevices = original.Server.DiscoverNatDevices;
|
||||
}
|
||||
|
||||
@@ -98,6 +100,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
current.Graphics.VideoDisplay != OriginalVideoDisplay ||
|
||||
current.Graphics.WindowedSize != OriginalGraphicsWindowedSize ||
|
||||
current.Graphics.FullscreenSize != OriginalGraphicsFullscreenSize ||
|
||||
current.Graphics.GLProfile != OriginalGLProfile ||
|
||||
current.Server.DiscoverNatDevices != OriginalServerDiscoverNatDevices)
|
||||
{
|
||||
Action restart = () =>
|
||||
@@ -253,16 +256,18 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
windowModeDropdown.GetText = () => ds.Mode == WindowMode.Windowed ?
|
||||
"Windowed" : ds.Mode == WindowMode.Fullscreen ? "Fullscreen (Legacy)" : "Fullscreen";
|
||||
|
||||
var modeChangesDesc = panel.Get("MODE_CHANGES_DESC");
|
||||
modeChangesDesc.IsVisible = () => ds.Mode != WindowMode.Windowed && (ds.Mode != OriginalGraphicsMode ||
|
||||
ds.VideoDisplay != OriginalVideoDisplay);
|
||||
|
||||
var displaySelectionDropDown = panel.Get<DropDownButtonWidget>("DISPLAY_SELECTION_DROPDOWN");
|
||||
displaySelectionDropDown.OnMouseDown = _ => ShowDisplaySelectionDropdown(displaySelectionDropDown, ds);
|
||||
var displaySelectionLabel = new CachedTransform<int, string>(i => "Display {0}".F(i + 1));
|
||||
displaySelectionDropDown.GetText = () => displaySelectionLabel.Update(ds.VideoDisplay);
|
||||
displaySelectionDropDown.IsDisabled = () => Game.Renderer.DisplayCount < 2;
|
||||
|
||||
var glProfileLabel = new CachedTransform<GLProfile, string>(p => p.ToString());
|
||||
var glProfileDropdown = panel.Get<DropDownButtonWidget>("GL_PROFILE_DROPDOWN");
|
||||
glProfileDropdown.OnMouseDown = _ => ShowGLProfileDropdown(glProfileDropdown, ds);
|
||||
glProfileDropdown.GetText = () => glProfileLabel.Update(ds.GLProfile);
|
||||
glProfileDropdown.IsDisabled = () => Game.Renderer.SupportedGLProfiles.Length < 2;
|
||||
|
||||
var statusBarsDropDown = panel.Get<DropDownButtonWidget>("STATUS_BAR_DROPDOWN");
|
||||
statusBarsDropDown.OnMouseDown = _ => ShowStatusBarsDropdown(statusBarsDropDown, gs);
|
||||
statusBarsDropDown.GetText = () => gs.StatusBars == StatusBarsType.Standard ?
|
||||
@@ -307,10 +312,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
|
||||
var windowHeight = panel.Get<TextFieldWidget>("WINDOW_HEIGHT");
|
||||
var origHeightText = windowHeight.Text = ds.WindowedSize.Y.ToString();
|
||||
windowHeight.Text = ds.WindowedSize.Y.ToString();
|
||||
|
||||
var windowChangesDesc = panel.Get("WINDOW_CHANGES_DESC");
|
||||
windowChangesDesc.IsVisible = () => ds.Mode == WindowMode.Windowed &&
|
||||
(ds.Mode != OriginalGraphicsMode || origWidthText != windowWidth.Text || origHeightText != windowHeight.Text);
|
||||
var restartDesc = panel.Get("RESTART_REQUIRED_DESC");
|
||||
restartDesc.IsVisible = () => ds.Mode != OriginalGraphicsMode || ds.VideoDisplay != OriginalVideoDisplay || ds.GLProfile != OriginalGLProfile ||
|
||||
(ds.Mode == WindowMode.Windowed && (origWidthText != windowWidth.Text || origHeightText != windowHeight.Text));
|
||||
|
||||
var frameLimitCheckbox = panel.Get<CheckboxWidget>("FRAME_LIMIT_CHECKBOX");
|
||||
var frameLimitOrigLabel = frameLimitCheckbox.Text;
|
||||
@@ -380,6 +386,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
ds.CapFramerate = dds.CapFramerate;
|
||||
ds.MaxFramerate = dds.MaxFramerate;
|
||||
ds.Language = dds.Language;
|
||||
ds.GLProfile = dds.GLProfile;
|
||||
ds.Mode = dds.Mode;
|
||||
ds.VideoDisplay = dds.VideoDisplay;
|
||||
ds.WindowedSize = dds.WindowedSize;
|
||||
@@ -505,7 +512,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
var gs = Game.Settings.Game;
|
||||
|
||||
BindCheckboxPref(panel, "CLASSIC_MOUSE_MIDDLE_SCROLL_CHECKBOX", gs, "ClassicMouseMiddleScroll");
|
||||
BindCheckboxPref(panel, "ALTERNATE_SCROLL_CHECKBOX", gs, "UseAlternateScrollButton");
|
||||
BindCheckboxPref(panel, "EDGESCROLL_CHECKBOX", gs, "ViewportEdgeScroll");
|
||||
BindCheckboxPref(panel, "LOCKMOUSE_CHECKBOX", gs, "LockMouseWindow");
|
||||
BindSliderPref(panel, "ZOOMSPEED_SLIDER", gs, "ZoomSpeed");
|
||||
@@ -520,23 +527,20 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
mouseScrollDropdown.OnMouseDown = _ => ShowMouseScrollDropdown(mouseScrollDropdown, gs);
|
||||
mouseScrollDropdown.GetText = () => gs.MouseScroll.ToString();
|
||||
|
||||
var classicMouseMiddleScrollCheckbox = panel.Get<CheckboxWidget>("CLASSIC_MOUSE_MIDDLE_SCROLL_CHECKBOX");
|
||||
classicMouseMiddleScrollCheckbox.IsVisible = () => gs.UseClassicMouseStyle;
|
||||
|
||||
var mouseControlDescClassic = panel.Get("MOUSE_CONTROL_DESC_CLASSIC");
|
||||
mouseControlDescClassic.IsVisible = () => gs.UseClassicMouseStyle;
|
||||
|
||||
var classicScrollRight = mouseControlDescClassic.Get("DESC_SCROLL_RIGHT");
|
||||
classicScrollRight.IsVisible = () => !gs.ClassicMouseMiddleScroll;
|
||||
|
||||
var classicScrollMiddle = mouseControlDescClassic.Get("DESC_SCROLL_MIDDLE");
|
||||
classicScrollMiddle.IsVisible = () => gs.ClassicMouseMiddleScroll;
|
||||
|
||||
var mouseControlDescModern = panel.Get("MOUSE_CONTROL_DESC_MODERN");
|
||||
mouseControlDescModern.IsVisible = () => !gs.UseClassicMouseStyle;
|
||||
|
||||
foreach (var container in new[] { mouseControlDescClassic, mouseControlDescModern })
|
||||
{
|
||||
var classicScrollRight = container.Get("DESC_SCROLL_RIGHT");
|
||||
classicScrollRight.IsVisible = () => gs.UseClassicMouseStyle ^ gs.UseAlternateScrollButton;
|
||||
|
||||
var classicScrollMiddle = container.Get("DESC_SCROLL_MIDDLE");
|
||||
classicScrollMiddle.IsVisible = () => !gs.UseClassicMouseStyle ^ gs.UseAlternateScrollButton;
|
||||
|
||||
var zoomDesc = container.Get("DESC_ZOOM");
|
||||
zoomDesc.IsVisible = () => gs.ZoomModifier == Modifiers.None;
|
||||
|
||||
@@ -635,7 +639,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
gs.UseClassicMouseStyle = dgs.UseClassicMouseStyle;
|
||||
gs.MouseScroll = dgs.MouseScroll;
|
||||
gs.ClassicMouseMiddleScroll = dgs.ClassicMouseMiddleScroll;
|
||||
gs.UseAlternateScrollButton = dgs.UseAlternateScrollButton;
|
||||
gs.LockMouseWindow = dgs.LockMouseWindow;
|
||||
gs.ViewportEdgeScroll = dgs.ViewportEdgeScroll;
|
||||
gs.ViewportEdgeScrollStep = dgs.ViewportEdgeScrollStep;
|
||||
@@ -875,6 +879,22 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, Enumerable.Range(0, Game.Renderer.DisplayCount), setupItem);
|
||||
}
|
||||
|
||||
static void ShowGLProfileDropdown(DropDownButtonWidget dropdown, GraphicSettings s)
|
||||
{
|
||||
Func<GLProfile, ScrollItemWidget, ScrollItemWidget> setupItem = (o, itemTemplate) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(itemTemplate,
|
||||
() => s.GLProfile == o,
|
||||
() => s.GLProfile = o);
|
||||
|
||||
var label = o.ToString();
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => label;
|
||||
return item;
|
||||
};
|
||||
|
||||
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, Game.Renderer.SupportedGLProfiles, setupItem);
|
||||
}
|
||||
|
||||
static void ShowTargetLinesDropdown(DropDownButtonWidget dropdown, GameSettings s)
|
||||
{
|
||||
var options = new Dictionary<string, TargetLinesType>()
|
||||
|
||||
@@ -146,13 +146,16 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
UpdateSmoothScrolling();
|
||||
|
||||
var rb = RenderBounds;
|
||||
|
||||
var scrollbarHeight = rb.Height - 2 * ScrollbarWidth;
|
||||
|
||||
var thumbHeight = ContentHeight == 0 ? 0 : Math.Max(MinimumThumbSize, (int)(scrollbarHeight * Math.Min(rb.Height * 1f / ContentHeight, 1f)));
|
||||
var thumbOrigin = rb.Y + ScrollbarWidth + (int)((scrollbarHeight - thumbHeight) * (-1f * currentListOffset / (ContentHeight - rb.Height)));
|
||||
if (thumbHeight == scrollbarHeight)
|
||||
thumbHeight = 0;
|
||||
// Scroll thumb is only visible if the content does not fit within the panel bounds
|
||||
var thumbHeight = 0;
|
||||
var thumbOrigin = rb.Y + ScrollbarWidth;
|
||||
if (ContentHeight > rb.Height)
|
||||
{
|
||||
thumbHeight = Math.Max(MinimumThumbSize, scrollbarHeight * rb.Height / ContentHeight);
|
||||
thumbOrigin += (int)((scrollbarHeight - thumbHeight) * currentListOffset / (rb.Height - ContentHeight));
|
||||
}
|
||||
|
||||
switch (ScrollBar)
|
||||
{
|
||||
|
||||
@@ -318,7 +318,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
}
|
||||
|
||||
var gs = Game.Settings.Game;
|
||||
var scrollButton = gs.UseClassicMouseStyle && !gs.ClassicMouseMiddleScroll ? MouseButton.Right : MouseButton.Middle;
|
||||
var scrollButton = gs.UseClassicMouseStyle ^ gs.UseAlternateScrollButton ? MouseButton.Right : MouseButton.Middle;
|
||||
var scrollType = mi.Button.HasFlag(scrollButton) ? gs.MouseScroll : MouseScrollType.Disabled;
|
||||
|
||||
if (scrollType == MouseScrollType.Disabled)
|
||||
|
||||
@@ -124,19 +124,13 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
if (useClassicMouseStyle && HasMouseFocus)
|
||||
{
|
||||
if (!IsValidDragbox && World.Selection.Actors.Any() && !multiClick)
|
||||
if (!IsValidDragbox && World.Selection.Actors.Any() && !multiClick && uog.InputOverridesSelection(World, mousePos, mi))
|
||||
{
|
||||
var selectableActor = World.ScreenMap.ActorsAtMouse(mousePos).Select(a => a.Actor).Any(x =>
|
||||
x.Info.HasTraitInfo<SelectableInfo>() && (x.Owner.IsAlliedWith(World.RenderPlayer) || !World.FogObscures(x)));
|
||||
|
||||
if (!selectableActor || uog.InputOverridesSelection(World, mousePos, mi))
|
||||
{
|
||||
// Order units instead of selecting
|
||||
ApplyOrders(World, mi);
|
||||
isDragging = false;
|
||||
YieldMouseFocus(mi);
|
||||
return true;
|
||||
}
|
||||
// Order units instead of selecting
|
||||
ApplyOrders(World, mi);
|
||||
isDragging = false;
|
||||
YieldMouseFocus(mi);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@ namespace OpenRA.Platforms.Default
|
||||
{
|
||||
public class DefaultPlatform : IPlatform
|
||||
{
|
||||
public IPlatformWindow CreateWindow(Size size, WindowMode windowMode, float scaleModifier, int batchSize, int videoDisplay)
|
||||
public IPlatformWindow CreateWindow(Size size, WindowMode windowMode, float scaleModifier, int batchSize, int videoDisplay, GLProfile profile)
|
||||
{
|
||||
return new Sdl2PlatformWindow(size, windowMode, scaleModifier, batchSize, videoDisplay);
|
||||
return new Sdl2PlatformWindow(size, windowMode, scaleModifier, batchSize, videoDisplay, profile);
|
||||
}
|
||||
|
||||
public ISoundEngine CreateSound(string device)
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace OpenRA.Platforms.Default
|
||||
OpenGL.glBindRenderbuffer(OpenGL.GL_RENDERBUFFER, depth);
|
||||
OpenGL.CheckGLError();
|
||||
|
||||
var glDepth = OpenGL.Features.HasFlag(OpenGL.GLFeatures.GLES) ? OpenGL.GL_DEPTH_COMPONENT16 : OpenGL.GL_DEPTH_COMPONENT;
|
||||
var glDepth = OpenGL.Profile == GLProfile.Embedded ? OpenGL.GL_DEPTH_COMPONENT16 : OpenGL.GL_DEPTH_COMPONENT;
|
||||
OpenGL.glRenderbufferStorage(OpenGL.GL_RENDERBUFFER, glDepth, size.Width, size.Height);
|
||||
OpenGL.CheckGLError();
|
||||
|
||||
|
||||
@@ -29,12 +29,11 @@ namespace OpenRA.Platforms.Default
|
||||
public enum GLFeatures
|
||||
{
|
||||
None = 0,
|
||||
Core = 1,
|
||||
GLES = 2,
|
||||
DebugMessagesCallback = 4,
|
||||
ESReadFormatBGRA = 8
|
||||
DebugMessagesCallback = 1,
|
||||
ESReadFormatBGRA = 2,
|
||||
}
|
||||
|
||||
public static GLProfile Profile { get; private set; }
|
||||
public static GLFeatures Features { get; private set; }
|
||||
|
||||
public static string Version { get; private set; }
|
||||
@@ -479,7 +478,7 @@ namespace OpenRA.Platforms.Default
|
||||
|
||||
#endregion
|
||||
|
||||
public static void Initialize()
|
||||
public static void Initialize(bool preferLegacyProfile)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -489,13 +488,18 @@ namespace OpenRA.Platforms.Default
|
||||
glGetError = Bind<GetError>("glGetError");
|
||||
glGetStringInternal = Bind<GetString>("glGetString");
|
||||
glGetStringiInternal = Bind<GetStringi>("glGetStringi");
|
||||
glGetIntegerv = Bind<GetIntegerv>("glGetIntegerv");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidProgramException("Failed to initialize low-level OpenGL bindings. GPU information is not available.", e);
|
||||
}
|
||||
|
||||
DetectGLFeatures();
|
||||
if (!DetectGLFeatures(preferLegacyProfile))
|
||||
{
|
||||
WriteGraphicsLog("Unsupported OpenGL version: " + glGetString(GL_VERSION));
|
||||
throw new InvalidProgramException("OpenGL Version Error: See graphics.log for details.");
|
||||
}
|
||||
|
||||
// Allow users to force-disable the debug message callback feature to work around driver bugs
|
||||
if (Features.HasFlag(GLFeatures.DebugMessagesCallback) && Game.Settings.Graphics.DisableGLDebugMessageCallback)
|
||||
@@ -509,10 +513,12 @@ namespace OpenRA.Platforms.Default
|
||||
Features ^= GLFeatures.DebugMessagesCallback;
|
||||
}
|
||||
|
||||
if (!Features.HasFlag(GLFeatures.Core))
|
||||
// Older Intel on Windows is broken too
|
||||
if (Features.HasFlag(GLFeatures.DebugMessagesCallback) && Platform.CurrentPlatform == PlatformType.Windows)
|
||||
{
|
||||
WriteGraphicsLog("Unsupported OpenGL version: " + glGetString(GL_VERSION));
|
||||
throw new InvalidProgramException("OpenGL Version Error: See graphics.log for details.");
|
||||
var renderer = glGetString(GL_RENDERER);
|
||||
if (renderer.Contains("HD Graphics") && !renderer.Contains("UHD Graphics"))
|
||||
Features ^= GLFeatures.DebugMessagesCallback;
|
||||
}
|
||||
|
||||
// Setup the debug message callback handler
|
||||
@@ -520,7 +526,7 @@ namespace OpenRA.Platforms.Default
|
||||
{
|
||||
try
|
||||
{
|
||||
var suffix = Features.HasFlag(GLFeatures.GLES) ? "KHR" : "";
|
||||
var suffix = Profile == GLProfile.Embedded ? "KHR" : "";
|
||||
glDebugMessageCallback = Bind<DebugMessageCallback>("glDebugMessageCallback" + suffix);
|
||||
glDebugMessageInsert = Bind<DebugMessageInsert>("glDebugMessageInsert" + suffix);
|
||||
|
||||
@@ -545,7 +551,6 @@ namespace OpenRA.Platforms.Default
|
||||
glViewport = Bind<Viewport>("glViewport");
|
||||
glClear = Bind<Clear>("glClear");
|
||||
glClearColor = Bind<ClearColor>("glClearColor");
|
||||
glGetIntegerv = Bind<GetIntegerv>("glGetIntegerv");
|
||||
glFinish = Bind<Finish>("glFinish");
|
||||
glCreateProgram = Bind<CreateProgram>("glCreateProgram");
|
||||
glUseProgram = Bind<UseProgram>("glUseProgram");
|
||||
@@ -574,10 +579,7 @@ namespace OpenRA.Platforms.Default
|
||||
glBufferData = Bind<BufferData>("glBufferData");
|
||||
glBufferSubData = Bind<BufferSubData>("glBufferSubData");
|
||||
glDeleteBuffers = Bind<DeleteBuffers>("glDeleteBuffers");
|
||||
glGenVertexArrays = Bind<GenVertexArrays>("glGenVertexArrays");
|
||||
glBindVertexArray = Bind<BindVertexArray>("glBindVertexArray");
|
||||
glBindAttribLocation = Bind<BindAttribLocation>("glBindAttribLocation");
|
||||
glBindFragDataLocation = Bind<BindFragDataLocation>("glBindFragDataLocation");
|
||||
glVertexAttribPointer = Bind<VertexAttribPointer>("glVertexAttribPointer");
|
||||
glEnableVertexAttribArray = Bind<EnableVertexAttribArray>("glEnableVertexAttribArray");
|
||||
glDisableVertexAttribArray = Bind<DisableVertexAttribArray>("glDisableVertexAttribArray");
|
||||
@@ -597,16 +599,39 @@ namespace OpenRA.Platforms.Default
|
||||
glGetTexImage = Bind<GetTexImage>("glGetTexImage");
|
||||
glTexParameteri = Bind<TexParameteri>("glTexParameteri");
|
||||
glTexParameterf = Bind<TexParameterf>("glTexParameterf");
|
||||
glGenFramebuffers = Bind<GenFramebuffers>("glGenFramebuffers");
|
||||
glBindFramebuffer = Bind<BindFramebuffer>("glBindFramebuffer");
|
||||
glFramebufferTexture2D = Bind<FramebufferTexture2D>("glFramebufferTexture2D");
|
||||
glDeleteFramebuffers = Bind<DeleteFramebuffers>("glDeleteFramebuffers");
|
||||
glGenRenderbuffers = Bind<GenRenderbuffers>("glGenRenderbuffers");
|
||||
glBindRenderbuffer = Bind<BindRenderbuffer>("glBindRenderbuffer");
|
||||
glRenderbufferStorage = Bind<RenderbufferStorage>("glRenderbufferStorage");
|
||||
glDeleteRenderbuffers = Bind<DeleteRenderbuffers>("glDeleteRenderbuffers");
|
||||
glFramebufferRenderbuffer = Bind<FramebufferRenderbuffer>("glFramebufferRenderbuffer");
|
||||
glCheckFramebufferStatus = Bind<CheckFramebufferStatus>("glCheckFramebufferStatus");
|
||||
|
||||
if (Profile != GLProfile.Legacy)
|
||||
{
|
||||
glGenVertexArrays = Bind<GenVertexArrays>("glGenVertexArrays");
|
||||
glBindVertexArray = Bind<BindVertexArray>("glBindVertexArray");
|
||||
glBindFragDataLocation = Bind<BindFragDataLocation>("glBindFragDataLocation");
|
||||
glGenFramebuffers = Bind<GenFramebuffers>("glGenFramebuffers");
|
||||
glBindFramebuffer = Bind<BindFramebuffer>("glBindFramebuffer");
|
||||
glFramebufferTexture2D = Bind<FramebufferTexture2D>("glFramebufferTexture2D");
|
||||
glDeleteFramebuffers = Bind<DeleteFramebuffers>("glDeleteFramebuffers");
|
||||
glGenRenderbuffers = Bind<GenRenderbuffers>("glGenRenderbuffers");
|
||||
glBindRenderbuffer = Bind<BindRenderbuffer>("glBindRenderbuffer");
|
||||
glRenderbufferStorage = Bind<RenderbufferStorage>("glRenderbufferStorage");
|
||||
glDeleteRenderbuffers = Bind<DeleteRenderbuffers>("glDeleteRenderbuffers");
|
||||
glFramebufferRenderbuffer = Bind<FramebufferRenderbuffer>("glFramebufferRenderbuffer");
|
||||
glCheckFramebufferStatus = Bind<CheckFramebufferStatus>("glCheckFramebufferStatus");
|
||||
}
|
||||
else
|
||||
{
|
||||
glGenVertexArrays = null;
|
||||
glBindVertexArray = null;
|
||||
glBindFragDataLocation = null;
|
||||
glGenFramebuffers = Bind<GenFramebuffers>("glGenFramebuffersEXT");
|
||||
glBindFramebuffer = Bind<BindFramebuffer>("glBindFramebufferEXT");
|
||||
glFramebufferTexture2D = Bind<FramebufferTexture2D>("glFramebufferTexture2DEXT");
|
||||
glDeleteFramebuffers = Bind<DeleteFramebuffers>("glDeleteFramebuffersEXT");
|
||||
glGenRenderbuffers = Bind<GenRenderbuffers>("glGenRenderbuffersEXT");
|
||||
glBindRenderbuffer = Bind<BindRenderbuffer>("glBindRenderbufferEXT");
|
||||
glRenderbufferStorage = Bind<RenderbufferStorage>("glRenderbufferStorageEXT");
|
||||
glDeleteRenderbuffers = Bind<DeleteRenderbuffers>("glDeleteRenderbuffersEXT");
|
||||
glFramebufferRenderbuffer = Bind<FramebufferRenderbuffer>("glFramebufferRenderbufferEXT");
|
||||
glCheckFramebufferStatus = Bind<CheckFramebufferStatus>("glCheckFramebufferStatusEXT");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -620,8 +645,9 @@ namespace OpenRA.Platforms.Default
|
||||
return (T)(object)Marshal.GetDelegateForFunctionPointer(SDL.SDL_GL_GetProcAddress(name), typeof(T));
|
||||
}
|
||||
|
||||
public static void DetectGLFeatures()
|
||||
public static bool DetectGLFeatures(bool preferLegacyProfile)
|
||||
{
|
||||
var hasValidConfiguration = false;
|
||||
try
|
||||
{
|
||||
Version = glGetString(GL_VERSION);
|
||||
@@ -641,20 +667,34 @@ namespace OpenRA.Platforms.Default
|
||||
var hasBGRA = SDL.SDL_GL_ExtensionSupported("GL_EXT_texture_format_BGRA8888") == SDL.SDL_bool.SDL_TRUE;
|
||||
if (Version.Contains(" ES") && hasBGRA && major >= 3)
|
||||
{
|
||||
Features = GLFeatures.Core | GLFeatures.GLES;
|
||||
|
||||
hasValidConfiguration = true;
|
||||
Profile = GLProfile.Embedded;
|
||||
if (SDL.SDL_GL_ExtensionSupported("GL_EXT_read_format_bgra") == SDL.SDL_bool.SDL_TRUE)
|
||||
Features |= GLFeatures.ESReadFormatBGRA;
|
||||
}
|
||||
else if (major > 3 || (major == 3 && minor >= 2))
|
||||
Features = GLFeatures.Core;
|
||||
{
|
||||
hasValidConfiguration = true;
|
||||
Profile = GLProfile.Modern;
|
||||
}
|
||||
|
||||
// Debug callbacks were introduced in GL 4.3
|
||||
var hasDebugMessagesCallback = SDL.SDL_GL_ExtensionSupported("GL_KHR_debug") == SDL.SDL_bool.SDL_TRUE;
|
||||
if (hasDebugMessagesCallback)
|
||||
Features |= GLFeatures.DebugMessagesCallback;
|
||||
|
||||
if (preferLegacyProfile || (major == 2 && minor == 1) || (major == 3 && minor < 2))
|
||||
{
|
||||
if (SDL.SDL_GL_ExtensionSupported("GL_EXT_framebuffer_object") == SDL.SDL_bool.SDL_TRUE)
|
||||
{
|
||||
hasValidConfiguration = true;
|
||||
Profile = GLProfile.Legacy;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
return hasValidConfiguration;
|
||||
}
|
||||
|
||||
public static void WriteGraphicsLog(string message)
|
||||
@@ -677,10 +717,15 @@ namespace OpenRA.Platforms.Default
|
||||
Log.Write("graphics", "Shader Version: {0}", glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||
Log.Write("graphics", "Available extensions:");
|
||||
|
||||
int extensionCount;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, out extensionCount);
|
||||
for (var i = 0; i < extensionCount; i++)
|
||||
Log.Write("graphics", glGetStringi(GL_EXTENSIONS, (uint)i));
|
||||
if (Profile != GLProfile.Legacy)
|
||||
{
|
||||
int extensionCount;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, out extensionCount);
|
||||
for (var i = 0; i < extensionCount; i++)
|
||||
Log.Write("graphics", glGetStringi(GL_EXTENSIONS, (uint)i));
|
||||
}
|
||||
else
|
||||
Log.Write("graphics", glGetString(GL_EXTENSIONS));
|
||||
}
|
||||
|
||||
public static void CheckGLError()
|
||||
|
||||
@@ -35,14 +35,17 @@ namespace OpenRA.Platforms.Default
|
||||
if (context == IntPtr.Zero || SDL.SDL_GL_MakeCurrent(window.Window, context) < 0)
|
||||
throw new InvalidOperationException("Can not create OpenGL context. (Error: {0})".F(SDL.SDL_GetError()));
|
||||
|
||||
OpenGL.Initialize();
|
||||
OpenGL.Initialize(window.GLProfile == GLProfile.Legacy);
|
||||
OpenGL.CheckGLError();
|
||||
|
||||
uint vao;
|
||||
OpenGL.CheckGLError();
|
||||
OpenGL.glGenVertexArrays(1, out vao);
|
||||
OpenGL.CheckGLError();
|
||||
OpenGL.glBindVertexArray(vao);
|
||||
OpenGL.CheckGLError();
|
||||
if (OpenGL.Profile != GLProfile.Legacy)
|
||||
{
|
||||
uint vao;
|
||||
OpenGL.glGenVertexArrays(1, out vao);
|
||||
OpenGL.CheckGLError();
|
||||
OpenGL.glBindVertexArray(vao);
|
||||
OpenGL.CheckGLError();
|
||||
}
|
||||
|
||||
OpenGL.glEnableVertexAttribArray(Shader.VertexPosAttributeIndex);
|
||||
OpenGL.CheckGLError();
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using OpenRA.Primitives;
|
||||
using SDL2;
|
||||
@@ -34,6 +35,8 @@ namespace OpenRA.Platforms.Default
|
||||
float windowScale = 1f;
|
||||
int2? lockedMousePosition;
|
||||
float scaleModifier;
|
||||
readonly GLProfile profile;
|
||||
readonly GLProfile[] supportedProfiles;
|
||||
|
||||
internal IntPtr Window
|
||||
{
|
||||
@@ -105,12 +108,31 @@ namespace OpenRA.Platforms.Default
|
||||
}
|
||||
}
|
||||
|
||||
public GLProfile GLProfile
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (syncObject)
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
|
||||
public GLProfile[] SupportedGLProfiles
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (syncObject)
|
||||
return supportedProfiles;
|
||||
}
|
||||
}
|
||||
|
||||
public event Action<float, float, float, float> OnWindowScaleChanged = (oldNative, oldEffective, newNative, newEffective) => { };
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
static extern bool SetProcessDPIAware();
|
||||
|
||||
public Sdl2PlatformWindow(Size requestEffectiveWindowSize, WindowMode windowMode, float scaleModifier, int batchSize, int videoDisplay)
|
||||
public Sdl2PlatformWindow(Size requestEffectiveWindowSize, WindowMode windowMode,
|
||||
float scaleModifier, int batchSize, int videoDisplay, GLProfile requestProfile)
|
||||
{
|
||||
// Lock the Window/Surface properties until initialization is complete
|
||||
lock (syncObject)
|
||||
@@ -122,31 +144,24 @@ namespace OpenRA.Platforms.Default
|
||||
SetProcessDPIAware();
|
||||
|
||||
SDL.SDL_Init(SDL.SDL_INIT_NOPARACHUTE | SDL.SDL_INIT_VIDEO);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_RED_SIZE, 8);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_GREEN_SIZE, 8);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_BLUE_SIZE, 8);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_ALPHA_SIZE, 0);
|
||||
|
||||
// Decide between OpenGL and OpenGL ES rendering
|
||||
// Test whether we can use the preferred renderer and fall back to the other if that fails
|
||||
// If neither works we will throw a graphics error later when trying to create the real window
|
||||
bool useGLES;
|
||||
if (Game.Settings.Graphics.PreferGLES)
|
||||
useGLES = CanCreateGLWindow(3, 0, SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_ES);
|
||||
// Decide which OpenGL profile to use.
|
||||
// We first need to query the available profiles on Windows/Linux.
|
||||
// On macOS, known/consistent OpenGL support is provided by the OS.
|
||||
if (Platform.CurrentPlatform == PlatformType.OSX)
|
||||
supportedProfiles = new[] { GLProfile.Modern, GLProfile.Legacy };
|
||||
else
|
||||
useGLES = !CanCreateGLWindow(3, 2, SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
supportedProfiles = new[] { GLProfile.Modern, GLProfile.Embedded, GLProfile.Legacy }
|
||||
.Where(CanCreateGLWindow)
|
||||
.ToArray();
|
||||
|
||||
var glMajor = 3;
|
||||
var glMinor = useGLES ? 0 : 2;
|
||||
var glProfile = useGLES ? SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_ES : SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE;
|
||||
if (!supportedProfiles.Any())
|
||||
throw new InvalidOperationException("No supported OpenGL profiles were found.");
|
||||
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, glMajor);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, glMinor);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, (int)glProfile);
|
||||
|
||||
Console.WriteLine("Using SDL 2 with OpenGL{0} renderer", useGLES ? " ES" : "");
|
||||
profile = supportedProfiles.Contains(requestProfile) ? requestProfile : supportedProfiles.First();
|
||||
SetSDLAttributes(profile);
|
||||
|
||||
Console.WriteLine("Using SDL 2 with OpenGL ({0}) renderer", profile);
|
||||
if (videoDisplay < 0 || videoDisplay >= DisplayCount)
|
||||
videoDisplay = 0;
|
||||
|
||||
@@ -449,12 +464,38 @@ namespace OpenRA.Platforms.Default
|
||||
return input.SetClipboardText(text);
|
||||
}
|
||||
|
||||
static bool CanCreateGLWindow(int major, int minor, SDL.SDL_GLprofile profile)
|
||||
static void SetSDLAttributes(GLProfile profile)
|
||||
{
|
||||
SDL.SDL_GL_ResetAttributes();
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_RED_SIZE, 8);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_GREEN_SIZE, 8);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_BLUE_SIZE, 8);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_ALPHA_SIZE, 0);
|
||||
|
||||
switch (profile)
|
||||
{
|
||||
case GLProfile.Modern:
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, (int)SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
break;
|
||||
case GLProfile.Embedded:
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, (int)SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_ES);
|
||||
break;
|
||||
case GLProfile.Legacy:
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool CanCreateGLWindow(GLProfile profile)
|
||||
{
|
||||
// Implementation inspired by TestIndividualGLVersion from Veldrid
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, major);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, minor);
|
||||
SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, (int)profile);
|
||||
SetSDLAttributes(profile);
|
||||
|
||||
var flags = SDL.SDL_WindowFlags.SDL_WINDOW_HIDDEN | SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL;
|
||||
var window = SDL.SDL_CreateWindow("", 0, 0, 1, 1, flags);
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace OpenRA.Platforms.Default
|
||||
public const int TexMetadataAttributeIndex = 2;
|
||||
|
||||
readonly Dictionary<string, int> samplers = new Dictionary<string, int>();
|
||||
readonly Dictionary<int, int> legacySizeUniforms = new Dictionary<int, int>();
|
||||
readonly Dictionary<int, ITexture> textures = new Dictionary<int, ITexture>();
|
||||
readonly Queue<int> unbindTextures = new Queue<int>();
|
||||
readonly uint program;
|
||||
@@ -33,7 +34,9 @@ namespace OpenRA.Platforms.Default
|
||||
var filename = Path.Combine(Platform.GameDir, "glsl", name + "." + ext);
|
||||
var code = File.ReadAllText(filename);
|
||||
|
||||
var version = OpenGL.Features.HasFlag(OpenGL.GLFeatures.GLES) ? "300 es" : "140";
|
||||
var version = OpenGL.Profile == GLProfile.Embedded ? "300 es" :
|
||||
OpenGL.Profile == GLProfile.Legacy ? "120" : "140";
|
||||
|
||||
code = code.Replace("{VERSION}", version);
|
||||
|
||||
var shader = OpenGL.glCreateShader(type);
|
||||
@@ -80,8 +83,12 @@ namespace OpenRA.Platforms.Default
|
||||
OpenGL.CheckGLError();
|
||||
OpenGL.glBindAttribLocation(program, TexMetadataAttributeIndex, "aVertexTexMetadata");
|
||||
OpenGL.CheckGLError();
|
||||
OpenGL.glBindFragDataLocation(program, 0, "fragColor");
|
||||
OpenGL.CheckGLError();
|
||||
|
||||
if (OpenGL.Profile != GLProfile.Legacy)
|
||||
{
|
||||
OpenGL.glBindFragDataLocation(program, 0, "fragColor");
|
||||
OpenGL.CheckGLError();
|
||||
}
|
||||
|
||||
OpenGL.glAttachShader(program, vertexShader);
|
||||
OpenGL.CheckGLError();
|
||||
@@ -132,6 +139,13 @@ namespace OpenRA.Platforms.Default
|
||||
OpenGL.glUniform1i(loc, nextTexUnit);
|
||||
OpenGL.CheckGLError();
|
||||
|
||||
if (OpenGL.Profile == GLProfile.Legacy)
|
||||
{
|
||||
var sizeLoc = OpenGL.glGetUniformLocation(program, sampler + "Size");
|
||||
if (sizeLoc >= 0)
|
||||
legacySizeUniforms.Add(nextTexUnit, sizeLoc);
|
||||
}
|
||||
|
||||
nextTexUnit++;
|
||||
}
|
||||
}
|
||||
@@ -146,13 +160,21 @@ namespace OpenRA.Platforms.Default
|
||||
// bind the textures
|
||||
foreach (var kv in textures)
|
||||
{
|
||||
var id = ((ITextureInternal)kv.Value).ID;
|
||||
var texture = (ITextureInternal)kv.Value;
|
||||
|
||||
// Evict disposed textures from the cache
|
||||
if (OpenGL.glIsTexture(id))
|
||||
if (OpenGL.glIsTexture(texture.ID))
|
||||
{
|
||||
OpenGL.glActiveTexture(OpenGL.GL_TEXTURE0 + kv.Key);
|
||||
OpenGL.glBindTexture(OpenGL.GL_TEXTURE_2D, id);
|
||||
OpenGL.glBindTexture(OpenGL.GL_TEXTURE_2D, texture.ID);
|
||||
|
||||
// Work around missing textureSize GLSL function by explicitly tracking sizes in a uniform
|
||||
int param;
|
||||
if (OpenGL.Profile == GLProfile.Legacy && legacySizeUniforms.TryGetValue(kv.Key, out param))
|
||||
{
|
||||
OpenGL.glUniform2f(param, texture.Size.Width, texture.Size.Height);
|
||||
OpenGL.CheckGLError();
|
||||
}
|
||||
}
|
||||
else
|
||||
unbindTextures.Enqueue(kv.Key);
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace OpenRA.Platforms.Default
|
||||
void SetData(IntPtr data, int width, int height)
|
||||
{
|
||||
PrepareTexture();
|
||||
var glInternalFormat = OpenGL.Features.HasFlag(OpenGL.GLFeatures.GLES) ? OpenGL.GL_BGRA : OpenGL.GL_RGBA8;
|
||||
var glInternalFormat = OpenGL.Profile == GLProfile.Embedded ? OpenGL.GL_BGRA : OpenGL.GL_RGBA8;
|
||||
OpenGL.glTexImage2D(OpenGL.GL_TEXTURE_2D, 0, glInternalFormat, width, height,
|
||||
0, OpenGL.GL_BGRA, OpenGL.GL_UNSIGNED_BYTE, data);
|
||||
OpenGL.CheckGLError();
|
||||
@@ -119,7 +119,7 @@ namespace OpenRA.Platforms.Default
|
||||
var data = new byte[4 * Size.Width * Size.Height];
|
||||
|
||||
// GLES doesn't support glGetTexImage so data must be read back via a frame buffer
|
||||
if (OpenGL.Features.HasFlag(OpenGL.GLFeatures.GLES))
|
||||
if (OpenGL.Profile == GLProfile.Embedded)
|
||||
{
|
||||
// Query the active framebuffer so we can restore it afterwards
|
||||
int lastFramebuffer;
|
||||
|
||||
158
OpenRA.sln
@@ -17,165 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenRA.Platforms.Default",
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenRA.Mods.Common", "OpenRA.Mods.Common\OpenRA.Mods.Common.csproj", "{FE6C8CC0-2F07-442A-B29F-17617B3B7FC6}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tiberian Dawn Lua scripts", "Tiberian Dawn Lua scripts", "{62FCD0D0-6D24-435D-9DD8-3CCADCF7ECAB}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
mods\cnc\maps\cnc64gdi01\cnc64gdi01.lua = mods\cnc\maps\cnc64gdi01\cnc64gdi01.lua
|
||||
mods\cnc\maps\gdi01\gdi01.lua = mods\cnc\maps\gdi01\gdi01.lua
|
||||
mods\cnc\maps\gdi02\gdi02.lua = mods\cnc\maps\gdi02\gdi02.lua
|
||||
mods\cnc\maps\gdi03\gdi03.lua = mods\cnc\maps\gdi03\gdi03.lua
|
||||
mods\cnc\maps\gdi04a\gdi04a.lua = mods\cnc\maps\gdi04a\gdi04a.lua
|
||||
mods\cnc\maps\gdi04b\gdi04b.lua = mods\cnc\maps\gdi04b\gdi04b.lua
|
||||
mods\cnc\maps\gdi04c\gdi04c.lua = mods\cnc\maps\gdi04c\gdi04c.lua
|
||||
mods\cnc\maps\gdi05a\gdi05a.lua = mods\cnc\maps\gdi05a\gdi05a.lua
|
||||
mods\cnc\maps\gdi05b\gdi05b.lua = mods\cnc\maps\gdi05b\gdi05b.lua
|
||||
mods\cnc\maps\gdi06\gdi06.lua = mods\cnc\maps\gdi06\gdi06.lua
|
||||
mods\cnc\maps\gdi07\gdi07.lua = mods\cnc\maps\gdi07\gdi07.lua
|
||||
mods\cnc\maps\nod01\nod01.lua = mods\cnc\maps\nod01\nod01.lua
|
||||
mods\cnc\maps\nod02a\nod02a.lua = mods\cnc\maps\nod02a\nod02a.lua
|
||||
mods\cnc\maps\nod02b\nod02b.lua = mods\cnc\maps\nod02b\nod02b.lua
|
||||
mods\cnc\maps\nod03a\nod03a.lua = mods\cnc\maps\nod03a\nod03a.lua
|
||||
mods\cnc\maps\nod03b\nod03b.lua = mods\cnc\maps\nod03b\nod03b.lua
|
||||
mods\cnc\maps\nod04a\nod04a.lua = mods\cnc\maps\nod04a\nod04a.lua
|
||||
mods\cnc\maps\nod04b\nod04b.lua = mods\cnc\maps\nod04b\nod04b.lua
|
||||
mods\cnc\maps\nod05\nod05.lua = mods\cnc\maps\nod05\nod05.lua
|
||||
mods\cnc\maps\nod06a\nod06a.lua = mods\cnc\maps\nod06a\nod06a.lua
|
||||
mods\cnc\maps\nod06b\nod06b.lua = mods\cnc\maps\nod06b\nod06b.lua
|
||||
mods\cnc\maps\nod06c\nod06c.lua = mods\cnc\maps\nod06c\nod06c.lua
|
||||
mods\cnc\maps\nod07a\nod07a-AI.lua = mods\cnc\maps\nod07a\nod07a-AI.lua
|
||||
mods\cnc\maps\nod07a\nod07a.lua = mods\cnc\maps\nod07a\nod07a.lua
|
||||
mods\cnc\maps\nod07b\nod07b-AI.lua = mods\cnc\maps\nod07b\nod07b-AI.lua
|
||||
mods\cnc\maps\nod07b\nod07b.lua = mods\cnc\maps\nod07b\nod07b.lua
|
||||
mods\cnc\maps\nod07c\nod07c.lua = mods\cnc\maps\nod07c\nod07c.lua
|
||||
mods\cnc\maps\nod08a\nod08a-AI.lua = mods\cnc\maps\nod08a\nod08a-AI.lua
|
||||
mods\cnc\maps\nod08a\nod08a.lua = mods\cnc\maps\nod08a\nod08a.lua
|
||||
mods\cnc\maps\nod08b\nod08b-AI.lua = mods\cnc\maps\nod08b\nod08b-AI.lua
|
||||
mods\cnc\maps\nod08b\nod08b.lua = mods\cnc\maps\nod08b\nod08b.lua
|
||||
mods\cnc\maps\nod09\nod09-AI.lua = mods\cnc\maps\nod09\nod09-AI.lua
|
||||
mods\cnc\maps\nod09\nod09.lua = mods\cnc\maps\nod09\nod09.lua
|
||||
mods\cnc\maps\funpark01\scj01ea.lua = mods\cnc\maps\funpark01\scj01ea.lua
|
||||
mods\cnc\maps\shellmap\shellmap.lua = mods\cnc\maps\shellmap\shellmap.lua
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Red Alert Lua scripts", "Red Alert Lua scripts", "{B35D533F-BEB6-4674-A466-324EEFD97259}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
mods\ra\maps\allies-01\allies01.lua = mods\ra\maps\allies-01\allies01.lua
|
||||
mods\ra\maps\allies-02\allies02.lua = mods\ra\maps\allies-02\allies02.lua
|
||||
mods\ra\maps\allies-03a\allies03a.lua = mods\ra\maps\allies-03a\allies03a.lua
|
||||
mods\ra\maps\allies-03b\allies03b.lua = mods\ra\maps\allies-03b\allies03b.lua
|
||||
mods\ra\maps\allies-05a\allies05a-AI.lua = mods\ra\maps\allies-05a\allies05a-AI.lua
|
||||
mods\ra\maps\allies-05a\allies05a.lua = mods\ra\maps\allies-05a\allies05a.lua
|
||||
mods\ra\maps\allies-06a\allies06a-AI.lua = mods\ra\maps\allies-06a\allies06a-AI.lua
|
||||
mods\ra\maps\allies-06a\allies06a.lua = mods\ra\maps\allies-06a\allies06a.lua
|
||||
mods\ra\maps\allies-06b\allies06b-AI.lua = mods\ra\maps\allies-06b\allies06b-AI.lua
|
||||
mods\ra\maps\allies-06b\allies06b.lua = mods\ra\maps\allies-06b\allies06b.lua
|
||||
mods\ra\maps\desert-shellmap\desert-shellmap.lua = mods\ra\maps\desert-shellmap\desert-shellmap.lua
|
||||
mods\ra\maps\evacuation\evacuation.lua = mods\ra\maps\evacuation\evacuation.lua
|
||||
mods\ra\maps\exodus\exodus.lua = mods\ra\maps\exodus\exodus.lua
|
||||
mods\ra\maps\fort-lonestar\fort-lonestar-AI.lua = mods\ra\maps\fort-lonestar\fort-lonestar-AI.lua
|
||||
mods\ra\maps\fort-lonestar\fort-lonestar.lua = mods\ra\maps\fort-lonestar\fort-lonestar.lua
|
||||
mods\ra\maps\infiltration\infiltration.lua = mods\ra\maps\infiltration\infiltration.lua
|
||||
mods\ra\maps\intervention\intervention.lua = mods\ra\maps\intervention\intervention.lua
|
||||
mods\ra\maps\monster-tank-madness\monster-tank-madness.lua = mods\ra\maps\monster-tank-madness\monster-tank-madness.lua
|
||||
mods\ra\maps\soviet-soldier-volkov-n-chitzkoi\scu35ea-AI.lua = mods\ra\maps\soviet-soldier-volkov-n-chitzkoi\scu35ea-AI.lua
|
||||
mods\ra\maps\soviet-soldier-volkov-n-chitzkoi\scu35ea.lua = mods\ra\maps\soviet-soldier-volkov-n-chitzkoi\scu35ea.lua
|
||||
mods\ra\maps\top-o-the-world\scu36ea.lua = mods\ra\maps\top-o-the-world\scu36ea.lua
|
||||
mods\ra\maps\soviet-01\soviet01.lua = mods\ra\maps\soviet-01\soviet01.lua
|
||||
mods\ra\maps\soviet-02a\soviet02a.lua = mods\ra\maps\soviet-02a\soviet02a.lua
|
||||
mods\ra\maps\soviet-02b\soviet02b.lua = mods\ra\maps\soviet-02b\soviet02b.lua
|
||||
mods\ra\maps\soviet-03\soviet03.lua = mods\ra\maps\soviet-03\soviet03.lua
|
||||
mods\ra\maps\soviet-04a\soviet04a-AI.lua = mods\ra\maps\soviet-04a\soviet04a-AI.lua
|
||||
mods\ra\maps\soviet-04a\soviet04a-reinforcements_teams.lua = mods\ra\maps\soviet-04a\soviet04a-reinforcements_teams.lua
|
||||
mods\ra\maps\soviet-04a\soviet04a.lua = mods\ra\maps\soviet-04a\soviet04a.lua
|
||||
mods\ra\maps\soviet-04b\soviet04b-AI.lua = mods\ra\maps\soviet-04b\soviet04b-AI.lua
|
||||
mods\ra\maps\soviet-04b\soviet04b-reinforcements_teams.lua = mods\ra\maps\soviet-04b\soviet04b-reinforcements_teams.lua
|
||||
mods\ra\maps\soviet-04b\soviet04b.lua = mods\ra\maps\soviet-04b\soviet04b.lua
|
||||
mods\ra\maps\soviet-05\soviet05-AI.lua = mods\ra\maps\soviet-05\soviet05-AI.lua
|
||||
mods\ra\maps\soviet-05\soviet05-reinforcements_teams.lua = mods\ra\maps\soviet-05\soviet05-reinforcements_teams.lua
|
||||
mods\ra\maps\soviet-05\soviet05.lua = mods\ra\maps\soviet-05\soviet05.lua
|
||||
mods\ra\maps\soviet-06a\soviet06a-AI.lua = mods\ra\maps\soviet-06a\soviet06a-AI.lua
|
||||
mods\ra\maps\soviet-06a\soviet06a-reinforcements_teams.lua = mods\ra\maps\soviet-06a\soviet06a-reinforcements_teams.lua
|
||||
mods\ra\maps\soviet-06a\soviet06a.lua = mods\ra\maps\soviet-06a\soviet06a.lua
|
||||
mods\ra\maps\soviet-06b\soviet06b-AI.lua = mods\ra\maps\soviet-06b\soviet06b-AI.lua
|
||||
mods\ra\maps\soviet-06b\soviet06b-reinforcements_teams.lua = mods\ra\maps\soviet-06b\soviet06b-reinforcements_teams.lua
|
||||
mods\ra\maps\soviet-06b\soviet06b.lua = mods\ra\maps\soviet-06b\soviet06b.lua
|
||||
mods\ra\maps\soviet-07\soviet07.lua = mods\ra\maps\soviet-07\soviet07.lua
|
||||
mods\ra\maps\survival01\survival01.lua = mods\ra\maps\survival01\survival01.lua
|
||||
mods\ra\maps\survival02\survival02.lua = mods\ra\maps\survival02\survival02.lua
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dune 2000 Lua scripts", "Dune 2000 Lua scripts", "{06B1AE07-DDB0-4287-8700-A8CD9A0E652E}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
mods\d2k\maps\atreides-01a\atreides01a.lua = mods\d2k\maps\atreides-01a\atreides01a.lua
|
||||
mods\d2k\maps\atreides-01b\atreides01b.lua = mods\d2k\maps\atreides-01b\atreides01b.lua
|
||||
mods\d2k\maps\atreides-02a\atreides02a-AI.lua = mods\d2k\maps\atreides-02a\atreides02a-AI.lua
|
||||
mods\d2k\maps\atreides-02a\atreides02a.lua = mods\d2k\maps\atreides-02a\atreides02a.lua
|
||||
mods\d2k\maps\atreides-02b\atreides02b-AI.lua = mods\d2k\maps\atreides-02b\atreides02b-AI.lua
|
||||
mods\d2k\maps\atreides-02b\atreides02b.lua = mods\d2k\maps\atreides-02b\atreides02b.lua
|
||||
mods\d2k\maps\atreides-03a\atreides03a-AI.lua = mods\d2k\maps\atreides-03a\atreides03a-AI.lua
|
||||
mods\d2k\maps\atreides-03a\atreides03a.lua = mods\d2k\maps\atreides-03a\atreides03a.lua
|
||||
mods\d2k\maps\atreides-03b\atreides03b-AI.lua = mods\d2k\maps\atreides-03b\atreides03b-AI.lua
|
||||
mods\d2k\maps\atreides-03b\atreides03b.lua = mods\d2k\maps\atreides-03b\atreides03b.lua
|
||||
mods\d2k\maps\atreides-04\atreides04-AI.lua = mods\d2k\maps\atreides-04\atreides04-AI.lua
|
||||
mods\d2k\maps\atreides-04\atreides04.lua = mods\d2k\maps\atreides-04\atreides04.lua
|
||||
mods\d2k\maps\atreides-05\atreides05-AI.lua = mods\d2k\maps\atreides-05\atreides05-AI.lua
|
||||
mods\d2k\maps\atreides-05\atreides05.lua = mods\d2k\maps\atreides-05\atreides05.lua
|
||||
mods\d2k\bits\scripts\campaign-global.lua = mods\d2k\bits\scripts\campaign-global.lua
|
||||
mods\d2k\maps\harkonnen-01a\harkonnen01a.lua = mods\d2k\maps\harkonnen-01a\harkonnen01a.lua
|
||||
mods\d2k\maps\harkonnen-01b\harkonnen01b.lua = mods\d2k\maps\harkonnen-01b\harkonnen01b.lua
|
||||
mods\d2k\maps\harkonnen-02a\harkonnen02a-AI.lua = mods\d2k\maps\harkonnen-02a\harkonnen02a-AI.lua
|
||||
mods\d2k\maps\harkonnen-02a\harkonnen02a.lua = mods\d2k\maps\harkonnen-02a\harkonnen02a.lua
|
||||
mods\d2k\maps\harkonnen-02b\harkonnen02b-AI.lua = mods\d2k\maps\harkonnen-02b\harkonnen02b-AI.lua
|
||||
mods\d2k\maps\harkonnen-02b\harkonnen02b.lua = mods\d2k\maps\harkonnen-02b\harkonnen02b.lua
|
||||
mods\d2k\maps\harkonnen-03a\harkonnen03a-AI.lua = mods\d2k\maps\harkonnen-03a\harkonnen03a-AI.lua
|
||||
mods\d2k\maps\harkonnen-03a\harkonnen03a.lua = mods\d2k\maps\harkonnen-03a\harkonnen03a.lua
|
||||
mods\d2k\maps\harkonnen-03b\harkonnen03b-AI.lua = mods\d2k\maps\harkonnen-03b\harkonnen03b-AI.lua
|
||||
mods\d2k\maps\harkonnen-03b\harkonnen03b.lua = mods\d2k\maps\harkonnen-03b\harkonnen03b.lua
|
||||
mods\d2k\maps\harkonnen-04\harkonnen04-AI.lua = mods\d2k\maps\harkonnen-04\harkonnen04-AI.lua
|
||||
mods\d2k\maps\harkonnen-04\harkonnen04.lua = mods\d2k\maps\harkonnen-04\harkonnen04.lua
|
||||
mods\d2k\maps\harkonnen-05\harkonnen05-AI.lua = mods\d2k\maps\harkonnen-05\harkonnen05-AI.lua
|
||||
mods\d2k\maps\harkonnen-05\harkonnen05.lua = mods\d2k\maps\harkonnen-05\harkonnen05.lua
|
||||
mods\d2k\maps\harkonnen-06a\harkonnen06a-AI.lua = mods\d2k\maps\harkonnen-06a\harkonnen06a-AI.lua
|
||||
mods\d2k\maps\harkonnen-06a\harkonnen06a.lua = mods\d2k\maps\harkonnen-06a\harkonnen06a.lua
|
||||
mods\d2k\maps\harkonnen-06b\harkonnen06b-AI.lua = mods\d2k\maps\harkonnen-06b\harkonnen06b-AI.lua
|
||||
mods\d2k\maps\harkonnen-06b\harkonnen06b.lua = mods\d2k\maps\harkonnen-06b\harkonnen06b.lua
|
||||
mods\d2k\maps\harkonnen-07\harkonnen07-AI.lua = mods\d2k\maps\harkonnen-07\harkonnen07-AI.lua
|
||||
mods\d2k\maps\harkonnen-07\harkonnen07.lua = mods\d2k\maps\harkonnen-07\harkonnen07.lua
|
||||
mods\d2k\maps\harkonnen-08\harkonnen08-AI.lua = mods\d2k\maps\harkonnen-08\harkonnen08-AI.lua
|
||||
mods\d2k\maps\harkonnen-08\harkonnen08.lua = mods\d2k\maps\harkonnen-08\harkonnen08.lua
|
||||
mods\d2k\maps\harkonnen-09a\harkonnen09a-AI.lua = mods\d2k\maps\harkonnen-09a\harkonnen09a-AI.lua
|
||||
mods\d2k\maps\harkonnen-09a\harkonnen09a.lua = mods\d2k\maps\harkonnen-09a\harkonnen09a.lua
|
||||
mods\d2k\maps\harkonnen-09b\harkonnen09b-AI.lua = mods\d2k\maps\harkonnen-09b\harkonnen09b-AI.lua
|
||||
mods\d2k\maps\harkonnen-09b\harkonnen09b.lua = mods\d2k\maps\harkonnen-09b\harkonnen09b.lua
|
||||
mods\d2k\maps\ordos-01a\ordos01a.lua = mods\d2k\maps\ordos-01a\ordos01a.lua
|
||||
mods\d2k\maps\ordos-01b\ordos01b.lua = mods\d2k\maps\ordos-01b\ordos01b.lua
|
||||
mods\d2k\maps\ordos-02a\ordos02a-AI.lua = mods\d2k\maps\ordos-02a\ordos02a-AI.lua
|
||||
mods\d2k\maps\ordos-02a\ordos02a.lua = mods\d2k\maps\ordos-02a\ordos02a.lua
|
||||
mods\d2k\maps\ordos-02b\ordos02b-AI.lua = mods\d2k\maps\ordos-02b\ordos02b-AI.lua
|
||||
mods\d2k\maps\ordos-02b\ordos02b.lua = mods\d2k\maps\ordos-02b\ordos02b.lua
|
||||
mods\d2k\maps\ordos-03a\ordos03a-AI.lua = mods\d2k\maps\ordos-03a\ordos03a-AI.lua
|
||||
mods\d2k\maps\ordos-03a\ordos03a.lua = mods\d2k\maps\ordos-03a\ordos03a.lua
|
||||
mods\d2k\maps\ordos-03b\ordos03b-AI.lua = mods\d2k\maps\ordos-03b\ordos03b-AI.lua
|
||||
mods\d2k\maps\ordos-03b\ordos03b.lua = mods\d2k\maps\ordos-03b\ordos03b.lua
|
||||
mods\d2k\maps\ordos-04\ordos04-AI.lua = mods\d2k\maps\ordos-04\ordos04-AI.lua
|
||||
mods\d2k\maps\ordos-04\ordos04.lua = mods\d2k\maps\ordos-04\ordos04.lua
|
||||
mods\d2k\maps\ordos-05\ordos05-AI.lua = mods\d2k\maps\ordos-05\ordos05-AI.lua
|
||||
mods\d2k\maps\ordos-05\ordos05.lua = mods\d2k\maps\ordos-05\ordos05.lua
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "System Lua scripts", "System Lua scripts", "{A4D6AEA4-8009-4256-903B-8D227E50452B}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
lua\sandbox.lua = lua\sandbox.lua
|
||||
lua\scriptwrapper.lua = lua\scriptwrapper.lua
|
||||
lua\stacktraceplus.lua = lua\stacktraceplus.lua
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenRA.Test", "OpenRA.Test\OpenRA.Test.csproj", "{6CB8E1B7-6B36-4D93-8633-7C573E194AC4}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tiberian Sun Lua scripts", "Tiberian Sun Lua scripts", "{85159569-F5BD-458E-B5C0-EB16690C432B}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
mods\ts\maps\fields-of-green\fields-of-green.lua = mods\ts\maps\fields-of-green\fields-of-green.lua
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenRA.PostProcess", "OpenRA.PostProcess\OpenRA.PostProcess.csproj", "{EE63AF7E-92EA-48FB-81E2-53D7D92F8050}"
|
||||
EndProject
|
||||
Global
|
||||
@@ -201,7 +44,6 @@ Global
|
||||
{F33337BE-CB69-4B24-850F-07D23E408DDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F33337BE-CB69-4B24-850F-07D23E408DDF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F33337BE-CB69-4B24-850F-07D23E408DDF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F33337BE-CB69-4B24-850F-07D23E408DDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F33337BE-CB69-4B24-850F-07D23E408DDF}.Release-x86|Any CPU.ActiveCfg = Release-x86|Any CPU
|
||||
{F33337BE-CB69-4B24-850F-07D23E408DDF}.Release-x86|Any CPU.Build.0 = Release-x86|Any CPU
|
||||
{76F621A1-3D8E-4A99-9F7E-B071EB657817}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
||||
@@ -51,6 +51,7 @@ test_script:
|
||||
- nunit3-console OpenRA.Test.dll --result=myresults.xml;format=AppVeyor
|
||||
|
||||
after_test:
|
||||
- appveyor DownloadFile "https://download.ip2location.com/lite/IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP" -FileName IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP
|
||||
- appveyor DownloadFile "https://raw.githubusercontent.com/wiki/OpenRA/OpenRA/Changelog.md" -FileName Changelog.md
|
||||
- make docs
|
||||
- ps: dir *.md | % {gc $_ -Raw | .\ConvertFrom-Markdown.ps1 | Out-File -FilePath "$($_.Name.TrimEnd(".md")).html"}
|
||||
|
||||
21
fetch-geoip.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
# Download the IP2Location country database for use by the game server
|
||||
|
||||
####
|
||||
# This file must stay /bin/sh and POSIX compliant for macOS and BSD portability.
|
||||
# Copy-paste the entire script into http://shellcheck.net to check.
|
||||
####
|
||||
|
||||
# Set the working directory to the location of this script
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
|
||||
# Database does not exist or is older than 30 days.
|
||||
if [ -z "$(find . -path ./IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP -mtime -30 -print)" ]; then
|
||||
rm -f IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP
|
||||
echo "Downloading IP2Location GeoIP database."
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -s -L -O https://download.ip2location.com/lite/IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP || echo "Warning: Download failed"
|
||||
else
|
||||
wget -cq https://download.ip2location.com/lite/IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP || echo "Warning: Download failed"
|
||||
fi
|
||||
fi
|
||||
@@ -3,8 +3,6 @@
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
in vec4 vColor;
|
||||
|
||||
uniform sampler2D Texture0;
|
||||
uniform sampler2D Texture1;
|
||||
uniform sampler2D Texture2;
|
||||
@@ -18,6 +16,27 @@ uniform bool EnableDepthPreview;
|
||||
uniform float DepthTextureScale;
|
||||
uniform float AntialiasPixelsPerTexel;
|
||||
|
||||
#if __VERSION__ == 120
|
||||
varying vec4 vTexCoord;
|
||||
varying vec2 vTexMetadata;
|
||||
varying vec4 vChannelMask;
|
||||
varying vec4 vDepthMask;
|
||||
varying vec2 vTexSampler;
|
||||
|
||||
varying vec4 vColorFraction;
|
||||
varying vec4 vRGBAFraction;
|
||||
varying vec4 vPalettedFraction;
|
||||
|
||||
uniform vec2 Texture0Size;
|
||||
uniform vec2 Texture1Size;
|
||||
uniform vec2 Texture2Size;
|
||||
uniform vec2 Texture3Size;
|
||||
uniform vec2 Texture4Size;
|
||||
uniform vec2 Texture5Size;
|
||||
uniform vec2 Texture6Size;
|
||||
#else
|
||||
in vec4 vColor;
|
||||
|
||||
in vec4 vTexCoord;
|
||||
in vec2 vTexMetadata;
|
||||
in vec4 vChannelMask;
|
||||
@@ -29,6 +48,7 @@ in vec4 vRGBAFraction;
|
||||
in vec4 vPalettedFraction;
|
||||
|
||||
out vec4 fragColor;
|
||||
#endif
|
||||
|
||||
float jet_r(float x)
|
||||
{
|
||||
@@ -45,6 +65,43 @@ float jet_b(float x)
|
||||
return x < 0.3 ? 4.0 * x + 0.5 : -4.0 * x + 2.5;
|
||||
}
|
||||
|
||||
#if __VERSION__ == 120
|
||||
vec2 Size(float samplerIndex)
|
||||
{
|
||||
if (samplerIndex < 0.5)
|
||||
return Texture0Size;
|
||||
else if (samplerIndex < 1.5)
|
||||
return Texture1Size;
|
||||
else if (samplerIndex < 2.5)
|
||||
return Texture2Size;
|
||||
else if (samplerIndex < 3.5)
|
||||
return Texture3Size;
|
||||
else if (samplerIndex < 4.5)
|
||||
return Texture4Size;
|
||||
else if (samplerIndex < 5.5)
|
||||
return Texture5Size;
|
||||
|
||||
return Texture6Size;
|
||||
}
|
||||
|
||||
vec4 Sample(float samplerIndex, vec2 pos)
|
||||
{
|
||||
if (samplerIndex < 0.5)
|
||||
return texture2D(Texture0, pos);
|
||||
else if (samplerIndex < 1.5)
|
||||
return texture2D(Texture1, pos);
|
||||
else if (samplerIndex < 2.5)
|
||||
return texture2D(Texture2, pos);
|
||||
else if (samplerIndex < 3.5)
|
||||
return texture2D(Texture3, pos);
|
||||
else if (samplerIndex < 4.5)
|
||||
return texture2D(Texture4, pos);
|
||||
else if (samplerIndex < 5.5)
|
||||
return texture2D(Texture5, pos);
|
||||
|
||||
return texture2D(Texture6, pos);
|
||||
}
|
||||
#else
|
||||
ivec2 Size(float samplerIndex)
|
||||
{
|
||||
if (samplerIndex < 0.5)
|
||||
@@ -80,6 +137,7 @@ vec4 Sample(float samplerIndex, vec2 pos)
|
||||
|
||||
return texture(Texture6, pos);
|
||||
}
|
||||
#endif
|
||||
|
||||
vec4 SamplePalettedBilinear(float samplerIndex, vec2 coords, vec2 textureSize)
|
||||
{
|
||||
@@ -93,10 +151,17 @@ vec4 SamplePalettedBilinear(float samplerIndex, vec2 coords, vec2 textureSize)
|
||||
vec4 x3 = Sample(samplerIndex, tl + vec2(0., px.y));
|
||||
vec4 x4 = Sample(samplerIndex, tl + px);
|
||||
|
||||
#if __VERSION__ == 120
|
||||
vec4 c1 = texture2D(Palette, vec2(dot(x1, vChannelMask), vTexMetadata.s));
|
||||
vec4 c2 = texture2D(Palette, vec2(dot(x2, vChannelMask), vTexMetadata.s));
|
||||
vec4 c3 = texture2D(Palette, vec2(dot(x3, vChannelMask), vTexMetadata.s));
|
||||
vec4 c4 = texture2D(Palette, vec2(dot(x4, vChannelMask), vTexMetadata.s));
|
||||
#else
|
||||
vec4 c1 = texture(Palette, vec2(dot(x1, vChannelMask), vTexMetadata.s));
|
||||
vec4 c2 = texture(Palette, vec2(dot(x2, vChannelMask), vTexMetadata.s));
|
||||
vec4 c3 = texture(Palette, vec2(dot(x3, vChannelMask), vTexMetadata.s));
|
||||
vec4 c4 = texture(Palette, vec2(dot(x4, vChannelMask), vTexMetadata.s));
|
||||
#endif
|
||||
|
||||
return mix(mix(c1, c2, interp.x), mix(c3, c4, interp.x), interp.y);
|
||||
}
|
||||
@@ -127,7 +192,11 @@ void main()
|
||||
{
|
||||
vec4 x = Sample(vTexSampler.s, coords);
|
||||
vec2 p = vec2(dot(x, vChannelMask), vTexMetadata.s);
|
||||
#if __VERSION__ == 120
|
||||
c = vPalettedFraction * texture2D(Palette, p) + vRGBAFraction * x + vColorFraction * vTexCoord;
|
||||
#else
|
||||
c = vPalettedFraction * texture(Palette, p) + vRGBAFraction * x + vColorFraction * vTexCoord;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Discard any transparent fragments (both color and depth)
|
||||
@@ -150,8 +219,16 @@ void main()
|
||||
float r = clamp(jet_r(x), 0.0, 1.0);
|
||||
float g = clamp(jet_g(x), 0.0, 1.0);
|
||||
float b = clamp(jet_b(x), 0.0, 1.0);
|
||||
#if __VERSION__ == 120
|
||||
gl_FragColor = vec4(r, g, b, 1.0);
|
||||
#else
|
||||
fragColor = vec4(r, g, b, 1.0);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
#if __VERSION__ == 120
|
||||
gl_FragColor = c;
|
||||
#else
|
||||
fragColor = c;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -3,6 +3,21 @@
|
||||
uniform vec3 Scroll;
|
||||
uniform vec3 r1, r2;
|
||||
|
||||
#if __VERSION__ == 120
|
||||
attribute vec4 aVertexPosition;
|
||||
attribute vec4 aVertexTexCoord;
|
||||
attribute vec2 aVertexTexMetadata;
|
||||
|
||||
varying vec4 vTexCoord;
|
||||
varying vec2 vTexMetadata;
|
||||
varying vec4 vChannelMask;
|
||||
varying vec4 vDepthMask;
|
||||
varying vec2 vTexSampler;
|
||||
|
||||
varying vec4 vColorFraction;
|
||||
varying vec4 vRGBAFraction;
|
||||
varying vec4 vPalettedFraction;
|
||||
#else
|
||||
in vec4 aVertexPosition;
|
||||
in vec4 aVertexTexCoord;
|
||||
in vec2 aVertexTexMetadata;
|
||||
@@ -16,6 +31,7 @@ out vec2 vTexSampler;
|
||||
out vec4 vColorFraction;
|
||||
out vec4 vRGBAFraction;
|
||||
out vec4 vPalettedFraction;
|
||||
#endif
|
||||
|
||||
vec4 UnpackChannelAttributes(float x)
|
||||
{
|
||||
|
||||
@@ -9,13 +9,30 @@ uniform vec2 PaletteRows;
|
||||
uniform vec4 LightDirection;
|
||||
uniform vec3 AmbientLight, DiffuseLight;
|
||||
|
||||
#if __VERSION__ == 120
|
||||
varying vec4 vTexCoord;
|
||||
varying vec4 vChannelMask;
|
||||
varying vec4 vNormalsMask;
|
||||
#else
|
||||
in vec4 vTexCoord;
|
||||
in vec4 vChannelMask;
|
||||
in vec4 vNormalsMask;
|
||||
out vec4 fragColor;
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
#if __VERSION__ == 120
|
||||
vec4 x = texture2D(DiffuseTexture, vTexCoord.st);
|
||||
vec4 color = texture2D(Palette, vec2(dot(x, vChannelMask), PaletteRows.x));
|
||||
if (color.a < 0.01)
|
||||
discard;
|
||||
|
||||
vec4 y = texture2D(DiffuseTexture, vTexCoord.pq);
|
||||
vec4 normal = (2.0 * texture2D(Palette, vec2(dot(y, vNormalsMask), PaletteRows.y)) - 1.0);
|
||||
vec3 intensity = AmbientLight + DiffuseLight * max(dot(normal, LightDirection), 0.0);
|
||||
gl_FragColor = vec4(intensity * color.rgb, color.a);
|
||||
#else
|
||||
vec4 x = texture(DiffuseTexture, vTexCoord.st);
|
||||
vec4 color = texture(Palette, vec2(dot(x, vChannelMask), PaletteRows.x));
|
||||
if (color.a < 0.01)
|
||||
@@ -25,4 +42,5 @@ void main()
|
||||
vec4 normal = (2.0 * texture(Palette, vec2(dot(y, vNormalsMask), PaletteRows.y)) - 1.0);
|
||||
vec3 intensity = AmbientLight + DiffuseLight * max(dot(normal, LightDirection), 0.0);
|
||||
fragColor = vec4(intensity * color.rgb, color.a);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -3,12 +3,21 @@
|
||||
uniform mat4 View;
|
||||
uniform mat4 TransformMatrix;
|
||||
|
||||
#if __VERSION__ == 120
|
||||
attribute vec4 aVertexPosition;
|
||||
attribute vec4 aVertexTexCoord;
|
||||
attribute vec2 aVertexTexMetadata;
|
||||
varying vec4 vTexCoord;
|
||||
varying vec4 vChannelMask;
|
||||
varying vec4 vNormalsMask;
|
||||
#else
|
||||
in vec4 aVertexPosition;
|
||||
in vec4 aVertexTexCoord;
|
||||
in vec2 aVertexTexMetadata;
|
||||
out vec4 vTexCoord;
|
||||
out vec4 vChannelMask;
|
||||
out vec4 vNormalsMask;
|
||||
#endif
|
||||
|
||||
vec4 DecodeMask(float x)
|
||||
{
|
||||
|
||||
@@ -8,20 +8,19 @@ set ListenPort=1234
|
||||
set AdvertiseOnline=True
|
||||
set Password=""
|
||||
|
||||
set GeoIPDatabase=""
|
||||
|
||||
set RequireAuthentication=False
|
||||
set ProfileIDBlacklist=""
|
||||
set ProfileIDWhitelist=""
|
||||
|
||||
set EnableSingleplayer=False
|
||||
set EnableSyncReports=False
|
||||
set EnableGeoIP=True
|
||||
set ShareAnonymizedIPs=True
|
||||
|
||||
set SupportDir=""
|
||||
|
||||
:loop
|
||||
|
||||
OpenRA.Server.exe Game.Mod=%Mod% Server.Name=%Name% Server.ListenPort=%ListenPort% Server.AdvertiseOnline=%AdvertiseOnline% Server.EnableSingleplayer=%EnableSingleplayer% Server.Password=%Password% Server.GeoIPDatabase=%GeoIPDatabase% Server.RequireAuthentication=%RequireAuthentication% Server.ProfileIDBlacklist=%ProfileIDBlacklist% Server.ProfileIDWhitelist=%ProfileIDWhitelist% Server.EnableSyncReports=%EnableSyncReports% Server.ShareAnonymizedIPs=%ShareAnonymizedIPs% Engine.SupportDir=%SupportDir%
|
||||
OpenRA.Server.exe Game.Mod=%Mod% Server.Name=%Name% Server.ListenPort=%ListenPort% Server.AdvertiseOnline=%AdvertiseOnline% Server.EnableSingleplayer=%EnableSingleplayer% Server.Password=%Password% Server.RequireAuthentication=%RequireAuthentication% Server.ProfileIDBlacklist=%ProfileIDBlacklist% Server.ProfileIDWhitelist=%ProfileIDWhitelist% Server.EnableSyncReports=%EnableSyncReports% Server.EnableGeoIP=%EnableGeoIP% Server.ShareAnonymizedIPs=%ShareAnonymizedIPs% Engine.SupportDir=%SupportDir%
|
||||
|
||||
goto loop
|
||||
|
||||
@@ -12,14 +12,13 @@ ListenPort="${ListenPort:-"1234"}"
|
||||
AdvertiseOnline="${AdvertiseOnline:-"True"}"
|
||||
Password="${Password:-""}"
|
||||
|
||||
GeoIPDatabase="${GeoIPDatabase:-""}"
|
||||
|
||||
RequireAuthentication="${RequireAuthentication:-"False"}"
|
||||
ProfileIDBlacklist="${ProfileIDBlacklist:-""}"
|
||||
ProfileIDWhitelist="${ProfileIDWhitelist:-""}"
|
||||
|
||||
EnableSingleplayer="${EnableSingleplayer:-"False"}"
|
||||
EnableSyncReports="${EnableSyncReports:-"False"}"
|
||||
EnableGeoIP="${EnableGeoIP:-"True"}"
|
||||
ShareAnonymizedIPs="${ShareAnonymizedIPs:-"True"}"
|
||||
|
||||
SupportDir="${SupportDir:-""}"
|
||||
@@ -36,6 +35,7 @@ while true; do
|
||||
Server.ProfileIDBlacklist="$ProfileIDBlacklist" \
|
||||
Server.ProfileIDWhitelist="$ProfileIDWhitelist" \
|
||||
Server.EnableSyncReports="$EnableSyncReports" \
|
||||
Server.EnableGeoIP="$EnableGeoIP" \
|
||||
Server.ShareAnonymizedIPs="$ShareAnonymizedIPs" \
|
||||
Engine.SupportDir="$SupportDir"
|
||||
done
|
||||
|
||||
7
make.ps1
@@ -21,6 +21,13 @@ function All-Command
|
||||
{
|
||||
Write-Host "Build succeeded." -ForegroundColor Green
|
||||
}
|
||||
|
||||
if (!(Test-Path "IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP") -Or (((get-date) - (get-item "IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP").LastWriteTime) -gt (new-timespan -days 30)))
|
||||
{
|
||||
echo "Downloading IP2Location GeoIP database."
|
||||
$target = Join-Path $pwd.ToString() "IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP"
|
||||
(New-Object System.Net.WebClient).DownloadFile("https://download.ip2location.com/lite/IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP", $target)
|
||||
}
|
||||
}
|
||||
|
||||
function Clean-Command
|
||||
|
||||
@@ -388,7 +388,7 @@ sidebar-bits:
|
||||
production-tooltip-time: 904, 51, 16, 16
|
||||
production-tooltip-power: 870, 51, 16, 16
|
||||
production-tooltip-cost: 836, 51, 16, 16
|
||||
indicator-muted: 918, 221, 24, 24
|
||||
indicator-muted: 918, 221, 26, 24
|
||||
|
||||
vertical-bars:
|
||||
Inherits: ^Chrome
|
||||
|
||||
@@ -182,8 +182,10 @@ Container@SAVE_MAP_PANEL:
|
||||
Font: Bold
|
||||
|
||||
ScrollPanel@MAP_SAVE_VISIBILITY_PANEL:
|
||||
TopBottomSpacing: 5
|
||||
ItemSpacing: 5
|
||||
Width: 220
|
||||
Height: 64
|
||||
Height: 55
|
||||
Children:
|
||||
Checkbox@VISIBILITY_TEMPLATE:
|
||||
X: 5
|
||||
@@ -642,29 +644,30 @@ Container@EDITOR_WORLD_ROOT:
|
||||
Contrast: true
|
||||
|
||||
ScrollPanel@CATEGORY_FILTER_PANEL:
|
||||
Width: 190
|
||||
Width: 230
|
||||
TopBottomSpacing: 5
|
||||
ItemSpacing: 5
|
||||
Children:
|
||||
Container@SELECT_CATEGORIES_BUTTONS:
|
||||
Width: PARENT_RIGHT
|
||||
Height: 30
|
||||
Height: 25
|
||||
Children:
|
||||
Button@SELECT_ALL:
|
||||
X: 10
|
||||
Y: 2
|
||||
Width: 60
|
||||
Y: 0 - 5
|
||||
Width: 88
|
||||
Height: 25
|
||||
Text: All
|
||||
Button@SELECT_NONE:
|
||||
X: PARENT_RIGHT - WIDTH - 34
|
||||
Y: 2
|
||||
Width: 60
|
||||
X: 10 + 88 + 10
|
||||
Y: 0 - 5
|
||||
Width: 88
|
||||
Height: 25
|
||||
Text: None
|
||||
Checkbox@CATEGORY_TEMPLATE:
|
||||
X: 5
|
||||
Y: 5
|
||||
Width: PARENT_RIGHT - 29
|
||||
Height: 22
|
||||
Height: 20
|
||||
Visible: false
|
||||
|
||||
ScrollPanel@COPY_FILTER_PANEL:
|
||||
|
||||
@@ -148,7 +148,7 @@ Container@LOBBY_PLAYER_BIN:
|
||||
Height: 25
|
||||
Font: Regular
|
||||
Image@STATUS_IMAGE:
|
||||
X: 521
|
||||
X: 529
|
||||
Y: 4
|
||||
Width: 20
|
||||
Height: 20
|
||||
@@ -198,7 +198,6 @@ Container@LOBBY_PLAYER_BIN:
|
||||
Template: ANONYMOUS_PLAYER_TOOLTIP
|
||||
Label@NAME:
|
||||
X: 39
|
||||
Y: 0 - 1
|
||||
Width: 161
|
||||
Height: 25
|
||||
DropDownButton@PLAYER_ACTION:
|
||||
@@ -233,8 +232,8 @@ Container@LOBBY_PLAYER_BIN:
|
||||
Height: 25
|
||||
Children:
|
||||
Image@FACTIONFLAG:
|
||||
X: 5
|
||||
Y: 5
|
||||
X: 4
|
||||
Y: 4
|
||||
Width: 30
|
||||
Height: 15
|
||||
Label@FACTIONNAME:
|
||||
@@ -265,7 +264,7 @@ Container@LOBBY_PLAYER_BIN:
|
||||
Font: Regular
|
||||
Visible: false
|
||||
Image@STATUS_IMAGE:
|
||||
X: 527
|
||||
X: 529
|
||||
Y: 4
|
||||
Width: 20
|
||||
Height: 20
|
||||
@@ -287,7 +286,6 @@ Container@LOBBY_PLAYER_BIN:
|
||||
Visible: false
|
||||
Label@NAME:
|
||||
X: 20
|
||||
Y: 0 - 1
|
||||
Width: 195
|
||||
Height: 25
|
||||
Visible: false
|
||||
@@ -348,7 +346,7 @@ Container@LOBBY_PLAYER_BIN:
|
||||
Align: Center
|
||||
Font: Bold
|
||||
Image@STATUS_IMAGE:
|
||||
X: 521
|
||||
X: 529
|
||||
Y: 4
|
||||
Width: 20
|
||||
Height: 20
|
||||
@@ -398,7 +396,6 @@ Container@LOBBY_PLAYER_BIN:
|
||||
Template: ANONYMOUS_PLAYER_TOOLTIP
|
||||
Label@NAME:
|
||||
X: 39
|
||||
Y: 0 - 1
|
||||
Width: 161
|
||||
Height: 25
|
||||
DropDownButton@PLAYER_ACTION:
|
||||
|
||||
@@ -190,7 +190,13 @@ Container@MAINMENU_INTRODUCTION_PROMPT:
|
||||
Height: 16
|
||||
Font: Small
|
||||
Text: - Zoom the battlefield using {MODIFIER + Scroll Wheel}
|
||||
LabelWithHighlight@DESC_SCROLL:
|
||||
LabelWithHighlight@DESC_SCROLL_RIGHT:
|
||||
X: 265
|
||||
Y: 34
|
||||
Height: 16
|
||||
Font: Small
|
||||
Text: - Pan the battlefield using the {Right} mouse button
|
||||
LabelWithHighlight@DESC_SCROLL_MIDDLE:
|
||||
X: 265
|
||||
Y: 34
|
||||
Height: 16
|
||||
|
||||
@@ -228,13 +228,6 @@ Container@SETTINGS_PANEL:
|
||||
Height: 25
|
||||
MaxLength: 5
|
||||
Type: Integer
|
||||
Label@WINDOW_CHANGES_DESC:
|
||||
X: 60
|
||||
Y: 27
|
||||
Width: 200
|
||||
Height: 15
|
||||
Font: Tiny
|
||||
Text: Video mode and window size changes require restart
|
||||
Container@DISPLAY_SELECTION:
|
||||
Y: 240
|
||||
Children:
|
||||
@@ -249,13 +242,6 @@ Container@SETTINGS_PANEL:
|
||||
Width: 160
|
||||
Height: 25
|
||||
Font: Regular
|
||||
Label@MODE_CHANGES_DESC:
|
||||
X: 60
|
||||
Y: 27
|
||||
Width: 200
|
||||
Height: 15
|
||||
Font: Tiny
|
||||
Text: Video mode and display changes require restart
|
||||
Checkbox@VSYNC_CHECKBOX:
|
||||
X: 310
|
||||
Y: 210
|
||||
@@ -278,6 +264,27 @@ Container@SETTINGS_PANEL:
|
||||
Ticks: 20
|
||||
MinimumValue: 50
|
||||
MaximumValue: 240
|
||||
Label@GL_PROFILE:
|
||||
X: 15
|
||||
Y: 270
|
||||
Width: 120
|
||||
Height: 25
|
||||
Align: Right
|
||||
Text: OpenGL Profile:
|
||||
DropDownButton@GL_PROFILE_DROPDOWN:
|
||||
X: 140
|
||||
Y: 270
|
||||
Width: 160
|
||||
Height: 25
|
||||
Font: Regular
|
||||
Label@RESTART_REQUIRED_DESC:
|
||||
X: 300
|
||||
Y: PARENT_BOTTOM + 10
|
||||
Width: PARENT_RIGHT - 300
|
||||
Height: 15
|
||||
Font: TinyBold
|
||||
Text: Display and OpenGL changes require restart
|
||||
Align: Center
|
||||
Container@AUDIO_PANEL:
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM
|
||||
@@ -484,7 +491,12 @@ Container@SETTINGS_PANEL:
|
||||
Height: 16
|
||||
Font: Small
|
||||
Text: - Zoom the battlefield using {MODIFIER + Scroll Wheel}
|
||||
LabelWithHighlight@DESC_SCROLL:
|
||||
LabelWithHighlight@DESC_SCROLL_RIGHT:
|
||||
Y: 85
|
||||
Height: 16
|
||||
Font: Small
|
||||
Text: - Pan the battlefield using the {Right} mouse button
|
||||
LabelWithHighlight@DESC_SCROLL_MIDDLE:
|
||||
Y: 85
|
||||
Height: 16
|
||||
Font: Small
|
||||
@@ -537,13 +549,13 @@ Container@SETTINGS_PANEL:
|
||||
Height: 20
|
||||
Font: Regular
|
||||
Text: Screen Edge Panning
|
||||
Checkbox@CLASSIC_MOUSE_MIDDLE_SCROLL_CHECKBOX:
|
||||
Checkbox@ALTERNATE_SCROLL_CHECKBOX:
|
||||
X: 360
|
||||
Y: 133
|
||||
Width: 180
|
||||
Height: 20
|
||||
Font: Regular
|
||||
Text: Middle Mouse Panning
|
||||
Text: Alternate Mouse Panning
|
||||
Label@SCROLL_SPEED_LABEL:
|
||||
X: 310
|
||||
Y: 210
|
||||
@@ -634,7 +646,7 @@ Container@SETTINGS_PANEL:
|
||||
Width: 80
|
||||
Height: 25
|
||||
Align: Left
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
TooltipContainer: SETTINGS_TOOLTIP_CONTAINER
|
||||
Container@THREE_COLUMN:
|
||||
Width: 173
|
||||
Height: 25
|
||||
@@ -650,7 +662,7 @@ Container@SETTINGS_PANEL:
|
||||
Width: 80
|
||||
Height: 25
|
||||
Align: Left
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
TooltipContainer: SETTINGS_TOOLTIP_CONTAINER
|
||||
Background@HOTKEY_DIALOG_ROOT:
|
||||
X: 15
|
||||
Y: 230
|
||||
@@ -700,7 +712,7 @@ Container@SETTINGS_PANEL:
|
||||
Width: 25
|
||||
Height: 25
|
||||
TooltipText: Unbind the hotkey
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
TooltipContainer: SETTINGS_TOOLTIP_CONTAINER
|
||||
TooltipTemplate: SIMPLE_TOOLTIP
|
||||
Children:
|
||||
Image:
|
||||
@@ -715,7 +727,7 @@ Container@SETTINGS_PANEL:
|
||||
Width: 25
|
||||
Height: 25
|
||||
TooltipText: Reset to default
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
TooltipContainer: SETTINGS_TOOLTIP_CONTAINER
|
||||
TooltipTemplate: SIMPLE_TOOLTIP
|
||||
Children:
|
||||
Image@IMAGE_RELOAD:
|
||||
@@ -860,4 +872,4 @@ Container@SETTINGS_PANEL:
|
||||
Width: 140
|
||||
Height: 35
|
||||
Text: Reset
|
||||
TooltipContainer@TOOLTIP_CONTAINER:
|
||||
TooltipContainer@SETTINGS_TOOLTIP_CONTAINER:
|
||||
|
||||
@@ -405,6 +405,9 @@ Actors:
|
||||
Owner: Nod
|
||||
Facing: 192
|
||||
SubCell: 3
|
||||
Actor101: moneycrate
|
||||
Location: 56,23
|
||||
Owner: Neutral
|
||||
HandOfNod: hand
|
||||
Location: 30,53
|
||||
Owner: Nod
|
||||
|
||||
@@ -165,10 +165,8 @@ OBLI:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
MoneyCrate:
|
||||
Inherits: ^Crate
|
||||
GiveCashCrateAction:
|
||||
Amount: 500
|
||||
UseCashTick: true
|
||||
|
||||
airstrike.proxy:
|
||||
AirstrikePower:
|
||||
|
||||
@@ -483,6 +483,9 @@ Actors:
|
||||
Owner: Nod
|
||||
Facing: 160
|
||||
SubCell: 4
|
||||
Actor167: moneycrate
|
||||
Location: 26,42
|
||||
Owner: Neutral
|
||||
waypoint27: waypoint
|
||||
Location: 32,41
|
||||
Owner: Neutral
|
||||
|
||||
194
mods/cnc/maps/gdi08a/gdi08a-AI.lua
Normal file
@@ -0,0 +1,194 @@
|
||||
--[[
|
||||
Copyright 2007-2020 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.
|
||||
]]
|
||||
|
||||
AttackPaths = { WaypointGroup3, WaypointGroup4, WaypointGroup5 }
|
||||
NodBase = { handofnod, nodairfield, nodrefinery, NodYard, nodpower1, nodpower2, nodpower3, nodpower4, gun1, gun2, nodsilo1, nodsilo2, nodsilo3, nodsilo4}
|
||||
|
||||
PatrolProductionQueue = { }
|
||||
|
||||
InfantryAttackGroup = { }
|
||||
InfantryGroupSize = 5
|
||||
InfantryProductionCooldown = DateTime.Minutes(3)
|
||||
InfantryProductionTypes = { "e1", "e1", "e1", "e3", "e3", "e4" }
|
||||
HarvesterProductionType = { "harv" }
|
||||
|
||||
VehicleAttackGroup = { }
|
||||
VehicleGroupSize = 5
|
||||
VehicleProductionCooldown = DateTime.Minutes(3)
|
||||
VehicleProductionTypes = { "bggy", "bggy", "bike", "ltnk", "ltnk" }
|
||||
|
||||
StartingCash = 14000
|
||||
|
||||
BaseRefinery = { type = "proc", pos = CPos.New(24, 16), cost = 1500 }
|
||||
BaseGun1 = { type = "gun", pos = CPos.New( 21, 19), cost = 600 }
|
||||
BaseGun2 = { type = "gun", pos = CPos.New( 26, 21), cost = 600 }
|
||||
BaseNuke1 = { type = "nuke", pos = CPos.New( 23, 14), cost = 500 }
|
||||
BaseNuke2 = { type = "nuke", pos = CPos.New( 10, 9), cost = 500 }
|
||||
BaseNuke3 = { type = "nuke", pos = CPos.New( 6, 8), cost = 500 }
|
||||
BaseNuke4 = { type = "nuke", pos = CPos.New( 8, 8), cost = 500 }
|
||||
InfantryProduction = { type = "hand", pos = CPos.New(27, 17), cost = 500 }
|
||||
VehicleProduction = { type = "afld", pos = CPos.New(27, 14), cost = 2000 }
|
||||
|
||||
NodGuards = { Actor154, Actor155, Actor218, Actor219 }
|
||||
|
||||
BaseBuildings = { BaseRefinery, BaseNuke1, BaseNuke2, BaseNuke3, BaseNuke4, InfantryProduction, VehicleProduction, BaseGun1, BaseGun2 }
|
||||
|
||||
BuildBuilding = function(building, cyard)
|
||||
if CyardIsBuilding or Nod.Cash < building.cost then
|
||||
Trigger.AfterDelay(DateTime.Seconds(10), function() BuildBuilding(building, cyard) end)
|
||||
return
|
||||
end
|
||||
|
||||
CyardIsBuilding = true
|
||||
|
||||
Nod.Cash = Nod.Cash - building.cost
|
||||
Trigger.AfterDelay(Actor.BuildTime(building.type), function()
|
||||
CyardIsBuilding = false
|
||||
|
||||
if cyard.IsDead or cyard.Owner ~= Nod then
|
||||
Nod.Cash = Nod.Cash + building.cost
|
||||
return
|
||||
end
|
||||
|
||||
local actor = Actor.Create(building.type, true, { Owner = Nod, Location = building.pos })
|
||||
|
||||
if actor.Type == 'hand' or actor.Type == 'pyle' then
|
||||
Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceInfantry(actor) end)
|
||||
elseif actor.Type == 'afld' or actor.Type == 'weap' then
|
||||
Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceVehicle(actor) end)
|
||||
end
|
||||
|
||||
Trigger.OnKilled(actor, function()
|
||||
BuildBuilding(building, cyard)
|
||||
end)
|
||||
|
||||
RepairBuilding(GDI, actor, 0.75)
|
||||
end)
|
||||
end
|
||||
|
||||
CheckForHarvester = function()
|
||||
local harv = Nod.GetActorsByType("harv")
|
||||
return #harv > 0
|
||||
end
|
||||
|
||||
GuardBase = function()
|
||||
Utils.Do(NodBase, function(building)
|
||||
Trigger.OnDamaged(building, function()
|
||||
Utils.Do(NodGuards, function(guard)
|
||||
if not guard.IsDead and not building.IsDead then
|
||||
guard.Stop()
|
||||
guard.Guard(building)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
ProduceHarvester = function(building)
|
||||
if not buildingHarvester then
|
||||
buildingHarvester = true
|
||||
building.Build(HarvesterProductionType, function()
|
||||
buildingHarvester = false
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
ProduceInfantry = function(building)
|
||||
if building.IsDead or building.Owner ~= Nod then
|
||||
return
|
||||
elseif not CheckForHarvester() then
|
||||
Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceInfantry(building) end)
|
||||
return
|
||||
end
|
||||
|
||||
local delay = Utils.RandomInteger(DateTime.Seconds(3), DateTime.Seconds(9))
|
||||
local toBuild = { Utils.Random(InfantryProductionTypes) }
|
||||
local Path = Utils.Random(AttackPaths)
|
||||
building.Build(toBuild, function(unit)
|
||||
InfantryAttackGroup[#InfantryAttackGroup + 1] = unit[1]
|
||||
|
||||
if #InfantryAttackGroup >= InfantryGroupSize then
|
||||
MoveAndHunt(InfantryAttackGroup, Path)
|
||||
InfantryAttackGroup = { }
|
||||
Trigger.AfterDelay(InfantryProductionCooldown, function() ProduceInfantry(building) end)
|
||||
else
|
||||
Trigger.AfterDelay(delay, function() ProduceInfantry(building) end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
ProduceVehicle = function(building)
|
||||
if building.IsDead or building.Owner ~= Nod then
|
||||
return
|
||||
elseif not CheckForHarvester() then
|
||||
ProduceHarvester(building)
|
||||
Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceVehicle(building) end)
|
||||
return
|
||||
end
|
||||
|
||||
local delay = Utils.RandomInteger(DateTime.Seconds(12), DateTime.Seconds(17))
|
||||
local toBuild = { Utils.Random(VehicleProductionTypes) }
|
||||
local Path = Utils.Random(AttackPaths)
|
||||
building.Build(toBuild, function(unit)
|
||||
VehicleAttackGroup[#VehicleAttackGroup + 1] = unit[1]
|
||||
|
||||
if #VehicleAttackGroup >= VehicleGroupSize then
|
||||
MoveAndHunt(VehicleAttackGroup, Path)
|
||||
VehicleAttackGroup = { }
|
||||
Trigger.AfterDelay(VehicleProductionCooldown, function() ProduceVehicle(building) end)
|
||||
else
|
||||
Trigger.AfterDelay(delay, function() ProduceVehicle(building) end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
StartAI = function()
|
||||
Nod.Cash = StartingCash
|
||||
GuardBase()
|
||||
end
|
||||
|
||||
Trigger.OnAllKilledOrCaptured(NodBase, function()
|
||||
Utils.Do(Nod.GetGroundAttackers(), IdleHunt)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(nodrefinery, function()
|
||||
BuildBuilding(BaseRefinery, NodYard)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(nodpower1, function()
|
||||
BuildBuilding(BaseNuke1, NodYard)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(nodpower2, function()
|
||||
BuildBuilding(BaseNuke2, NodYard)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(nodpower3, function()
|
||||
BuildBuilding(BaseNuke3, NodYard)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(nodpower4, function()
|
||||
BuildBuilding(BaseNuke4, NodYard)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(gun1, function()
|
||||
BuildBuilding(BaseGun1, NodYard)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(gun2, function()
|
||||
BuildBuilding(BaseGun2, NodYard)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(handofnod, function()
|
||||
BuildBuilding(InfantryProduction, NodYard)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(nodairfield, function()
|
||||
BuildBuilding(VehicleProduction, NodYard)
|
||||
end)
|
||||
152
mods/cnc/maps/gdi08a/gdi08a.lua
Normal file
@@ -0,0 +1,152 @@
|
||||
--[[
|
||||
Copyright 2007-2020 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.
|
||||
]]
|
||||
|
||||
SamSites = { sam1, sam2, sam3 }
|
||||
|
||||
WaypointGroup1 = { waypoint4, waypoint5, waypoint6, waypoint7, waypoint10, waypoint11, waypoint12 }
|
||||
WaypointGroup2 = { waypoint4, waypoint5, waypoint13, waypoint16 }
|
||||
WaypointGroup3 = { waypoint4, waypoint5, waypoint6, waypoint8 }
|
||||
WaypointGroup4 = { waypoint4, waypoint5, waypoint6, waypoint7, waypoint9 }
|
||||
WaypointGroup5 = { waypoint4, waypoint5, waypoint6 }
|
||||
WaypointGroup6 = { waypoint4, waypoint5, waypoint6, waypoint7, waypoint14, waypoint15 }
|
||||
WaypointGroup7 = { waypoint4, waypoint5 }
|
||||
WaypointGroupCiv = { waypoint0, waypoint1, waypoint2, waypoint3 }
|
||||
|
||||
Atk1 = { units = { ['ltnk'] = 1 }, waypoints = WaypointGroup1, delay = 0 }
|
||||
Atk2 = { units = { ['ltnk'] = 1 }, waypoints = WaypointGroup2, delay = 0 }
|
||||
Civ1 = { units = { ['c3'] = 1 }, waypoints = WaypointGroupCiv, delay = 0 }
|
||||
Nod1 = { units = { ['e1'] = 2, ['e2'] = 2, ['e4'] = 2 }, waypoints = WaypointGroup3, delay = 90 }
|
||||
Nod2 = { units = { ['e3'] = 2, ['e4'] = 2 }, waypoints = WaypointGroup3, delay = 130 }
|
||||
Nod3 = { units = { ['e1'] = 2, ['e3'] = 3 }, waypoints = WaypointGroup3, delay = 50 }
|
||||
Nod4 = { units = { ['bggy'] = 2 }, waypoints = WaypointGroup3, delay = 200 }
|
||||
Nod5 = { units = { ['e4'] = 2, ['ltnk'] = 1 }, waypoints = WaypointGroup1, delay = 250 }
|
||||
Nod6 = { units = { ['arty'] = 1 }, waypoints = WaypointGroup4, delay = 40 }
|
||||
Nod7 = { units = { ['e3'] = 2, ['e4'] = 2 }, waypoints = WaypointGroup4, delay = 40 }
|
||||
Nod8 = { units = { ['ltnk'] = 1, ['bggy'] = 1 }, waypoints = WaypointGroup3, delay = 170 }
|
||||
Auto1 = { units = { ['e1'] = 2, ['e2'] = 2 }, waypoints = WaypointGroup5, delay = 50 }
|
||||
Auto2 = { units = { ['e3'] = 3, ['e4'] = 2 }, waypoints = WaypointGroup3, delay = 50 }
|
||||
Auto3 = { units = { ['ltnk'] = 1, ['bggy'] = 1 }, waypoints = WaypointGroup3, delay = 50 }
|
||||
Auto4 = { units = { ['bggy'] = 2 }, waypoints = WaypointGroup7, delay = 50 }
|
||||
Auto5 = { units = { ['ltnk'] = 1 }, waypoints = WaypointGroup6, delay = 50 }
|
||||
Auto6 = { units = { ['arty'] = 1 }, waypoints = WaypointGroup4, delay = 50 }
|
||||
Auto7 = { units = { ['e3'] = 3, ['e4'] = 2 }, waypoints = WaypointGroup6, delay = 50 }
|
||||
|
||||
AutoAttackWaves = { Atk1, Atk2, Nod1, Nod2, Nod3, Nod4, Nod5, Nod6, Nod7, Nod8, Auto1, Auto2, Auto3, Auto4, Auto5, Auto6, Auto7 }
|
||||
|
||||
StationaryGuardUnits = { Actor181, Actor182, Actor183, Actor184, Actor198, Actor199, Actor157, Actor175, Actor176, Actor173, Actor174, Actor158, Actor200, Actor159, Actor179, Actor180, Actor184, Actor185, Actor216, Actor217, Actor153, Actor215, Actor214, Actor213}
|
||||
|
||||
DamagedGDIAssets = { Actor126, Actor127, Actor128, Actor129, Actor130,Actor131, Actor132, Actor133, Actor134, Actor135, Actor136, Actor137, Actor138, Actor160, Actor161, Actor162, Actor163, Actor164, Actor165, Actor166, Actor168, Actor169, Actor170}
|
||||
|
||||
StartStationaryGuards = function(StationaryGuards)
|
||||
Utils.Do(StationaryGuards, function(unit)
|
||||
if not unit.IsDead then
|
||||
unit.Patrol( { unit.Location } , true, 20)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
SendWaves = function(counter, Waves)
|
||||
if counter <= #Waves then
|
||||
local team = Waves[counter]
|
||||
|
||||
for type, amount in pairs(team.units) do
|
||||
MoveAndHunt(Utils.Take(amount, Nod.GetActorsByType(type)), team.waypoints)
|
||||
end
|
||||
|
||||
Trigger.AfterDelay(DateTime.Seconds(team.delay), function() SendWaves(counter + 1, Waves) end)
|
||||
end
|
||||
end
|
||||
|
||||
SendAttackWave = function(team)
|
||||
for type, amount in pairs(team.units) do
|
||||
count = 0
|
||||
local actors = Nod.GetActorsByType(type)
|
||||
Utils.Do(actors, function(actor)
|
||||
if actor.IsIdle and count < amount then
|
||||
SetAttackWaypoints(actor, team.waypoints)
|
||||
IdleHunt(actor)
|
||||
count = count + 1
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
SetAttackWaypoints = function(actor, waypoints)
|
||||
if not actor.IsDead then
|
||||
Utils.Do(waypoints, function(waypoint)
|
||||
actor.AttackMove(waypoint.Location)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
CeckRepairGDIAssetsObjective = function()
|
||||
local failed = false
|
||||
local repaired = true
|
||||
Utils.Do(DamagedGDIAssets, function(actor)
|
||||
if actor.IsDead then
|
||||
failed = true
|
||||
elseif actor.Health < actor.MaxHealth then
|
||||
repaired = false
|
||||
end
|
||||
end)
|
||||
|
||||
if failed then
|
||||
GDI.MarkFailedObjective(RepairAssets)
|
||||
return
|
||||
elseif repaired then
|
||||
GDI.MarkCompletedObjective(RepairAssets)
|
||||
return
|
||||
end
|
||||
Trigger.AfterDelay(DateTime.Seconds(3), function() CeckRepairGDIAssetsObjective() end)
|
||||
end
|
||||
|
||||
WorldLoaded = function()
|
||||
GDI = Player.GetPlayer("GDI")
|
||||
Nod = Player.GetPlayer("Nod")
|
||||
|
||||
Camera.Position = DefaultCameraPosition.CenterPosition
|
||||
|
||||
StartStationaryGuards(StationaryGuardUnits)
|
||||
|
||||
StartAI()
|
||||
|
||||
InitObjectives(GDI)
|
||||
|
||||
SecureArea = GDI.AddObjective("Destroy the Nod strike force.")
|
||||
KillGDI = Nod.AddObjective("Kill all enemies!")
|
||||
|
||||
RepairAssets = GDI.AddObjective("Repair GDI base and vehicles.", "Secondary", false)
|
||||
Trigger.AfterDelay(DateTime.Seconds(5), function() CeckRepairGDIAssetsObjective() end)
|
||||
|
||||
AirSupport = GDI.AddObjective("Destroy the SAM sites to receive air support.", "Secondary", false)
|
||||
Trigger.OnAllKilled(SamSites, function()
|
||||
GDI.MarkCompletedObjective(AirSupport)
|
||||
Actor.Create("airstrike.proxy", true, { Owner = GDI })
|
||||
end)
|
||||
|
||||
local InitialArrivingUnits = { Actor166 }
|
||||
Utils.Do(InitialArrivingUnits, function(unit)
|
||||
unit.Move(unit.Location + CVec.New(1, 1), 0)
|
||||
end)
|
||||
|
||||
Trigger.AfterDelay(DateTime.Minutes(1), function() SendWaves(1, AutoAttackWaves) end)
|
||||
Trigger.AfterDelay(DateTime.Minutes(2), function() ProduceInfantry(handofnod) end)
|
||||
Trigger.AfterDelay(DateTime.Minutes(2), function() ProduceVehicle(nodairfield) end)
|
||||
end
|
||||
|
||||
Tick = function()
|
||||
if DateTime.GameTime > DateTime.Seconds(5) then
|
||||
if GDI.HasNoRequiredUnits() then
|
||||
Nod.MarkCompletedObjective(KillGDI)
|
||||
end
|
||||
if Nod.HasNoRequiredUnits() then
|
||||
GDI.MarkCompletedObjective(SecureArea)
|
||||
end
|
||||
end
|
||||
end
|
||||
BIN
mods/cnc/maps/gdi08a/map.bin
Normal file
BIN
mods/cnc/maps/gdi08a/map.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
889
mods/cnc/maps/gdi08a/map.yaml
Normal file
@@ -0,0 +1,889 @@
|
||||
MapFormat: 11
|
||||
|
||||
RequiresMod: cnc
|
||||
|
||||
Title: 08a: Restore GDI Presence Near Salzburg
|
||||
|
||||
Author: Westwood Studios
|
||||
|
||||
Tileset: WINTER
|
||||
|
||||
MapSize: 64,64
|
||||
|
||||
Bounds: 3,7,51,50
|
||||
|
||||
Visibility: MissionSelector
|
||||
|
||||
Categories: Campaign
|
||||
|
||||
LockPreview: True
|
||||
|
||||
Players:
|
||||
PlayerReference@Neutral:
|
||||
Name: Neutral
|
||||
OwnsWorld: True
|
||||
NonCombatant: True
|
||||
Faction: gdi
|
||||
PlayerReference@Nod:
|
||||
Name: Nod
|
||||
Faction: nod
|
||||
Color: FF1400
|
||||
Allies: Nod
|
||||
Enemies: GDI
|
||||
Bot: campaign
|
||||
PlayerReference@GDI:
|
||||
Name: GDI
|
||||
AllowBots: False
|
||||
Playable: True
|
||||
Required: True
|
||||
LockFaction: True
|
||||
Faction: gdi
|
||||
LockColor: True
|
||||
Color: F5D378
|
||||
LockSpawn: True
|
||||
LockTeam: True
|
||||
Allies: GDI
|
||||
Enemies: Nod
|
||||
|
||||
Actors:
|
||||
Actor0: sbag
|
||||
Location: 53,56
|
||||
Owner: Neutral
|
||||
Actor1: sbag
|
||||
Location: 52,56
|
||||
Owner: Neutral
|
||||
Actor2: sbag
|
||||
Location: 51,56
|
||||
Owner: Neutral
|
||||
Actor3: sbag
|
||||
Location: 50,56
|
||||
Owner: Neutral
|
||||
Actor4: sbag
|
||||
Location: 49,56
|
||||
Owner: Neutral
|
||||
Actor5: sbag
|
||||
Location: 48,56
|
||||
Owner: Neutral
|
||||
Actor6: sbag
|
||||
Location: 47,56
|
||||
Owner: Neutral
|
||||
Actor7: sbag
|
||||
Location: 46,56
|
||||
Owner: Neutral
|
||||
Actor8: sbag
|
||||
Location: 45,56
|
||||
Owner: Neutral
|
||||
Actor9: sbag
|
||||
Location: 44,56
|
||||
Owner: Neutral
|
||||
Actor10: sbag
|
||||
Location: 43,56
|
||||
Owner: Neutral
|
||||
Actor11: sbag
|
||||
Location: 41,56
|
||||
Owner: Neutral
|
||||
Actor12: sbag
|
||||
Location: 40,56
|
||||
Owner: Neutral
|
||||
Actor13: wood
|
||||
Location: 5,56
|
||||
Owner: Neutral
|
||||
Actor14: wood
|
||||
Location: 4,56
|
||||
Owner: Neutral
|
||||
Actor15: wood
|
||||
Location: 3,56
|
||||
Owner: Neutral
|
||||
Actor16: sbag
|
||||
Location: 53,55
|
||||
Owner: Neutral
|
||||
Actor17: sbag
|
||||
Location: 40,55
|
||||
Owner: Neutral
|
||||
Actor18: v18
|
||||
Location: 5,55
|
||||
Owner: Neutral
|
||||
Actor19: v17
|
||||
Location: 4,55
|
||||
Owner: Neutral
|
||||
Actor20: sbag
|
||||
Location: 53,54
|
||||
Owner: Neutral
|
||||
Actor21: sbag
|
||||
Location: 40,54
|
||||
Owner: Neutral
|
||||
Actor22: wood
|
||||
Location: 17,54
|
||||
Owner: Neutral
|
||||
Actor23: wood
|
||||
Location: 16,54
|
||||
Owner: Neutral
|
||||
Actor24: wood
|
||||
Location: 15,54
|
||||
Owner: Neutral
|
||||
Actor25: wood
|
||||
Location: 14,54
|
||||
Owner: Neutral
|
||||
Actor26: wood
|
||||
Location: 11,54
|
||||
Owner: Neutral
|
||||
Actor27: wood
|
||||
Location: 10,54
|
||||
Owner: Neutral
|
||||
Actor28: v14
|
||||
Location: 6,54
|
||||
Owner: Neutral
|
||||
Actor29: v15
|
||||
Location: 5,54
|
||||
Owner: Neutral
|
||||
Actor30: v16
|
||||
Location: 4,54
|
||||
Owner: Neutral
|
||||
Actor31: sbag
|
||||
Location: 53,53
|
||||
Owner: Neutral
|
||||
Actor32: wood
|
||||
Location: 6,53
|
||||
Owner: Neutral
|
||||
Actor33: wood
|
||||
Location: 17,52
|
||||
Owner: Neutral
|
||||
Actor34: wood
|
||||
Location: 16,52
|
||||
Owner: Neutral
|
||||
Actor35: wood
|
||||
Location: 15,52
|
||||
Owner: Neutral
|
||||
Actor36: wood
|
||||
Location: 14,52
|
||||
Owner: Neutral
|
||||
Actor37: wood
|
||||
Location: 11,52
|
||||
Owner: Neutral
|
||||
Actor38: wood
|
||||
Location: 10,52
|
||||
Owner: Neutral
|
||||
Actor39: sbag
|
||||
Location: 53,51
|
||||
Owner: Neutral
|
||||
Actor40: sbag
|
||||
Location: 53,50
|
||||
Owner: Neutral
|
||||
Actor41: sbag
|
||||
Location: 40,50
|
||||
Owner: Neutral
|
||||
Actor42: sbag
|
||||
Location: 53,49
|
||||
Owner: Neutral
|
||||
Actor43: sbag
|
||||
Location: 40,49
|
||||
Owner: Neutral
|
||||
Actor44: sbag
|
||||
Location: 53,48
|
||||
Owner: Neutral
|
||||
Actor45: sbag
|
||||
Location: 53,47
|
||||
Owner: Neutral
|
||||
Actor46: sbag
|
||||
Location: 40,47
|
||||
Owner: Neutral
|
||||
Actor47: sbag
|
||||
Location: 53,46
|
||||
Owner: Neutral
|
||||
Actor48: sbag
|
||||
Location: 52,46
|
||||
Owner: Neutral
|
||||
Actor49: sbag
|
||||
Location: 51,46
|
||||
Owner: Neutral
|
||||
Actor50: sbag
|
||||
Location: 50,46
|
||||
Owner: Neutral
|
||||
Actor51: sbag
|
||||
Location: 49,46
|
||||
Owner: Neutral
|
||||
Actor52: sbag
|
||||
Location: 48,46
|
||||
Owner: Neutral
|
||||
Actor53: sbag
|
||||
Location: 42,46
|
||||
Owner: Neutral
|
||||
Actor54: sbag
|
||||
Location: 41,46
|
||||
Owner: Neutral
|
||||
Actor55: sbag
|
||||
Location: 40,46
|
||||
Owner: Neutral
|
||||
Actor56: t01
|
||||
Location: 33,32
|
||||
Owner: Neutral
|
||||
Actor57: t01
|
||||
Location: 33,34
|
||||
Owner: Neutral
|
||||
Actor58: tc01
|
||||
Location: 47,27
|
||||
Owner: Neutral
|
||||
Actor59: t01
|
||||
Location: 42,18
|
||||
Owner: Neutral
|
||||
Actor60: t01
|
||||
Location: 41,16
|
||||
Owner: Neutral
|
||||
Actor61: t01
|
||||
Location: 36,18
|
||||
Owner: Neutral
|
||||
Actor62: t01
|
||||
Location: 15,50
|
||||
Owner: Neutral
|
||||
Actor63: t01
|
||||
Location: 6,54
|
||||
Owner: Neutral
|
||||
Actor64: t01
|
||||
Location: 11,48
|
||||
Owner: Neutral
|
||||
Actor65: tc01
|
||||
Location: 14,33
|
||||
Owner: Neutral
|
||||
Actor66: tc04
|
||||
Location: 17,35
|
||||
Owner: Neutral
|
||||
Actor67: tc01
|
||||
Location: 9,19
|
||||
Owner: Neutral
|
||||
Actor68: tc04
|
||||
Location: 11,20
|
||||
Owner: Neutral
|
||||
Actor69: t06
|
||||
Location: 37,8
|
||||
Owner: Neutral
|
||||
Actor70: t03
|
||||
Location: 33,8
|
||||
Owner: Neutral
|
||||
Actor71: t02
|
||||
Location: 36,9
|
||||
Owner: Neutral
|
||||
Actor72: t01
|
||||
Location: 32,7
|
||||
Owner: Neutral
|
||||
Actor73: tc04
|
||||
Location: 50,9
|
||||
Owner: Neutral
|
||||
Actor74: tc01
|
||||
Location: 45,7
|
||||
Owner: Neutral
|
||||
Actor75: tc01
|
||||
Location: 38,13
|
||||
Owner: Neutral
|
||||
Actor76: tc02
|
||||
Location: 35,22
|
||||
Owner: Neutral
|
||||
Actor77: tc05
|
||||
Location: 32,21
|
||||
Owner: Neutral
|
||||
Actor78: t10
|
||||
Location: 17,47
|
||||
Owner: Neutral
|
||||
Actor79: t13
|
||||
Location: 16,50
|
||||
Owner: Neutral
|
||||
Actor80: tc01
|
||||
Location: 15,48
|
||||
Owner: Neutral
|
||||
Actor81: tc02
|
||||
Location: 6,51
|
||||
Owner: Neutral
|
||||
Actor82: tc02
|
||||
Location: 3,47
|
||||
Owner: Neutral
|
||||
Actor83: tc04
|
||||
Location: 3,51
|
||||
Owner: Neutral
|
||||
Actor84: tc04
|
||||
Location: 5,47
|
||||
Owner: Neutral
|
||||
Actor85: tc04
|
||||
Location: 9,54
|
||||
Owner: Neutral
|
||||
Actor86: tc04
|
||||
Location: 3,40
|
||||
Owner: Neutral
|
||||
Actor87: t13
|
||||
Location: 4,42
|
||||
Owner: Neutral
|
||||
Actor88: t11
|
||||
Location: 10,12
|
||||
Owner: Neutral
|
||||
Actor89: t15
|
||||
Location: 4,10
|
||||
Owner: Neutral
|
||||
Actor90: t16
|
||||
Location: 3,9
|
||||
Owner: Neutral
|
||||
Actor91: tc02
|
||||
Location: 5,12
|
||||
Owner: Neutral
|
||||
Actor92: tc01
|
||||
Location: 4,7
|
||||
Owner: Neutral
|
||||
Actor93: tc04
|
||||
Location: 20,11
|
||||
Owner: Neutral
|
||||
Actor94: tc04
|
||||
Location: 19,47
|
||||
Owner: Neutral
|
||||
Actor95: tc05
|
||||
Location: 20,49
|
||||
Owner: Neutral
|
||||
Actor96: tc04
|
||||
Location: 40,17
|
||||
Owner: Neutral
|
||||
Actor97: tc01
|
||||
Location: 44,36
|
||||
Owner: Neutral
|
||||
Actor98: tc04
|
||||
Location: 39,37
|
||||
Owner: Neutral
|
||||
Actor99: tc02
|
||||
Location: 40,30
|
||||
Owner: Neutral
|
||||
Actor100: tc02
|
||||
Location: 30,32
|
||||
Owner: Neutral
|
||||
Actor101: t10
|
||||
Location: 32,33
|
||||
Owner: Neutral
|
||||
Actor102: tc04
|
||||
Location: 37,54
|
||||
Owner: Neutral
|
||||
Actor103: tc02
|
||||
Location: 34,53
|
||||
Owner: Neutral
|
||||
Actor104: t01
|
||||
Location: 31,53
|
||||
Owner: Neutral
|
||||
Actor105: t01
|
||||
Location: 16,54
|
||||
Owner: Neutral
|
||||
Actor106: t02
|
||||
Location: 18,55
|
||||
Owner: Neutral
|
||||
Actor107: t11
|
||||
Location: 25,32
|
||||
Owner: Neutral
|
||||
Actor108: t11
|
||||
Location: 41,44
|
||||
Owner: Neutral
|
||||
Actor109: tc01
|
||||
Location: 51,44
|
||||
Owner: Neutral
|
||||
Actor110: tc04
|
||||
Location: 27,40
|
||||
Owner: Neutral
|
||||
Actor111: tc05
|
||||
Location: 30,42
|
||||
Owner: Neutral
|
||||
Actor112: t07
|
||||
Location: 31,39
|
||||
Owner: Neutral
|
||||
Actor113: t06
|
||||
Location: 26,42
|
||||
Owner: Neutral
|
||||
Actor114: t03
|
||||
Location: 28,43
|
||||
Owner: Neutral
|
||||
sam1: sam
|
||||
Location: 11,20
|
||||
Owner: Nod
|
||||
sam2: sam
|
||||
Location: 33,18
|
||||
Owner: Nod
|
||||
sam3: sam
|
||||
Location: 16,7
|
||||
Owner: Nod
|
||||
Actor118: v05
|
||||
Location: 17,49
|
||||
Owner: Neutral
|
||||
Actor119: v07
|
||||
Location: 9,49
|
||||
Owner: Neutral
|
||||
Actor120: v06
|
||||
Location: 4,53
|
||||
Owner: Neutral
|
||||
Actor121: v05
|
||||
Location: 6,49
|
||||
Owner: Neutral
|
||||
Actor122: v04
|
||||
Location: 4,50
|
||||
Owner: Neutral
|
||||
Actor123: v03
|
||||
Location: 10,50
|
||||
Owner: Neutral
|
||||
Actor124: v01
|
||||
Location: 3,48
|
||||
Owner: Neutral
|
||||
Actor125: v06
|
||||
Location: 20,49
|
||||
Owner: Neutral
|
||||
Actor126: gtwr
|
||||
Location: 41,54
|
||||
Owner: GDI
|
||||
Health: 41
|
||||
Actor127: gtwr
|
||||
Location: 41,50
|
||||
Owner: GDI
|
||||
Health: 16
|
||||
Actor128: hq
|
||||
Location: 51,47
|
||||
Owner: GDI
|
||||
Health: 39
|
||||
Actor129: proc
|
||||
Location: 44,46
|
||||
Owner: GDI
|
||||
Health: 33
|
||||
FreeActor: False
|
||||
Actor130: silo
|
||||
Location: 49,54
|
||||
Owner: GDI
|
||||
Health: 53
|
||||
Actor131: silo
|
||||
Location: 47,47
|
||||
Owner: GDI
|
||||
Health: 20
|
||||
Actor132: silo
|
||||
Location: 47,53
|
||||
Owner: GDI
|
||||
Health: 13
|
||||
Actor133: silo
|
||||
Location: 51,54
|
||||
Owner: GDI
|
||||
Health: 28
|
||||
Actor134: nuke
|
||||
Location: 49,51
|
||||
Owner: GDI
|
||||
Health: 14
|
||||
Actor135: nuke
|
||||
Location: 49,48
|
||||
Owner: GDI
|
||||
Health: 20
|
||||
Actor136: nuke
|
||||
Location: 51,50
|
||||
Owner: GDI
|
||||
Health: 35
|
||||
Actor137: pyle
|
||||
Location: 47,49
|
||||
Owner: GDI
|
||||
Health: 18
|
||||
Actor138: fix
|
||||
Location: 44,52
|
||||
Owner: GDI
|
||||
Health: 28
|
||||
gun1: gun
|
||||
Location: 21,19
|
||||
Owner: Nod
|
||||
NodYard: fact
|
||||
Location: 7,11
|
||||
Owner: Nod
|
||||
nodrefinery: proc
|
||||
Location: 24,16
|
||||
Owner: Nod
|
||||
FreeActor: False
|
||||
nodsilo1: silo
|
||||
Location: 29,17
|
||||
Owner: Nod
|
||||
nodsilo2: silo
|
||||
Location: 9,7
|
||||
Owner: Nod
|
||||
nodsilo3: silo
|
||||
Location: 12,9
|
||||
Owner: Nod
|
||||
nodsilo4: silo
|
||||
Location: 11,7
|
||||
Owner: Nod
|
||||
nodairfield: afld
|
||||
Location: 27,14
|
||||
Owner: Nod
|
||||
nodpower1: nuke
|
||||
Location: 23,14
|
||||
Owner: Nod
|
||||
nodpower2: nuke
|
||||
Location: 10,9
|
||||
Owner: Nod
|
||||
nodpower3: nuke
|
||||
Location: 6,8
|
||||
Owner: Nod
|
||||
nodpower4: nuke
|
||||
Location: 8,8
|
||||
Owner: Nod
|
||||
handofnod: hand
|
||||
Location: 27,17
|
||||
Owner: Nod
|
||||
gun2: gun
|
||||
Location: 26,21
|
||||
Owner: Nod
|
||||
Actor153: arty
|
||||
Location: 4,12
|
||||
Owner: Nod
|
||||
Facing: 127
|
||||
Actor154: bggy
|
||||
Location: 20,18
|
||||
Owner: Nod
|
||||
Actor155: ltnk
|
||||
Location: 19,18
|
||||
Owner: Nod
|
||||
Actor156: bggy
|
||||
Location: 10,21
|
||||
Owner: Nod
|
||||
Facing: 95
|
||||
Actor157: ltnk
|
||||
Location: 40,35
|
||||
Owner: Nod
|
||||
Facing: 159
|
||||
Actor158: ltnk
|
||||
Location: 45,23
|
||||
Owner: Nod
|
||||
Facing: 95
|
||||
Actor159: ltnk
|
||||
Location: 41,14
|
||||
Owner: Nod
|
||||
Facing: 159
|
||||
Actor160: mtnk
|
||||
Location: 42,55
|
||||
Owner: GDI
|
||||
Health: 35
|
||||
Facing: 223
|
||||
Actor161: mtnk
|
||||
Location: 33,52
|
||||
Owner: GDI
|
||||
Health: 43
|
||||
Facing: 31
|
||||
Actor162: apc
|
||||
Location: 37,50
|
||||
Owner: GDI
|
||||
Health: 68
|
||||
Facing: 127
|
||||
Actor163: apc
|
||||
Location: 39,48
|
||||
Owner: GDI
|
||||
Health: 45
|
||||
Facing: 127
|
||||
Actor164: msam
|
||||
Location: 42,49
|
||||
Owner: GDI
|
||||
Health: 40
|
||||
Facing: 95
|
||||
Actor165: msam
|
||||
Location: 41,51
|
||||
Owner: GDI
|
||||
Health: 50
|
||||
Facing: 159
|
||||
Actor166: msam
|
||||
Location: 33,48
|
||||
Owner: GDI
|
||||
Health: 65
|
||||
Facing: 159
|
||||
Actor167: harv
|
||||
Location: 23,23
|
||||
Owner: Nod
|
||||
Facing: 95
|
||||
Actor168: harv
|
||||
Location: 38,53
|
||||
Owner: GDI
|
||||
Health: 51
|
||||
Facing: 31
|
||||
Actor169: jeep
|
||||
Location: 36,52
|
||||
Owner: GDI
|
||||
Health: 72
|
||||
Facing: 95
|
||||
Actor170: jeep
|
||||
Location: 34,51
|
||||
Owner: GDI
|
||||
Health: 31
|
||||
Facing: 159
|
||||
Actor171: arty
|
||||
Location: 19,17
|
||||
Owner: Nod
|
||||
Actor172: ltnk
|
||||
Location: 22,11
|
||||
Owner: Nod
|
||||
Facing: 159
|
||||
Actor173: e1
|
||||
Location: 45,36
|
||||
Owner: Nod
|
||||
SubCell: 3
|
||||
Actor174: e1
|
||||
Location: 44,36
|
||||
Owner: Nod
|
||||
SubCell: 4
|
||||
Actor175: e3
|
||||
Location: 41,36
|
||||
Owner: Nod
|
||||
Facing: 159
|
||||
SubCell: 2
|
||||
Actor176: e3
|
||||
Location: 41,36
|
||||
Owner: Nod
|
||||
Facing: 159
|
||||
SubCell: 3
|
||||
Actor177: e3
|
||||
Location: 42,17
|
||||
Owner: Nod
|
||||
Facing: 159
|
||||
SubCell: 0
|
||||
Actor178: e3
|
||||
Location: 40,17
|
||||
Owner: Nod
|
||||
Facing: 159
|
||||
SubCell: 4
|
||||
Actor179: e4
|
||||
Location: 42,15
|
||||
Owner: Nod
|
||||
Facing: 159
|
||||
SubCell: 2
|
||||
Actor180: e4
|
||||
Location: 42,15
|
||||
Owner: Nod
|
||||
Facing: 159
|
||||
SubCell: 3
|
||||
Actor181: e1
|
||||
Location: 16,53
|
||||
Owner: Nod
|
||||
Facing: 191
|
||||
SubCell: 3
|
||||
Actor182: e1
|
||||
Location: 15,53
|
||||
Owner: Nod
|
||||
Facing: 191
|
||||
SubCell: 2
|
||||
Actor183: e1
|
||||
Location: 17,53
|
||||
Owner: Nod
|
||||
Facing: 191
|
||||
SubCell: 3
|
||||
Actor184: e1
|
||||
Location: 16,53
|
||||
Owner: Nod
|
||||
Facing: 191
|
||||
SubCell: 2
|
||||
Actor185: c3
|
||||
Location: 18,50
|
||||
Owner: Neutral
|
||||
Facing: 159
|
||||
SubCell: 1
|
||||
Actor186: e1
|
||||
Location: 46,51
|
||||
Owner: GDI
|
||||
Facing: 159
|
||||
SubCell: 1
|
||||
Actor187: e1
|
||||
Location: 44,52
|
||||
Owner: GDI
|
||||
Health: 87
|
||||
Facing: 223
|
||||
SubCell: 2
|
||||
Actor188: e1
|
||||
Location: 43,53
|
||||
Owner: GDI
|
||||
Health: 31
|
||||
Facing: 31
|
||||
SubCell: 4
|
||||
Actor189: e1
|
||||
Location: 42,54
|
||||
Owner: GDI
|
||||
Health: 75
|
||||
SubCell: 2
|
||||
Actor190: e1
|
||||
Location: 39,52
|
||||
Owner: GDI
|
||||
Health: 77
|
||||
Facing: 159
|
||||
SubCell: 4
|
||||
Actor191: e1
|
||||
Location: 42,53
|
||||
Owner: GDI
|
||||
Health: 69
|
||||
Facing: 159
|
||||
SubCell: 0
|
||||
Actor192: e2
|
||||
Location: 40,53
|
||||
Owner: GDI
|
||||
Health: 75
|
||||
Facing: 31
|
||||
SubCell: 2
|
||||
Actor193: e2
|
||||
Location: 45,51
|
||||
Owner: GDI
|
||||
Facing: 127
|
||||
SubCell: 1
|
||||
Actor194: e2
|
||||
Location: 38,50
|
||||
Owner: GDI
|
||||
Health: 27
|
||||
Facing: 223
|
||||
SubCell: 0
|
||||
Actor195: e2
|
||||
Location: 46,52
|
||||
Owner: GDI
|
||||
Facing: 31
|
||||
SubCell: 1
|
||||
Actor196: e2
|
||||
Location: 36,53
|
||||
Owner: GDI
|
||||
Health: 57
|
||||
Facing: 159
|
||||
SubCell: 0
|
||||
Actor197: e2
|
||||
Location: 37,54
|
||||
Owner: GDI
|
||||
Facing: 31
|
||||
SubCell: 1
|
||||
Actor198: e3
|
||||
Location: 27,40
|
||||
Owner: Nod
|
||||
SubCell: 4
|
||||
Actor199: e3
|
||||
Location: 29,40
|
||||
Owner: Nod
|
||||
SubCell: 3
|
||||
Actor200: e3
|
||||
Location: 48,27
|
||||
Owner: Nod
|
||||
SubCell: 3
|
||||
Actor201: e1
|
||||
Location: 27,21
|
||||
Owner: Nod
|
||||
SubCell: 3
|
||||
Actor202: e1
|
||||
Location: 27,21
|
||||
Owner: Nod
|
||||
SubCell: 4
|
||||
Actor203: e1
|
||||
Location: 27,21
|
||||
Owner: Nod
|
||||
SubCell: 2
|
||||
Actor204: e1
|
||||
Location: 27,21
|
||||
Owner: Nod
|
||||
SubCell: 1
|
||||
Actor205: e3
|
||||
Location: 28,21
|
||||
Owner: Nod
|
||||
SubCell: 4
|
||||
Actor206: e3
|
||||
Location: 28,21
|
||||
Owner: Nod
|
||||
SubCell: 2
|
||||
Actor207: e3
|
||||
Location: 29,21
|
||||
Owner: Nod
|
||||
SubCell: 1
|
||||
Actor208: e3
|
||||
Location: 29,21
|
||||
Owner: Nod
|
||||
SubCell: 3
|
||||
Actor209: e4
|
||||
Location: 30,21
|
||||
Owner: Nod
|
||||
SubCell: 1
|
||||
Actor210: e4
|
||||
Location: 30,21
|
||||
Owner: Nod
|
||||
SubCell: 3
|
||||
Actor211: e4
|
||||
Location: 30,21
|
||||
Owner: Nod
|
||||
SubCell: 2
|
||||
Actor212: e4
|
||||
Location: 30,21
|
||||
Owner: Nod
|
||||
SubCell: 4
|
||||
Actor213: e1
|
||||
Location: 13,12
|
||||
Owner: Nod
|
||||
SubCell: 3
|
||||
Actor214: e1
|
||||
Location: 14,12
|
||||
Owner: Nod
|
||||
SubCell: 1
|
||||
Actor215: e1
|
||||
Location: 15,11
|
||||
Owner: Nod
|
||||
SubCell: 0
|
||||
Actor216: e3
|
||||
Location: 5,7
|
||||
Owner: Nod
|
||||
SubCell: 3
|
||||
Actor217: e3
|
||||
Location: 7,7
|
||||
Owner: Nod
|
||||
SubCell: 3
|
||||
Actor218: e3
|
||||
Location: 19,15
|
||||
Owner: Nod
|
||||
SubCell: 1
|
||||
Actor219: e3
|
||||
Location: 20,16
|
||||
Owner: Nod
|
||||
SubCell: 1
|
||||
DefaultChinookTarget: waypoint
|
||||
Location: 41,56
|
||||
Owner: Neutral
|
||||
DefaultCameraPosition: waypoint
|
||||
Location: 41,49
|
||||
Owner: Neutral
|
||||
waypoint16: waypoint
|
||||
Location: 10,22
|
||||
Owner: Neutral
|
||||
waypoint15: waypoint
|
||||
Location: 53,45
|
||||
Owner: Neutral
|
||||
waypoint14: waypoint
|
||||
Location: 53,40
|
||||
Owner: Neutral
|
||||
waypoint13: waypoint
|
||||
Location: 13,26
|
||||
Owner: Neutral
|
||||
waypoint12: waypoint
|
||||
Location: 52,25
|
||||
Owner: Neutral
|
||||
waypoint11: waypoint
|
||||
Location: 36,27
|
||||
Owner: Neutral
|
||||
waypoint10: waypoint
|
||||
Location: 38,32
|
||||
Owner: Neutral
|
||||
waypoint9: waypoint
|
||||
Location: 47,43
|
||||
Owner: Neutral
|
||||
waypoint8: waypoint
|
||||
Location: 24,55
|
||||
Owner: Neutral
|
||||
waypoint7: waypoint
|
||||
Location: 43,40
|
||||
Owner: Neutral
|
||||
waypoint6: waypoint
|
||||
Location: 24,38
|
||||
Owner: Neutral
|
||||
waypoint5: waypoint
|
||||
Location: 20,29
|
||||
Owner: Neutral
|
||||
waypoint4: waypoint
|
||||
Location: 23,24
|
||||
Owner: Neutral
|
||||
waypoint3: waypoint
|
||||
Location: 5,43
|
||||
Owner: Neutral
|
||||
waypoint2: waypoint
|
||||
Location: 8,47
|
||||
Owner: Neutral
|
||||
waypoint1: waypoint
|
||||
Location: 8,53
|
||||
Owner: Neutral
|
||||
waypoint0: waypoint
|
||||
Location: 19,53
|
||||
Owner: Neutral
|
||||
Actor239: moneycrate
|
||||
Owner: Neutral
|
||||
Location: 5,42
|
||||
|
||||
Rules: cnc|rules/campaign-maprules.yaml, cnc|rules/campaign-tooltips.yaml, cnc|rules/campaign-palettes.yaml, rules.yaml
|
||||
154
mods/cnc/maps/gdi08a/rules.yaml
Normal file
@@ -0,0 +1,154 @@
|
||||
World:
|
||||
LuaScript:
|
||||
Scripts: campaign-global.lua, gdi08a.lua, gdi08a-AI.lua
|
||||
MusicPlaylist:
|
||||
StartingMusic: march
|
||||
VictoryMusic: gdi_win1
|
||||
MissionData:
|
||||
Briefing: U.N. Sanction has cut funding to the Global Defense Initiative. Field Units are helpless.\n\nUse the repair facility to keep your units in the field long enough to destroy the Nod base in this region.\n\nAll Nod units and structures must be destroyed.
|
||||
BriefingVideo: gdi8a.vqa
|
||||
BackgroundVideo: tbrinfo1.vqa
|
||||
WinVideo: paratrop.vqa
|
||||
LossVideo: gameover.vqa
|
||||
SmudgeLayer@SCORCH:
|
||||
InitialSmudges:
|
||||
42,56: sc1,0
|
||||
41,52: sc6,0
|
||||
38,51: sc2,0
|
||||
43,50: sc3,0
|
||||
40,48: sc4,0
|
||||
47,46: sc5,0
|
||||
|
||||
ATWR:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
NUK2:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
HPAD:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
BRIK:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
EYE:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
OBLI:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
TMPL:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
HTNK:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
TRAN:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
ORCA:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
RMBO:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
MSAM:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
MCV:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
BOAT:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
FTNK:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
STNK:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
HELI:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
LTNK:
|
||||
Buildable:
|
||||
Prerequisites: ~afld
|
||||
|
||||
ARTY:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
E4:
|
||||
Buildable:
|
||||
Prerequisites: barracks
|
||||
|
||||
E5:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
MLRS:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
CYCL:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
GTWR:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
SBAG:
|
||||
Buildable:
|
||||
Queue: Defence.GDI, Defence.Nod
|
||||
|
||||
GUN:
|
||||
Buildable:
|
||||
Queue: Defence.GDI, Defence.Nod
|
||||
|
||||
C3:
|
||||
Tooltip:
|
||||
Name: Farmer Mike
|
||||
|
||||
SAM:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
^Bridge:
|
||||
DamageMultiplier@INVULNERABLE:
|
||||
Modifier: 0
|
||||
|
||||
BRIDGEHUT:
|
||||
-Targetable:
|
||||
|
||||
airstrike.proxy:
|
||||
AirstrikePower:
|
||||
SquadSize: 2
|
||||
SquadOffset: -1536, 1024, 0
|
||||
|
||||
HQ:
|
||||
Tooltip:
|
||||
-AirstrikePower:
|
||||
Buildable:
|
||||
Description: Provides an overview of the battlefield.\n Requires power to operate.
|
||||
|
||||
AFLD:
|
||||
RallyPoint:
|
||||
Path: -5,2
|
||||
204
mods/cnc/maps/gdi09/gdi09-AI.lua
Normal file
@@ -0,0 +1,204 @@
|
||||
--[[
|
||||
Copyright 2007-2020 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.
|
||||
]]
|
||||
|
||||
AttackPaths = { { waypoint7 }, { waypoint8 } }
|
||||
NodBase = { handofnod, nodairfield, nodrefinery, NodCYard, nodpower1, nodpower2, nodpower3, nodpower4, nodpower5, gun5, gun6, gun7, gun8, nodsilo1, nodsilo2, nodsilo3, nodsilo4, nodobelisk }
|
||||
|
||||
PatrolProductionQueue = { }
|
||||
InfantryAttackGroup = { }
|
||||
InfantryGroupSize = 5
|
||||
InfantryProductionCooldown = DateTime.Minutes(3)
|
||||
InfantryProductionTypes = { "e1", "e1", "e1", "e3", "e3", "e4" }
|
||||
HarvesterProductionType = { "harv" }
|
||||
VehicleAttackGroup = { }
|
||||
VehicleGroupSize = 5
|
||||
VehicleProductionCooldown = DateTime.Minutes(3)
|
||||
VehicleProductionTypes = { "bggy", "bggy", "bggy", "ltnk", "ltnk", "arty" }
|
||||
StartingCash = 14000
|
||||
|
||||
BaseRefinery = { type = "proc", pos = CPos.New(12, 25) }
|
||||
BaseNuke1 = { type = "nuke", pos = CPos.New(5, 24) }
|
||||
BaseNuke2 = { type = "nuke", pos = CPos.New(3, 24) }
|
||||
BaseNuke3 = { type = "nuke", pos = CPos.New(16, 30) }
|
||||
BaseNuke4 = { type = "nuke", pos = CPos.New(14, 30) }
|
||||
BaseNuke5 = { type = "nuke", pos = CPos.New(12, 30) }
|
||||
InfantryProduction = { type = "hand", pos = CPos.New(15, 24) }
|
||||
VehicleProduction = { type = "afld", pos = CPos.New(3, 27) }
|
||||
|
||||
NodGuards = { Actor168, Actor169, Actor170, Actor171, Actor172, Actor181, Actor177, Actor188, Actor189, Actor190 }
|
||||
|
||||
BaseBuildings = { BaseRefinery, BaseNuke1, BaseNuke2, BaseNuke3, BaseNuke4, InfantryProduction, VehicleProduction }
|
||||
|
||||
BuildBuilding = function(building, cyard)
|
||||
local buildingCost = Actor.Cost(building.type)
|
||||
if CyardIsBuilding or Nod.Cash < buildingCost then
|
||||
Trigger.AfterDelay(DateTime.Seconds(10), function() BuildBuilding(building, cyard) end)
|
||||
return
|
||||
end
|
||||
|
||||
CyardIsBuilding = true
|
||||
|
||||
Nod.Cash = Nod.Cash - buildingCost
|
||||
Trigger.AfterDelay(Actor.BuildTime(building.type), function()
|
||||
CyardIsBuilding = false
|
||||
|
||||
if cyard.IsDead or cyard.Owner ~= Nod then
|
||||
Nod.Cash = Nod.Cash + buildingCost
|
||||
return
|
||||
end
|
||||
|
||||
local actor = Actor.Create(building.type, true, { Owner = Nod, Location = building.pos })
|
||||
|
||||
if actor.Type == 'hand' or actor.Type == 'pyle' then
|
||||
Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceInfantry(actor) end)
|
||||
elseif actor.Type == 'afld' or actor.Type == 'weap' then
|
||||
Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceVehicle(actor) end)
|
||||
end
|
||||
|
||||
Trigger.OnKilled(actor, function()
|
||||
BuildBuilding(building, cyard)
|
||||
end)
|
||||
|
||||
RepairBuilding(Nod, actor, 0.75)
|
||||
end)
|
||||
end
|
||||
|
||||
HasHarvester = function()
|
||||
local harv = Nod.GetActorsByType("harv")
|
||||
return #harv > 0
|
||||
end
|
||||
|
||||
GuardBase = function()
|
||||
Utils.Do(NodBase, function(building)
|
||||
Trigger.OnDamaged(building, function()
|
||||
if not building.IsDead then
|
||||
Utils.Do(NodGuards, function(guard)
|
||||
if not guard.IsDead then
|
||||
guard.Stop()
|
||||
guard.Guard(building)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
ProduceHarvester = function(building)
|
||||
if not buildingHarvester then
|
||||
buildingHarvester = true
|
||||
building.Build(HarvesterProductionType, function()
|
||||
buildingHarvester = false
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
ProduceInfantry = function(building)
|
||||
if building.IsDead or building.Owner ~= Nod then
|
||||
return
|
||||
elseif not HasHarvester() then
|
||||
Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceInfantry(building) end)
|
||||
return
|
||||
end
|
||||
|
||||
if #PatrolProductionQueue >= 1 then
|
||||
local inQueue = PatrolProductionQueue[1]
|
||||
local toBuild = { inQueue.unit[1] }
|
||||
local patrolPath = inQueue.waypoints
|
||||
building.Build(toBuild, function(unit)
|
||||
ReplenishPatrolUnit(unit[1], handofnod, patrolPath, 40)
|
||||
table.remove(PatrolProductionQueue, 1)
|
||||
end)
|
||||
Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceInfantry(building) end)
|
||||
return
|
||||
end
|
||||
|
||||
local delay = Utils.RandomInteger(DateTime.Seconds(3), DateTime.Seconds(9))
|
||||
local toBuild = { Utils.Random(InfantryProductionTypes) }
|
||||
local Path = Utils.Random(AttackPaths)
|
||||
building.Build(toBuild, function(unit)
|
||||
InfantryAttackGroup[#InfantryAttackGroup + 1] = unit[1]
|
||||
|
||||
if #InfantryAttackGroup >= InfantryGroupSize then
|
||||
MoveAndHunt(InfantryAttackGroup, Path)
|
||||
InfantryAttackGroup = { }
|
||||
Trigger.AfterDelay(InfantryProductionCooldown, function() ProduceInfantry(building) end)
|
||||
else
|
||||
Trigger.AfterDelay(delay, function() ProduceInfantry(building) end)
|
||||
end
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
ProduceVehicle = function(building)
|
||||
if building.IsDead or building.Owner ~= Nod then
|
||||
return
|
||||
elseif not HasHarvester() then
|
||||
ProduceHarvester(building)
|
||||
Trigger.AfterDelay(DateTime.Seconds(10), function() ProduceVehicle(building) end)
|
||||
return
|
||||
end
|
||||
|
||||
local delay = Utils.RandomInteger(DateTime.Seconds(12), DateTime.Seconds(17))
|
||||
local toBuild = { Utils.Random(VehicleProductionTypes) }
|
||||
local Path = Utils.Random(AttackPaths)
|
||||
building.Build(toBuild, function(unit)
|
||||
VehicleAttackGroup[#VehicleAttackGroup + 1] = unit[1]
|
||||
|
||||
if #VehicleAttackGroup >= VehicleGroupSize then
|
||||
MoveAndHunt(VehicleAttackGroup, Path)
|
||||
VehicleAttackGroup = { }
|
||||
Trigger.AfterDelay(VehicleProductionCooldown, function() ProduceVehicle(building) end)
|
||||
else
|
||||
Trigger.AfterDelay(delay, function() ProduceVehicle(building) end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
StartAI = function()
|
||||
RepairNamedActors(Nod, 0.75)
|
||||
|
||||
Nod.Cash = StartingCash
|
||||
GuardBase()
|
||||
end
|
||||
|
||||
Trigger.OnAllKilledOrCaptured(NodBase, function()
|
||||
Utils.Do(Nod.GetGroundAttackers(), IdleHunt)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(nodrefinery, function(building)
|
||||
BuildBuilding(BaseRefinery, NodCYard)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(nodpower1, function(building)
|
||||
BuildBuilding(BaseNuke1, NodCYard)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(nodpower2, function(building)
|
||||
BuildBuilding(BaseNuke2, NodCYard)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(nodpower3, function(building)
|
||||
BuildBuilding(BaseNuke3, NodCYard)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(nodpower4, function(building)
|
||||
BuildBuilding(BaseNuke4, NodCYard)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(nodpower5, function(building)
|
||||
BuildBuilding(BaseNuke5, NodCYard)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(handofnod, function(building)
|
||||
BuildBuilding(InfantryProduction, NodCYard)
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(nodairfield, function(building)
|
||||
BuildBuilding(VehicleProduction, NodCYard)
|
||||
end)
|
||||
170
mods/cnc/maps/gdi09/gdi09.lua
Normal file
@@ -0,0 +1,170 @@
|
||||
--[[
|
||||
Copyright 2007-2020 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.
|
||||
]]
|
||||
|
||||
InsertionHelicopterType = "tran.insertion"
|
||||
GDIHeliReinfUnits = { "e2", "e2", "e2", "e3", "e3" }
|
||||
|
||||
SamSites = { sam1, sam2, sam3, sam4 }
|
||||
NodBunkersNorth = { gun3, gun4 }
|
||||
NodBunkersSouth = { gun1, gun2 }
|
||||
|
||||
BoatEscapeTrigger = { CPos.New(2,37) }
|
||||
|
||||
WaypointGroup1 = { waypoint1, waypoint2, waypoint8 }
|
||||
WaypointGroup2 = { waypoint1, waypoint2, waypoint3, waypoint9 }
|
||||
WaypointGroup3 = { waypoint1, waypoint2, waypoint3, waypoint10, waypoint11, waypoint12, waypoint6, waypoint13 }
|
||||
WaypointGroup4 = { waypoint1, waypoint2, waypoint3, waypoint4 }
|
||||
Patrol1Waypoints = { waypoint11.Location, waypoint10.Location }
|
||||
Patrol2Waypoints = { waypoint1.Location, waypoint2.Location, waypoint3.Location, waypoint4.Location, waypoint5.Location, waypoint4.Location, waypoint3.Location, waypoint2.Location, waypoint1.Location, waypoint6.Location }
|
||||
|
||||
Nod1 = { units = { ['e1'] = 2, ['e3'] = 2 }, waypoints = WaypointGroup1, delay = 40 }
|
||||
Nod2 = { units = { ['e3'] = 2, ['e4'] = 2 }, waypoints = WaypointGroup2, delay = 50 }
|
||||
Nod3 = { units = { ['e1'] = 2, ['e3'] = 3, ['e4'] = 2 }, waypoints = WaypointGroup1, delay = 50 }
|
||||
Nod4 = { units = { ['bggy'] = 2 }, waypoints = WaypointGroup2, delay = 50 }
|
||||
Nod5 = { units = { ['e4'] = 2, ['ltnk'] = 1 }, waypoints = WaypointGroup1, delay = 50 }
|
||||
Auto1 = { units = { ['e4'] = 2, ['arty'] = 1 }, waypoints = WaypointGroup1, delay = 50 }
|
||||
Auto2 = { units = { ['e1'] = 2, ['e3'] = 2 }, waypoints = WaypointGroup2, delay = 50 }
|
||||
Auto3 = { units = { ['e3'] = 2, ['e4'] = 2 }, waypoints = WaypointGroup1, delay = 50 }
|
||||
Auto4 = { units = { ['e1'] = 3, ['e4'] = 1 }, waypoints = WaypointGroup1, delay = 50 }
|
||||
Auto5 = { units = { ['ltnk'] = 1, ['bggy'] = 1 }, waypoints = WaypointGroup1, delay = 60 }
|
||||
Auto6 = { units = { ['bggy'] = 1 }, waypoints = WaypointGroup2, delay = 50 }
|
||||
Auto7 = { units = { ['ltnk'] = 1 }, waypoints = WaypointGroup2, delay = 50 }
|
||||
Auto8 = { units = { ['e4'] = 2, ['bggy'] = 1 }, waypoints = WaypointGroup4, delay = 0 }
|
||||
|
||||
Patrols = {
|
||||
grd1 = { units = { ['e3'] = 3 }, waypoints = Patrol1Waypoints, wait = 40, initialWaypointPlacement = { 1 } },
|
||||
grd2 = { units = { ['e1'] = 2, ['e3'] = 2, ['e4'] = 2 }, waypoints = Patrol2Waypoints, wait = 20, initialWaypointPlacement = { 4, 10, 1 } }
|
||||
}
|
||||
|
||||
AutoAttackWaves = { Nod1, Nod2, Nod3, Nod4, Nod5, Auto1, Auto2, Auto3, Auto4, Auto5, Auto6, Auto7, Auto8 }
|
||||
|
||||
StationaryGuards = { Actor174, Actor173, Actor182, Actor183, Actor184, Actor185, Actor186, Actor187 , Actor199, Actor200, Actor201, Actor202, Actor203, Actor204 }
|
||||
|
||||
StartStationaryGuards = function()
|
||||
Utils.Do(StationaryGuards, function(unit)
|
||||
if not unit.IsDead then
|
||||
unit.Patrol( { unit.Location } , true, 20)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
SendWaves = function(counter, Waves)
|
||||
if counter <= #Waves then
|
||||
local team = Waves[counter]
|
||||
|
||||
for type, amount in pairs(team.units) do
|
||||
MoveAndHunt(Utils.Take(amount, Nod.GetActorsByType(type)), team.waypoints)
|
||||
end
|
||||
|
||||
Trigger.AfterDelay(DateTime.Seconds(team.delay), function() SendWaves(counter + 1, Waves) end)
|
||||
end
|
||||
end
|
||||
|
||||
StartPatrols = function()
|
||||
for k, team in pairs(Patrols) do
|
||||
local group = 1
|
||||
for type, amount in pairs(team.units) do
|
||||
for i = 1, amount do
|
||||
Reinforcements.Reinforce(Nod, { type }, { team.waypoints[team.initialWaypointPlacement[group]] }, 0, function(unit)
|
||||
ReplenishPatrolUnit(unit, handofnod, team.waypoints, team.wait)
|
||||
end)
|
||||
end
|
||||
group = group + 1
|
||||
end
|
||||
end
|
||||
Patrols = nil
|
||||
end
|
||||
|
||||
ReplenishPatrolUnit = function(unit, building, waypoints, waitatwaypoint)
|
||||
unit.Patrol(waypoints, true, waitatwaypoint)
|
||||
Trigger.OnKilled(unit, function()
|
||||
local queueUnit = { unit = { unit.Type }, atbuilding = { building }, waypoints = waypoints }
|
||||
PatrolProductionQueue[#PatrolProductionQueue + 1] = queueUnit
|
||||
end)
|
||||
end
|
||||
|
||||
SendGDIReinforcements = function()
|
||||
Media.PlaySpeechNotification(GDI, "Reinforce")
|
||||
Reinforcements.ReinforceWithTransport(GDI, InsertionHelicopterType, GDIHeliReinfUnits, { GDIHeliEntryNorth.Location, GDIHeliLZ.Location }, { GDIHeliLZ.Location + CVec.New(20, 0) })
|
||||
end
|
||||
|
||||
SendGDIReinforcementChinook = function()
|
||||
Reinforcements.ReinforceWithTransport(GDI, 'tran', nil, { GDIHeliEntryNorth.Location, GDIHeliLZ.Location })
|
||||
end
|
||||
|
||||
SpawnGunboat = function()
|
||||
Media.PlaySpeechNotification(GDI, "Reinforce")
|
||||
Actor.Create("boat", true, { Owner = GDI, Facing = 0, Location = CPos.New(62,37) })
|
||||
end
|
||||
|
||||
WorldLoaded = function()
|
||||
GDI = Player.GetPlayer("GDI")
|
||||
Nod = Player.GetPlayer("Nod")
|
||||
|
||||
Camera.Position = DefaultCameraPosition.CenterPosition
|
||||
|
||||
DestroyBunkers = GDI.AddObjective("Destroy the Nod bunkers to allow Carter's\nconvoy to pass through safely.")
|
||||
Trigger.OnAllKilled(NodBunkersNorth, function()
|
||||
GDI.MarkCompletedObjective(DestroyBunkers)
|
||||
Trigger.AfterDelay(DateTime.Seconds(1), SpawnGunboat)
|
||||
end)
|
||||
Trigger.OnAllKilled(NodBunkersSouth, function()
|
||||
GDI.MarkCompletedObjective(DestroyBunkers)
|
||||
SendGDIReinforcementChinook()
|
||||
Trigger.AfterDelay(DateTime.Seconds(1), SpawnGunboat)
|
||||
end)
|
||||
Trigger.OnEnteredFootprint(BoatEscapeTrigger, function(a, id)
|
||||
if a.Type == "boat" then
|
||||
a.Destroy()
|
||||
Media.DisplayMessage("Part of Carter's convoy passed through!")
|
||||
Media.PlaySoundNotification(GDI, "AlertBleep")
|
||||
end
|
||||
end)
|
||||
|
||||
SecureArea = GDI.AddObjective("Destroy the Nod strike force.")
|
||||
KillGDI = Nod.AddObjective("Kill all enemies!")
|
||||
|
||||
Trigger.AfterDelay(DateTime.Seconds(5), SendGDIReinforcements)
|
||||
|
||||
AirSupport = GDI.AddObjective("Destroy the SAM sites to receive air support.", "Secondary", false)
|
||||
Trigger.OnAllKilled(SamSites, function()
|
||||
GDI.MarkCompletedObjective(AirSupport)
|
||||
Actor.Create("airstrike.proxy", true, { Owner = GDI })
|
||||
end)
|
||||
|
||||
Actor.Create("flare", true, { Owner = GDI, Location = DefaultFlareLocation.Location })
|
||||
|
||||
StartStationaryGuards()
|
||||
|
||||
StartAI()
|
||||
|
||||
StartPatrols()
|
||||
|
||||
InitObjectives(GDI)
|
||||
|
||||
Trigger.AfterDelay(DateTime.Minutes(1), function() SendWaves(1, AutoAttackWaves) end)
|
||||
Trigger.AfterDelay(DateTime.Minutes(3), function() ProduceInfantry(handofnod) end)
|
||||
Trigger.AfterDelay(DateTime.Minutes(3), function() ProduceVehicle(nodairfield) end)
|
||||
|
||||
local initialArrivingUnits = { Actor175, Actor191, Actor192, Actor193, Actor194, Actor195, Actor196, Actor197, Actor198 }
|
||||
Utils.Do(initialArrivingUnits, function(unit)
|
||||
unit.Move(unit.Location + CVec.New(0, 1), 0)
|
||||
end)
|
||||
end
|
||||
|
||||
Tick = function()
|
||||
if DateTime.GameTime > DateTime.Seconds(5) then
|
||||
if GDI.HasNoRequiredUnits() then
|
||||
Nod.MarkCompletedObjective(KillGDI)
|
||||
end
|
||||
if Nod.HasNoRequiredUnits() then
|
||||
GDI.MarkCompletedObjective(SecureArea)
|
||||
end
|
||||
end
|
||||
end
|
||||
BIN
mods/cnc/maps/gdi09/map.bin
Normal file
BIN
mods/cnc/maps/gdi09/map.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
785
mods/cnc/maps/gdi09/map.yaml
Normal file
@@ -0,0 +1,785 @@
|
||||
MapFormat: 11
|
||||
|
||||
RequiresMod: cnc
|
||||
|
||||
Title: 09: Secure the Danube
|
||||
|
||||
Author: Westwood Studios
|
||||
|
||||
Tileset: WINTER
|
||||
|
||||
MapSize: 64,64
|
||||
|
||||
Bounds: 2,3,60,48
|
||||
|
||||
Visibility: MissionSelector
|
||||
|
||||
Categories: Campaign
|
||||
|
||||
LockPreview: True
|
||||
|
||||
Players:
|
||||
PlayerReference@Neutral:
|
||||
Name: Neutral
|
||||
OwnsWorld: True
|
||||
NonCombatant: True
|
||||
Faction: gdi
|
||||
PlayerReference@Nod:
|
||||
Name: Nod
|
||||
Faction: nod
|
||||
Color: FF1400
|
||||
Allies: Nod
|
||||
Enemies: GDI
|
||||
Bot: campaign
|
||||
PlayerReference@GDI:
|
||||
Name: GDI
|
||||
AllowBots: False
|
||||
Playable: True
|
||||
Required: True
|
||||
LockFaction: True
|
||||
Faction: gdi
|
||||
LockColor: True
|
||||
Color: F5D378
|
||||
LockSpawn: True
|
||||
LockTeam: True
|
||||
Allies: GDI
|
||||
Enemies: Nod
|
||||
|
||||
Actors:
|
||||
Actor0: sbag
|
||||
Location: 30,43
|
||||
Owner: Neutral
|
||||
Actor1: sbag
|
||||
Location: 29,43
|
||||
Owner: Neutral
|
||||
Actor2: sbag
|
||||
Location: 28,43
|
||||
Owner: Neutral
|
||||
Actor3: sbag
|
||||
Location: 27,43
|
||||
Owner: Neutral
|
||||
Actor4: sbag
|
||||
Location: 26,43
|
||||
Owner: Neutral
|
||||
Actor5: sbag
|
||||
Location: 30,42
|
||||
Owner: Neutral
|
||||
Actor6: sbag
|
||||
Location: 28,42
|
||||
Owner: Neutral
|
||||
Actor7: sbag
|
||||
Location: 26,42
|
||||
Owner: Neutral
|
||||
Actor8: sbag
|
||||
Location: 35,33
|
||||
Owner: Neutral
|
||||
Actor9: sbag
|
||||
Location: 33,33
|
||||
Owner: Neutral
|
||||
Actor10: sbag
|
||||
Location: 31,33
|
||||
Owner: Neutral
|
||||
Actor11: brik
|
||||
Location: 21,33
|
||||
Owner: Neutral
|
||||
Actor12: brik
|
||||
Location: 20,33
|
||||
Owner: Neutral
|
||||
Actor13: brik
|
||||
Location: 19,33
|
||||
Owner: Neutral
|
||||
Actor14: brik
|
||||
Location: 18,33
|
||||
Owner: Neutral
|
||||
Actor15: brik
|
||||
Location: 17,33
|
||||
Owner: Neutral
|
||||
Actor16: brik
|
||||
Location: 16,33
|
||||
Owner: Neutral
|
||||
Actor17: brik
|
||||
Location: 15,33
|
||||
Owner: Neutral
|
||||
Actor18: brik
|
||||
Location: 14,33
|
||||
Owner: Neutral
|
||||
Actor19: brik
|
||||
Location: 13,33
|
||||
Owner: Neutral
|
||||
Actor20: brik
|
||||
Location: 12,33
|
||||
Owner: Neutral
|
||||
Actor21: brik
|
||||
Location: 11,33
|
||||
Owner: Neutral
|
||||
Actor22: brik
|
||||
Location: 10,33
|
||||
Owner: Neutral
|
||||
Actor23: brik
|
||||
Location: 9,33
|
||||
Owner: Neutral
|
||||
Actor24: brik
|
||||
Location: 8,33
|
||||
Owner: Neutral
|
||||
Actor25: brik
|
||||
Location: 7,33
|
||||
Owner: Neutral
|
||||
Actor26: brik
|
||||
Location: 6,33
|
||||
Owner: Neutral
|
||||
Actor27: brik
|
||||
Location: 5,33
|
||||
Owner: Neutral
|
||||
Actor28: brik
|
||||
Location: 4,33
|
||||
Owner: Neutral
|
||||
Actor29: brik
|
||||
Location: 3,33
|
||||
Owner: Neutral
|
||||
Actor30: brik
|
||||
Location: 2,33
|
||||
Owner: Neutral
|
||||
Actor31: sbag
|
||||
Location: 35,32
|
||||
Owner: Neutral
|
||||
Actor32: sbag
|
||||
Location: 34,32
|
||||
Owner: Neutral
|
||||
Actor33: sbag
|
||||
Location: 33,32
|
||||
Owner: Neutral
|
||||
Actor34: sbag
|
||||
Location: 32,32
|
||||
Owner: Neutral
|
||||
Actor35: sbag
|
||||
Location: 31,32
|
||||
Owner: Neutral
|
||||
Actor36: brik
|
||||
Location: 21,32
|
||||
Owner: Neutral
|
||||
Actor37: brik
|
||||
Location: 2,32
|
||||
Owner: Neutral
|
||||
Actor38: brik
|
||||
Location: 21,31
|
||||
Owner: Neutral
|
||||
Actor39: brik
|
||||
Location: 20,31
|
||||
Owner: Neutral
|
||||
Actor40: brik
|
||||
Location: 2,31
|
||||
Owner: Neutral
|
||||
Actor41: brik
|
||||
Location: 21,30
|
||||
Owner: Neutral
|
||||
Actor42: brik
|
||||
Location: 20,30
|
||||
Owner: Neutral
|
||||
Actor43: brik
|
||||
Location: 2,30
|
||||
Owner: Neutral
|
||||
Actor44: wood
|
||||
Location: 31,29
|
||||
Owner: Neutral
|
||||
Actor45: brik
|
||||
Location: 2,29
|
||||
Owner: Neutral
|
||||
Actor46: wood
|
||||
Location: 31,28
|
||||
Owner: Neutral
|
||||
Actor47: brik
|
||||
Location: 2,28
|
||||
Owner: Neutral
|
||||
Actor48: wood
|
||||
Location: 42,27
|
||||
Owner: Neutral
|
||||
Actor49: wood
|
||||
Location: 41,27
|
||||
Owner: Neutral
|
||||
Actor50: wood
|
||||
Location: 40,27
|
||||
Owner: Neutral
|
||||
Actor51: wood
|
||||
Location: 39,27
|
||||
Owner: Neutral
|
||||
Actor52: brik
|
||||
Location: 2,27
|
||||
Owner: Neutral
|
||||
Actor53: wood
|
||||
Location: 42,26
|
||||
Owner: Neutral
|
||||
Actor54: v16
|
||||
Location: 41,26
|
||||
Owner: Neutral
|
||||
Actor55: v18
|
||||
Location: 40,26
|
||||
Owner: Neutral
|
||||
Actor56: wood
|
||||
Location: 39,26
|
||||
Owner: Neutral
|
||||
Actor57: brik
|
||||
Location: 21,26
|
||||
Owner: Neutral
|
||||
Actor58: brik
|
||||
Location: 20,26
|
||||
Owner: Neutral
|
||||
Actor59: brik
|
||||
Location: 2,26
|
||||
Owner: Neutral
|
||||
Actor60: wood
|
||||
Location: 42,25
|
||||
Owner: Neutral
|
||||
Actor61: v14
|
||||
Location: 41,25
|
||||
Owner: Neutral
|
||||
Actor62: v15
|
||||
Location: 40,25
|
||||
Owner: Neutral
|
||||
Actor63: wood
|
||||
Location: 39,25
|
||||
Owner: Neutral
|
||||
Actor64: brik
|
||||
Location: 21,25
|
||||
Owner: Neutral
|
||||
Actor65: brik
|
||||
Location: 20,25
|
||||
Owner: Neutral
|
||||
Actor66: brik
|
||||
Location: 2,25
|
||||
Owner: Neutral
|
||||
Actor67: wood
|
||||
Location: 42,24
|
||||
Owner: Neutral
|
||||
Actor68: wood
|
||||
Location: 39,24
|
||||
Owner: Neutral
|
||||
Actor69: brik
|
||||
Location: 21,24
|
||||
Owner: Neutral
|
||||
Actor70: brik
|
||||
Location: 14,24
|
||||
Owner: Neutral
|
||||
Actor71: brik
|
||||
Location: 13,24
|
||||
Owner: Neutral
|
||||
Actor72: brik
|
||||
Location: 8,24
|
||||
Owner: Neutral
|
||||
Actor73: brik
|
||||
Location: 7,24
|
||||
Owner: Neutral
|
||||
Actor74: brik
|
||||
Location: 2,24
|
||||
Owner: Neutral
|
||||
Actor75: brik
|
||||
Location: 21,23
|
||||
Owner: Neutral
|
||||
Actor76: brik
|
||||
Location: 20,23
|
||||
Owner: Neutral
|
||||
Actor77: brik
|
||||
Location: 19,23
|
||||
Owner: Neutral
|
||||
Actor78: brik
|
||||
Location: 18,23
|
||||
Owner: Neutral
|
||||
Actor79: brik
|
||||
Location: 17,23
|
||||
Owner: Neutral
|
||||
Actor80: brik
|
||||
Location: 16,23
|
||||
Owner: Neutral
|
||||
Actor81: brik
|
||||
Location: 15,23
|
||||
Owner: Neutral
|
||||
Actor82: brik
|
||||
Location: 14,23
|
||||
Owner: Neutral
|
||||
Actor83: brik
|
||||
Location: 13,23
|
||||
Owner: Neutral
|
||||
Actor84: brik
|
||||
Location: 8,23
|
||||
Owner: Neutral
|
||||
Actor85: brik
|
||||
Location: 7,23
|
||||
Owner: Neutral
|
||||
Actor86: brik
|
||||
Location: 6,23
|
||||
Owner: Neutral
|
||||
Actor87: brik
|
||||
Location: 5,23
|
||||
Owner: Neutral
|
||||
Actor88: brik
|
||||
Location: 4,23
|
||||
Owner: Neutral
|
||||
Actor89: brik
|
||||
Location: 3,23
|
||||
Owner: Neutral
|
||||
Actor90: brik
|
||||
Location: 2,23
|
||||
Owner: Neutral
|
||||
Actor91: t02
|
||||
Location: 46,27
|
||||
Owner: Neutral
|
||||
Actor92: t02
|
||||
Location: 14,19
|
||||
Owner: Neutral
|
||||
Actor93: t01
|
||||
Location: 18,19
|
||||
Owner: Neutral
|
||||
Actor94: t14
|
||||
Location: 44,27
|
||||
Owner: Neutral
|
||||
Actor95: tc01
|
||||
Location: 43,24
|
||||
Owner: Neutral
|
||||
Actor96: tc02
|
||||
Location: 42,27
|
||||
Owner: Neutral
|
||||
Actor97: tc04
|
||||
Location: 45,24
|
||||
Owner: Neutral
|
||||
Actor98: split2
|
||||
Location: 55,23
|
||||
Owner: Neutral
|
||||
Actor99: split2
|
||||
Location: 60,28
|
||||
Owner: Neutral
|
||||
Actor100: tc04
|
||||
Location: 34,26
|
||||
Owner: Neutral
|
||||
Actor101: tc02
|
||||
Location: 37,25
|
||||
Owner: Neutral
|
||||
Actor102: t10
|
||||
Location: 32,27
|
||||
Owner: Neutral
|
||||
Actor103: t01
|
||||
Location: 34,25
|
||||
Owner: Neutral
|
||||
Actor104: t17
|
||||
Location: 35,24
|
||||
Owner: Neutral
|
||||
Actor105: tc01
|
||||
Location: 31,26
|
||||
Owner: Neutral
|
||||
Actor106: tc04
|
||||
Location: 51,15
|
||||
Owner: Neutral
|
||||
Actor107: tc04
|
||||
Location: 37,5
|
||||
Owner: Neutral
|
||||
Actor108: tc04
|
||||
Location: 39,14
|
||||
Owner: Neutral
|
||||
Actor109: tc04
|
||||
Location: 30,6
|
||||
Owner: Neutral
|
||||
Actor110: tc02
|
||||
Location: 29,14
|
||||
Owner: Neutral
|
||||
Actor111: tc01
|
||||
Location: 44,12
|
||||
Owner: Neutral
|
||||
Actor112: tc01
|
||||
Location: 32,9
|
||||
Owner: Neutral
|
||||
Actor113: t03
|
||||
Location: 34,17
|
||||
Owner: Neutral
|
||||
Actor114: t02
|
||||
Location: 44,20
|
||||
Owner: Neutral
|
||||
Actor115: t01
|
||||
Location: 42,19
|
||||
Owner: Neutral
|
||||
Actor116: t01
|
||||
Location: 43,10
|
||||
Owner: Neutral
|
||||
Actor117: t01
|
||||
Location: 35,15
|
||||
Owner: Neutral
|
||||
Actor118: t01
|
||||
Location: 31,10
|
||||
Owner: Neutral
|
||||
Actor119: tc02
|
||||
Location: 22,42
|
||||
Owner: Neutral
|
||||
Actor120: tc04
|
||||
Location: 2,47
|
||||
Owner: Neutral
|
||||
Actor121: tc05
|
||||
Location: 15,52
|
||||
Owner: Neutral
|
||||
Actor122: tc02
|
||||
Location: 27,53
|
||||
Owner: Neutral
|
||||
Actor123: t01
|
||||
Location: 30,50
|
||||
Owner: Neutral
|
||||
Actor124: t02
|
||||
Location: 23,52
|
||||
Owner: Neutral
|
||||
Actor125: t03
|
||||
Location: 33,45
|
||||
Owner: Neutral
|
||||
Actor126: tc01
|
||||
Location: 22,22
|
||||
Owner: Neutral
|
||||
Actor127: t07
|
||||
Location: 5,5
|
||||
Owner: Neutral
|
||||
Actor128: tc04
|
||||
Location: 8,4
|
||||
Owner: Neutral
|
||||
Actor129: tc01
|
||||
Location: 3,4
|
||||
Owner: Neutral
|
||||
Actor130: t07
|
||||
Location: 38,23
|
||||
Owner: Neutral
|
||||
Actor131: t11
|
||||
Location: 30,43
|
||||
Owner: Neutral
|
||||
Actor132: tc04
|
||||
Location: 24,43
|
||||
Owner: Neutral
|
||||
Actor133: tc04
|
||||
Location: 25,29
|
||||
Owner: Neutral
|
||||
Actor134: tc05
|
||||
Location: 52,22
|
||||
Owner: Neutral
|
||||
gun1: gun
|
||||
Location: 27,42
|
||||
Owner: Nod
|
||||
gun2: gun
|
||||
Location: 29,42
|
||||
Owner: Nod
|
||||
gun3: gun
|
||||
Location: 34,33
|
||||
Owner: Nod
|
||||
Facing: 159
|
||||
gun4: gun
|
||||
Location: 32,33
|
||||
Owner: Nod
|
||||
Facing: 159
|
||||
Actor139: v04
|
||||
Location: 45,26
|
||||
Owner: Neutral
|
||||
Actor140: v07
|
||||
Location: 51,24
|
||||
Owner: Neutral
|
||||
Actor141: v06
|
||||
Location: 40,24
|
||||
Owner: Neutral
|
||||
Actor142: v05
|
||||
Location: 43,26
|
||||
Owner: Neutral
|
||||
Actor143: v03
|
||||
Location: 35,25
|
||||
Owner: Neutral
|
||||
Actor144: v01
|
||||
Location: 37,26
|
||||
Owner: Neutral
|
||||
sam1: sam
|
||||
Location: 3,4
|
||||
Owner: Nod
|
||||
sam2: sam
|
||||
Location: 19,24
|
||||
Owner: Nod
|
||||
sam3: sam
|
||||
Location: 2,22
|
||||
Owner: Nod
|
||||
sam4: sam
|
||||
Location: 19,32
|
||||
Owner: Nod
|
||||
nodpower1: nuke
|
||||
Location: 5,24
|
||||
Owner: Nod
|
||||
nodpower2: nuke
|
||||
Location: 3,24
|
||||
Owner: Nod
|
||||
gun5: gun
|
||||
Location: 13,22
|
||||
Owner: Nod
|
||||
gun6: gun
|
||||
Location: 8,22
|
||||
Owner: Nod
|
||||
NodCYard: fact
|
||||
Location: 7,30
|
||||
Owner: Nod
|
||||
nodobelisk: obli
|
||||
Location: 18,32
|
||||
Owner: Nod
|
||||
nodpower3: nuke
|
||||
Location: 16,30
|
||||
Owner: Nod
|
||||
nodpower4: nuke
|
||||
Location: 14,30
|
||||
Owner: Nod
|
||||
nodsilo1: silo
|
||||
Location: 5,31
|
||||
Owner: Nod
|
||||
nodsilo2: silo
|
||||
Location: 10,31
|
||||
Owner: Nod
|
||||
nodsilo3: silo
|
||||
Location: 3,31
|
||||
Owner: Nod
|
||||
nodrefinery: proc
|
||||
Location: 12,25
|
||||
Owner: Nod
|
||||
FreeActor: False
|
||||
nodairfield: afld
|
||||
Location: 3,27
|
||||
Owner: Nod
|
||||
handofnod: hand
|
||||
Location: 15,25
|
||||
Owner: Nod
|
||||
nodsilo4: silo
|
||||
Location: 17,24
|
||||
Owner: Nod
|
||||
nodpower5: nuke
|
||||
Location: 12,30
|
||||
Owner: Nod
|
||||
gun7: gun
|
||||
Location: 22,25
|
||||
Owner: Nod
|
||||
Facing: 191
|
||||
gun8: gun
|
||||
Location: 22,31
|
||||
Owner: Nod
|
||||
Facing: 191
|
||||
Actor167: harv
|
||||
Location: 20,28
|
||||
Owner: Nod
|
||||
Facing: 191
|
||||
Actor168: ltnk
|
||||
Location: 19,26
|
||||
Owner: Nod
|
||||
Facing: 127
|
||||
Actor169: ltnk
|
||||
Location: 18,26
|
||||
Owner: Nod
|
||||
Facing: 127
|
||||
Actor170: ltnk
|
||||
Location: 8,28
|
||||
Owner: Nod
|
||||
Facing: 127
|
||||
Actor171: bggy
|
||||
Location: 18,31
|
||||
Owner: Nod
|
||||
Actor172: bggy
|
||||
Location: 19,31
|
||||
Owner: Nod
|
||||
Actor173: bggy
|
||||
Location: 40,14
|
||||
Owner: Nod
|
||||
Actor174: arty
|
||||
Location: 8,15
|
||||
Owner: Nod
|
||||
Facing: 31
|
||||
Actor175: mcv
|
||||
Location: 54,4
|
||||
Owner: GDI
|
||||
Facing: 127
|
||||
Actor176: e3
|
||||
Location: 19,25
|
||||
Owner: Nod
|
||||
SubCell: 1
|
||||
Actor177: e3
|
||||
Location: 17,26
|
||||
Owner: Nod
|
||||
SubCell: 3
|
||||
Actor178: e3
|
||||
Location: 8,25
|
||||
Owner: Nod
|
||||
SubCell: 1
|
||||
Actor179: e3
|
||||
Location: 10,29
|
||||
Owner: Nod
|
||||
SubCell: 0
|
||||
Actor180: e1
|
||||
Location: 5,30
|
||||
Owner: Nod
|
||||
SubCell: 0
|
||||
Actor181: e1
|
||||
Location: 8,26
|
||||
Owner: Nod
|
||||
SubCell: 4
|
||||
Actor182: e4
|
||||
Location: 7,14
|
||||
Owner: Nod
|
||||
Facing: 31
|
||||
SubCell: 3
|
||||
Actor183: e4
|
||||
Location: 7,14
|
||||
Owner: Nod
|
||||
Facing: 31
|
||||
SubCell: 2
|
||||
Actor184: e4
|
||||
Location: 25,6
|
||||
Owner: Nod
|
||||
Facing: 191
|
||||
SubCell: 1
|
||||
Actor185: e4
|
||||
Location: 25,4
|
||||
Owner: Nod
|
||||
Facing: 191
|
||||
SubCell: 3
|
||||
Actor186: e3
|
||||
Location: 33,9
|
||||
Owner: Nod
|
||||
SubCell: 0
|
||||
Actor187: e3
|
||||
Location: 31,6
|
||||
Owner: Nod
|
||||
SubCell: 3
|
||||
Actor188: e3
|
||||
Location: 17,27
|
||||
Owner: Nod
|
||||
SubCell: 2
|
||||
Actor189: e3
|
||||
Location: 17,26
|
||||
Owner: Nod
|
||||
SubCell: 2
|
||||
Actor190: e3
|
||||
Location: 17,27
|
||||
Owner: Nod
|
||||
SubCell: 0
|
||||
Actor191: e2
|
||||
Location: 53,6
|
||||
Owner: GDI
|
||||
Facing: 127
|
||||
SubCell: 4
|
||||
Actor192: e2
|
||||
Location: 54,6
|
||||
Owner: GDI
|
||||
Facing: 127
|
||||
SubCell: 3
|
||||
Actor193: e2
|
||||
Location: 53,7
|
||||
Owner: GDI
|
||||
Facing: 127
|
||||
SubCell: 2
|
||||
Actor194: e3
|
||||
Location: 54,6
|
||||
Owner: GDI
|
||||
Facing: 127
|
||||
SubCell: 4
|
||||
Actor195: e3
|
||||
Location: 55,6
|
||||
Owner: GDI
|
||||
Facing: 127
|
||||
SubCell: 3
|
||||
Actor196: e3
|
||||
Location: 55,7
|
||||
Owner: GDI
|
||||
Facing: 127
|
||||
SubCell: 1
|
||||
Actor197: e3
|
||||
Location: 54,7
|
||||
Owner: GDI
|
||||
Facing: 127
|
||||
SubCell: 2
|
||||
Actor198: e2
|
||||
Location: 54,7
|
||||
Owner: GDI
|
||||
Facing: 127
|
||||
SubCell: 1
|
||||
Actor199: e3
|
||||
Location: 25,34
|
||||
Owner: Nod
|
||||
Facing: 127
|
||||
SubCell: 4
|
||||
Actor200: e3
|
||||
Location: 25,34
|
||||
Owner: Nod
|
||||
Facing: 127
|
||||
SubCell: 3
|
||||
Actor201: e3
|
||||
Location: 24,34
|
||||
Owner: Nod
|
||||
Facing: 127
|
||||
SubCell: 4
|
||||
Actor202: e3
|
||||
Location: 25,34
|
||||
Owner: Nod
|
||||
Facing: 127
|
||||
SubCell: 1
|
||||
Actor203: e3
|
||||
Location: 24,34
|
||||
Owner: Nod
|
||||
Facing: 127
|
||||
SubCell: 2
|
||||
Actor204: e3
|
||||
Location: 25,34
|
||||
Owner: Nod
|
||||
Facing: 127
|
||||
SubCell: 2
|
||||
GDIHeliEntryNorth: waypoint
|
||||
Location: 57,6
|
||||
Owner: Neutral
|
||||
DefaultCameraPosition: waypoint
|
||||
Location: 48,3
|
||||
Owner: Neutral
|
||||
DefaultFlareLocation: waypoint
|
||||
Location: 33,31
|
||||
Owner: Neutral
|
||||
waypoint23: waypoint
|
||||
Location: 53,37
|
||||
Owner: Neutral
|
||||
waypoint19: waypoint
|
||||
Location: 52,42
|
||||
Owner: Neutral
|
||||
waypoint18: waypoint
|
||||
Location: 52,19
|
||||
Owner: Neutral
|
||||
waypoint17: waypoint
|
||||
Location: 9,30
|
||||
Owner: Neutral
|
||||
waypoint11: waypoint
|
||||
Location: 23,24
|
||||
Owner: Neutral
|
||||
waypoint10: waypoint
|
||||
Location: 23,32
|
||||
Owner: Neutral
|
||||
waypoint9: waypoint
|
||||
Location: 43,5
|
||||
Owner: Neutral
|
||||
waypoint8: waypoint
|
||||
Location: 60,18
|
||||
Owner: Neutral
|
||||
waypoint7: waypoint
|
||||
Location: 52,6
|
||||
Owner: Neutral
|
||||
waypoint6: waypoint
|
||||
Location: 9,16
|
||||
Owner: Neutral
|
||||
waypoint5: waypoint
|
||||
Location: 4,10
|
||||
Owner: Neutral
|
||||
waypoint4: waypoint
|
||||
Location: 12,5
|
||||
Owner: Neutral
|
||||
waypoint3: waypoint
|
||||
Location: 28,5
|
||||
Owner: Neutral
|
||||
waypoint2: waypoint
|
||||
Location: 30,20
|
||||
Owner: Neutral
|
||||
waypoint1: waypoint
|
||||
Location: 23,28
|
||||
Owner: Neutral
|
||||
GDIHeliLZ: waypoint
|
||||
Location: 41,44
|
||||
Owner: Neutral
|
||||
Actor224: moneycrate
|
||||
Owner: Neutral
|
||||
Location: 5,48
|
||||
|
||||
Rules: cnc|rules/campaign-maprules.yaml, cnc|rules/campaign-tooltips.yaml, cnc|rules/campaign-palettes.yaml, rules.yaml
|
||||
|
||||
Weapons: weapons.yaml
|
||||
146
mods/cnc/maps/gdi09/rules.yaml
Normal file
@@ -0,0 +1,146 @@
|
||||
World:
|
||||
LuaScript:
|
||||
Scripts: campaign-global.lua, gdi09.lua, gdi09-AI.lua
|
||||
MusicPlaylist:
|
||||
StartingMusic: march
|
||||
VictoryMusic: gdi_win1
|
||||
MissionData:
|
||||
Briefing: Take out Nod turrets along shore so Gunboats can move in safely on the Nod base.\n\nThe Nod base must be destroyed.\n\nIf gunboats can get in, they should be able to destroy the base with no difficulty.\n\nKeep an eye out for the new weapon Nod is rumored to be working on.
|
||||
BriefingVideo: gdi9.vqa
|
||||
WinVideo: gunboat.vqa
|
||||
LossVideo: gameover.vqa
|
||||
|
||||
ATWR:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
NUK2:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
HPAD:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
BRIK:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
EYE:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
GUN:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
OBLI:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
TMPL:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
HTNK:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
TRAN:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
ORCA:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
RMBO:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
MSAM:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
MCV:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
BOAT:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
FTNK:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
STNK:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
HELI:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
LTNK:
|
||||
Buildable:
|
||||
Prerequisites: ~afld
|
||||
|
||||
ARTY:
|
||||
Buildable:
|
||||
Prerequisites: ~afld
|
||||
|
||||
E4:
|
||||
Buildable:
|
||||
Prerequisites: barracks
|
||||
|
||||
E5:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
MLRS:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
SAM:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
^Bridge:
|
||||
DamageMultiplier@INVULNERABLE:
|
||||
Modifier: 0
|
||||
|
||||
BRIDGEHUT:
|
||||
-Targetable:
|
||||
|
||||
TRAN.Extraction:
|
||||
Inherits: TRAN
|
||||
RevealsShroud:
|
||||
Range: 0c0
|
||||
RejectsOrders:
|
||||
-Selectable:
|
||||
RenderSprites:
|
||||
Image: tran
|
||||
Interactable:
|
||||
|
||||
TRAN.Insertion:
|
||||
Inherits: TRAN.Extraction
|
||||
Cargo:
|
||||
MaxWeight: 0
|
||||
|
||||
HQ:
|
||||
Tooltip:
|
||||
-AirstrikePower:
|
||||
Buildable:
|
||||
Description: Provides an overview of the battlefield.\n Requires power to operate.
|
||||
|
||||
airstrike.proxy:
|
||||
AirstrikePower:
|
||||
SquadSize: 2
|
||||
SquadOffset: -1536, 1024, 0
|
||||
|
||||
BOAT:
|
||||
AutoTarget:
|
||||
InitialStance: AttackAnything
|
||||
RejectsOrders:
|
||||
Except: Attack
|
||||
13
mods/cnc/maps/gdi09/weapons.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
BoatMissile:
|
||||
Warhead@Bonus: SpreadDamage
|
||||
Spread: 128
|
||||
Damage: 2000
|
||||
Versus:
|
||||
None: 50
|
||||
Wood: 100
|
||||
Light: 100
|
||||
Heavy: 100
|
||||
DamageTypes: Prone50Percent, TriggerProne, ExplosionDeath
|
||||
|
||||
TurretGun:
|
||||
ReloadDelay: 40
|
||||
@@ -105,13 +105,11 @@ SendReinforcementsWave = function(team)
|
||||
|
||||
transport.UnloadPassengers()
|
||||
Trigger.OnPassengerExited(transport, function(_, passenger)
|
||||
Utils.Do(passengers, function(actor)
|
||||
if actor.Type == "e6" then
|
||||
CaptureStructures(actor)
|
||||
else
|
||||
IdleHunt(actor)
|
||||
end
|
||||
end)
|
||||
if passenger.Type == "e6" then
|
||||
Trigger.OnIdle(passenger, CaptureStructures)
|
||||
else
|
||||
IdleHunt(passenger)
|
||||
end
|
||||
|
||||
if not transport.HasPassengers then
|
||||
IdleHunt(transport)
|
||||
@@ -177,6 +175,10 @@ WorldLoaded = function()
|
||||
EliminateGDI = Nod.AddObjective("Eliminate all GDI forces in the area.")
|
||||
BuildSAMs = Nod.AddObjective("Build 3 SAMs to fend off the GDI bombers.", "Secondary", false)
|
||||
GDIObjective = GDI.AddObjective("Eliminate all Nod forces in the area.")
|
||||
|
||||
Trigger.OnKilled(GDIProc, function()
|
||||
Actor.Create("moneycrate", true, { Owner = GDI, Location = CPos.New(24, 54) })
|
||||
end)
|
||||
end
|
||||
|
||||
Tick = function()
|
||||
|
||||
@@ -12,7 +12,7 @@ WaypointGroup2 = { waypoint0, waypoint1, waypoint2, waypoint3, waypoint4, waypoi
|
||||
WaypointGroup3 = { waypoint0, waypoint1, waypoint2, waypoint3, waypoint9, waypoint10, waypoint11, waypoint6, waypoint7 }
|
||||
WaypointGroup4 = { waypoint9, waypoint10, waypoint11, waypoint6, waypoint7, waypoint14 }
|
||||
|
||||
GDI1 = { units = { "e2", "e2", "e6" }, waypoints = WaypointGroup4, delay = 40 }
|
||||
GDI1 = { units = { "e2", "e2", "e6", "e6", "e6" }, waypoints = WaypointGroup4, delay = 40 }
|
||||
GDI2 = { units = { "e1", "e2" }, waypoints = WaypointGroup3, delay = 40 }
|
||||
GDI3 = { units = { "e2", "e3", "jeep" }, waypoints = WaypointGroup2, delay = 40 }
|
||||
GDI4 = { units = { "mtnk" }, waypoints = WaypointGroup3, delay = 40 }
|
||||
@@ -100,17 +100,18 @@ SendWaves = function(counter, Waves)
|
||||
end
|
||||
|
||||
SendReinforcementsWave = function(team)
|
||||
Reinforcements.ReinforceWithTransport(GDI, "apc", GetCargo(team), { ReinforcementsGDISpawn.Location, waypoint12.Location}, nil, function(transport, passengers)
|
||||
MoveAndHunt(transport, team.waypoints)
|
||||
Reinforcements.ReinforceWithTransport(GDI, "apc", team.units, { ReinforcementsGDISpawn.Location, waypoint12.Location}, nil, function(transport, passengers)
|
||||
Utils.Do(team.waypoints, function(waypoint)
|
||||
transport.Move(waypoint.Location)
|
||||
end)
|
||||
|
||||
transport.UnloadPassengers()
|
||||
Trigger.OnPassengerExited(transport, function(_, passenger)
|
||||
Utils.Do(passengers, function(actor)
|
||||
if actor.Type == "e6" then
|
||||
CaptureStructures(actor)
|
||||
else
|
||||
IdleHunt(actor)
|
||||
end
|
||||
end)
|
||||
if passenger.Type == "e6" then
|
||||
Trigger.OnIdle(passenger, CaptureStructures)
|
||||
else
|
||||
IdleHunt(passenger)
|
||||
end
|
||||
|
||||
if not transport.HasPassengers then
|
||||
IdleHunt(transport)
|
||||
|
||||
@@ -156,12 +156,6 @@ FACTOUT.IN:
|
||||
Capturable:
|
||||
Types: building
|
||||
|
||||
MoneyCrate:
|
||||
Inherits: ^Crate
|
||||
GiveCashCrateAction:
|
||||
Amount: 2000
|
||||
UseCashTick: true
|
||||
|
||||
NUKEOUT.IN:
|
||||
Inherits: NUKE
|
||||
RenderSprites:
|
||||
|
||||
@@ -189,12 +189,6 @@ FACTOUT.IN:
|
||||
Capturable:
|
||||
Types: building
|
||||
|
||||
MoneyCrate:
|
||||
Inherits: ^Crate
|
||||
GiveCashCrateAction:
|
||||
Amount: 2000
|
||||
UseCashTick: true
|
||||
|
||||
NUKEOUT.IN:
|
||||
Inherits: NUKE
|
||||
RenderSprites:
|
||||
|
||||
@@ -696,6 +696,9 @@ Actors:
|
||||
Location: 54,6
|
||||
Facing: 92
|
||||
TurretFacing: 92
|
||||
Actor245: moneycrate
|
||||
Location: 5,6
|
||||
Owner: Neutral
|
||||
waypoint27: waypoint
|
||||
Location: 32,32
|
||||
Owner: Neutral
|
||||
|
||||
@@ -9,6 +9,8 @@ GDI Campaign:
|
||||
gdi05b
|
||||
gdi06
|
||||
gdi07
|
||||
gdi08a
|
||||
gdi09
|
||||
|
||||
Nod Campaign:
|
||||
nod01
|
||||
|
||||
@@ -255,7 +255,7 @@ GameSpeeds:
|
||||
OrderLatency: 6
|
||||
|
||||
ColorValidator:
|
||||
TeamColorPresets: f70606, ff7a22, f8d3b3, f8e947, b6f706, f335a0, a64d6c, ce08f9, f5b2db, 12b572, 380135, 1d06f7, 328dff, 78dbf8, cef6b1, 000000
|
||||
TeamColorPresets: f70606, ff7a22, f8d3b3, f8e947, 94b319, f335a0, a64d6c, ce08f9, f5b2db, 12b572, 502048, 1d06f7, 328dff, 78dbf8, cef6b1, 391d1d
|
||||
|
||||
ModContent:
|
||||
InstallPromptMessage: Tiberian Dawn requires artwork and audio from the original game.\n\nQuick Install will automatically download this content (without music\nor videos) from a mirror of the 2007 C&C Gold freeware release.\n\nAdvanced Install includes options for downloading the music and for\ncopying the videos and other content from an original game disc.
|
||||
|
||||
@@ -63,6 +63,17 @@ airstrike.proxy:
|
||||
CircleSequence: circles
|
||||
SupportPowerPaletteOrder: 10
|
||||
|
||||
MoneyCrate:
|
||||
Inherits: ^Crate
|
||||
Tooltip:
|
||||
Name: Money Crate
|
||||
GiveCashCrateAction:
|
||||
Amount: 2000
|
||||
Sequence: dollar
|
||||
UseCashTick: true
|
||||
RenderSprites:
|
||||
Image: wcrate
|
||||
|
||||
TRUCK:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
@@ -425,7 +425,7 @@ AFLD:
|
||||
Queue: Building.Nod
|
||||
Description: Provides a dropzone\nfor vehicle reinforcements
|
||||
Building:
|
||||
Footprint: XXXX xxxx ====
|
||||
Footprint: XXX+ xxx+ ====
|
||||
Dimensions: 4,3
|
||||
LocalCenterOffset: 0,-512,0
|
||||
Health:
|
||||
|
||||
@@ -175,6 +175,14 @@ crate:
|
||||
idle:
|
||||
ZOffset: -511
|
||||
|
||||
scrate:
|
||||
idle:
|
||||
ZOffset: -511
|
||||
|
||||
wcrate:
|
||||
idle:
|
||||
ZOffset: -511
|
||||
|
||||
xcratea:
|
||||
idle: xcrate
|
||||
ZOffset: -511
|
||||
|
||||
|
Before Width: | Height: | Size: 409 KiB After Width: | Height: | Size: 410 KiB |
|
Before Width: | Height: | Size: 991 KiB After Width: | Height: | Size: 735 KiB |
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 168 KiB |
@@ -173,8 +173,10 @@ Background@SAVE_MAP_PANEL:
|
||||
Key: escape
|
||||
|
||||
ScrollPanel@MAP_SAVE_VISIBILITY_PANEL:
|
||||
TopBottomSpacing: 5
|
||||
ItemSpacing: 5
|
||||
Width: 220
|
||||
Height: 64
|
||||
Height: 55
|
||||
Children:
|
||||
Checkbox@VISIBILITY_TEMPLATE:
|
||||
X: 5
|
||||
@@ -618,29 +620,30 @@ Container@EDITOR_WORLD_ROOT:
|
||||
Contrast: true
|
||||
|
||||
ScrollPanel@CATEGORY_FILTER_PANEL:
|
||||
Width: 170
|
||||
Width: 240
|
||||
TopBottomSpacing: 5
|
||||
ItemSpacing: 5
|
||||
Children:
|
||||
Container@SELECT_CATEGORIES_BUTTONS:
|
||||
Width: PARENT_RIGHT
|
||||
Height: 29
|
||||
Height: 25
|
||||
Children:
|
||||
Button@SELECT_ALL:
|
||||
X: 10
|
||||
Y: 2
|
||||
Width: 60
|
||||
Y: 0 - 5
|
||||
Width: 93
|
||||
Height: 25
|
||||
Text: All
|
||||
Font: Bold
|
||||
Button@SELECT_NONE:
|
||||
X: PARENT_RIGHT - WIDTH - 34
|
||||
Y: 2
|
||||
Width: 60
|
||||
X: 10 + 93 + 10
|
||||
Y: 0 - 5
|
||||
Width: 93
|
||||
Height: 25
|
||||
Text: None
|
||||
Font: Bold
|
||||
Checkbox@CATEGORY_TEMPLATE:
|
||||
X: 5
|
||||
Y: 5
|
||||
Width: PARENT_RIGHT - 29
|
||||
Height: 20
|
||||
Visible: false
|
||||
|
||||
@@ -185,7 +185,13 @@ Background@MAINMENU_INTRODUCTION_PROMPT:
|
||||
Height: 16
|
||||
Font: Small
|
||||
Text: - Zoom the battlefield using {MODIFIER + Scroll Wheel}
|
||||
LabelWithHighlight@DESC_SCROLL:
|
||||
LabelWithHighlight@DESC_SCROLL_RIGHT:
|
||||
X: 265
|
||||
Y: 34
|
||||
Height: 16
|
||||
Font: Small
|
||||
Text: - Pan the battlefield using the {Right} mouse button
|
||||
LabelWithHighlight@DESC_SCROLL_MIDDLE:
|
||||
X: 265
|
||||
Y: 34
|
||||
Height: 16
|
||||
|
||||
@@ -91,7 +91,7 @@ Background@SETTINGS_PANEL:
|
||||
X: 5
|
||||
Y: 50
|
||||
Width: PARENT_RIGHT - 10
|
||||
Height: PARENT_BOTTOM
|
||||
Height: PARENT_BOTTOM - 50
|
||||
Children:
|
||||
Label@PLAYER:
|
||||
Text: Player Name:
|
||||
@@ -249,13 +249,6 @@ Background@SETTINGS_PANEL:
|
||||
Height: 25
|
||||
MaxLength: 5
|
||||
Type: Integer
|
||||
Label@WINDOW_CHANGES_DESC:
|
||||
X: 60
|
||||
Y: 27
|
||||
Width: 200
|
||||
Height: 15
|
||||
Font: Tiny
|
||||
Text: Video mode and window size changes require restart
|
||||
Container@DISPLAY_SELECTION:
|
||||
Y: 240
|
||||
Children:
|
||||
@@ -271,13 +264,6 @@ Background@SETTINGS_PANEL:
|
||||
Height: 25
|
||||
Font: Regular
|
||||
Text: Standard
|
||||
Label@MODE_CHANGES_DESC:
|
||||
X: 60
|
||||
Y: 27
|
||||
Width: 200
|
||||
Height: 15
|
||||
Font: Tiny
|
||||
Text: Video mode and display changes require restart
|
||||
Checkbox@FRAME_LIMIT_CHECKBOX:
|
||||
X: 310
|
||||
Y: 243
|
||||
@@ -293,6 +279,26 @@ Background@SETTINGS_PANEL:
|
||||
Ticks: 20
|
||||
MinimumValue: 50
|
||||
MaximumValue: 240
|
||||
Label@GL_PROFILE:
|
||||
X: 15
|
||||
Y: 270
|
||||
Width: 120
|
||||
Height: 25
|
||||
Align: Right
|
||||
Text: OpenGL Profile:
|
||||
DropDownButton@GL_PROFILE_DROPDOWN:
|
||||
X: 140
|
||||
Y: 270
|
||||
Width: 160
|
||||
Height: 25
|
||||
Font: Regular
|
||||
Label@RESTART_REQUIRED_DESC:
|
||||
Y: PARENT_BOTTOM - 40
|
||||
Width: PARENT_RIGHT
|
||||
Height: 15
|
||||
Font: Tiny
|
||||
Text: Display and OpenGL changes require restart
|
||||
Align: Center
|
||||
Container@AUDIO_PANEL:
|
||||
X: 5
|
||||
Y: 50
|
||||
@@ -491,7 +497,12 @@ Background@SETTINGS_PANEL:
|
||||
Height: 16
|
||||
Font: Small
|
||||
Text: - Zoom the battlefield using {MODIFIER + Scroll Wheel}
|
||||
LabelWithHighlight@DESC_SCROLL:
|
||||
LabelWithHighlight@DESC_SCROLL_RIGHT:
|
||||
Y: 85
|
||||
Height: 16
|
||||
Font: Small
|
||||
Text: - Pan the battlefield using the {Right} mouse button
|
||||
LabelWithHighlight@DESC_SCROLL_MIDDLE:
|
||||
Y: 85
|
||||
Height: 16
|
||||
Font: Small
|
||||
@@ -544,13 +555,13 @@ Background@SETTINGS_PANEL:
|
||||
Height: 20
|
||||
Font: Regular
|
||||
Text: Screen Edge Panning
|
||||
Checkbox@CLASSIC_MOUSE_MIDDLE_SCROLL_CHECKBOX:
|
||||
Checkbox@ALTERNATE_SCROLL_CHECKBOX:
|
||||
X: 360
|
||||
Y: 133
|
||||
Width: 180
|
||||
Height: 20
|
||||
Font: Regular
|
||||
Text: Middle Mouse Panning
|
||||
Text: Alternate Mouse Panning
|
||||
Label@SCROLL_SPEED_LABEL:
|
||||
X: 305
|
||||
Y: 210
|
||||
@@ -648,7 +659,7 @@ Background@SETTINGS_PANEL:
|
||||
Width: 80
|
||||
Height: 25
|
||||
Align: Left
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
TooltipContainer: SETTINGS_TOOLTIP_CONTAINER
|
||||
Container@THREE_COLUMN:
|
||||
Width: 173
|
||||
Height: 25
|
||||
@@ -664,7 +675,7 @@ Background@SETTINGS_PANEL:
|
||||
Width: 80
|
||||
Height: 25
|
||||
Align: Left
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
TooltipContainer: SETTINGS_TOOLTIP_CONTAINER
|
||||
Background@HOTKEY_DIALOG_ROOT:
|
||||
X: 15
|
||||
Y: 230
|
||||
@@ -716,7 +727,7 @@ Background@SETTINGS_PANEL:
|
||||
Text: Clear
|
||||
Font: Bold
|
||||
TooltipText: Unbind the hotkey
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
TooltipContainer: SETTINGS_TOOLTIP_CONTAINER
|
||||
TooltipTemplate: SIMPLE_TOOLTIP
|
||||
Button@RESET_HOTKEY_BUTTON:
|
||||
X: PARENT_RIGHT - WIDTH - 20
|
||||
@@ -726,7 +737,7 @@ Background@SETTINGS_PANEL:
|
||||
Text: Reset
|
||||
Font: Bold
|
||||
TooltipText: Reset to default
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
TooltipContainer: SETTINGS_TOOLTIP_CONTAINER
|
||||
TooltipTemplate: SIMPLE_TOOLTIP
|
||||
Container@ADVANCED_PANEL:
|
||||
X: 5
|
||||
@@ -846,4 +857,4 @@ Background@SETTINGS_PANEL:
|
||||
Height: 20
|
||||
Font: Regular
|
||||
Text: Enable Debug Commands in Replays
|
||||
TooltipContainer@TOOLTIP_CONTAINER:
|
||||
TooltipContainer@SETTINGS_TOOLTIP_CONTAINER:
|
||||
|
||||
@@ -39,7 +39,7 @@ InitObjectives = function(player)
|
||||
end)
|
||||
end
|
||||
|
||||
SendCarryallReinforcements = function(player, currentWave, totalWaves, delay, pathFunction, unitTypes, customCondition, customHuntFunction)
|
||||
SendCarryallReinforcements = function(player, currentWave, totalWaves, delay, pathFunction, unitTypes, customCondition, customHuntFunction, announcementFunction)
|
||||
Trigger.AfterDelay(delay, function()
|
||||
if customCondition and customCondition() then
|
||||
return
|
||||
@@ -50,6 +50,10 @@ SendCarryallReinforcements = function(player, currentWave, totalWaves, delay, pa
|
||||
return
|
||||
end
|
||||
|
||||
if announcementFunction then
|
||||
announcementFunction(currentWave)
|
||||
end
|
||||
|
||||
local path = pathFunction()
|
||||
local units = Reinforcements.ReinforceWithTransport(player, "carryall.reinforce", unitTypes[currentWave], path, { path[1] })[2]
|
||||
|
||||
@@ -78,6 +82,10 @@ TriggerCarryallReinforcements = function(triggeringPlayer, reinforcingPlayer, ar
|
||||
end)
|
||||
end
|
||||
|
||||
DestroyCarryalls = function(player)
|
||||
Utils.Do(player.GetActorsByType("carryall"), function(actor) actor.Kill() end)
|
||||
end
|
||||
|
||||
-- Used for the AI:
|
||||
|
||||
IdlingUnits = { }
|
||||
|
||||
@@ -48,7 +48,7 @@ sidebar-bits:
|
||||
production-tooltip-time: 136, 51, 16, 16
|
||||
production-tooltip-power: 102, 51, 16, 16
|
||||
production-tooltip-cost: 68, 51, 16, 16
|
||||
indicator-muted: 0, 119, 24, 24
|
||||
indicator-muted: 0, 119, 26, 24
|
||||
|
||||
commandbar:
|
||||
Inherits: ^Chrome
|
||||
|
||||
BIN
mods/d2k/maps/ordos-06a/map.bin
Normal file
BIN
mods/d2k/maps/ordos-06a/map.png
Normal file
|
After Width: | Height: | Size: 301 KiB |
578
mods/d2k/maps/ordos-06a/map.yaml
Normal file
@@ -0,0 +1,578 @@
|
||||
MapFormat: 11
|
||||
|
||||
RequiresMod: d2k
|
||||
|
||||
Title: Ordos 06a
|
||||
|
||||
Author: Westwood Studios
|
||||
|
||||
Tileset: ARRAKIS
|
||||
|
||||
MapSize: 84,84
|
||||
|
||||
Bounds: 2,2,80,80
|
||||
|
||||
Visibility: MissionSelector
|
||||
|
||||
Categories: Campaign
|
||||
|
||||
LockPreview: True
|
||||
|
||||
Players:
|
||||
PlayerReference@Neutral:
|
||||
Name: Neutral
|
||||
OwnsWorld: True
|
||||
NonCombatant: True
|
||||
Faction: Random
|
||||
PlayerReference@Creeps:
|
||||
Name: Creeps
|
||||
NonCombatant: True
|
||||
Faction: Random
|
||||
Enemies: Atreides, Ordos, Harkonnen
|
||||
PlayerReference@Ordos:
|
||||
Name: Ordos
|
||||
Playable: True
|
||||
LockFaction: True
|
||||
Faction: ordos
|
||||
LockColor: True
|
||||
Color: B3EAA5
|
||||
Enemies: Atreides, Harkonnen, Creeps
|
||||
PlayerReference@Atreides:
|
||||
Name: Atreides
|
||||
LockFaction: True
|
||||
Faction: atreides
|
||||
LockColor: True
|
||||
Color: 9191FF
|
||||
Allies: Harkonnen
|
||||
Enemies: Ordos, Creeps
|
||||
PlayerReference@Harkonnen:
|
||||
Name: Harkonnen
|
||||
LockFaction: True
|
||||
Faction: harkonnen
|
||||
LockColor: True
|
||||
Color: FE0000
|
||||
Allies: Atreides
|
||||
Enemies: Ordos, Creeps
|
||||
|
||||
Actors:
|
||||
Actor0: wall
|
||||
Location: 65,2
|
||||
Owner: Ordos
|
||||
AHeavyFactory: heavy_factory
|
||||
Location: 3,3
|
||||
Owner: Atreides
|
||||
AConyard: construction_yard
|
||||
Location: 8,3
|
||||
Owner: Atreides
|
||||
APower1: wind_trap
|
||||
Location: 11,3
|
||||
Owner: Atreides
|
||||
APower2: wind_trap
|
||||
Location: 13,3
|
||||
Owner: Atreides
|
||||
ALightFactory: light_factory
|
||||
Location: 16,3
|
||||
Owner: Atreides
|
||||
Actor6: wall
|
||||
Location: 65,3
|
||||
Owner: Ordos
|
||||
OOutpost: outpost
|
||||
Location: 66,3
|
||||
Owner: Ordos
|
||||
OPower1: wind_trap
|
||||
Location: 72,3
|
||||
Owner: Ordos
|
||||
OPower2: wind_trap
|
||||
Location: 74,3
|
||||
Owner: Ordos
|
||||
OConyard: construction_yard
|
||||
Location: 76,3
|
||||
Owner: Ordos
|
||||
OBarracks: barracks
|
||||
Location: 79,3
|
||||
Owner: Ordos
|
||||
Actor12: wall
|
||||
Location: 65,4
|
||||
Owner: Ordos
|
||||
ARock1: large_gun_turret
|
||||
Location: 20,5
|
||||
Facing: 160
|
||||
Owner: Atreides
|
||||
Actor14: wall
|
||||
Location: 65,5
|
||||
Owner: Ordos
|
||||
Actor15: combat_tank_a
|
||||
Location: 13,6
|
||||
Owner: Atreides
|
||||
Actor16: wall
|
||||
Location: 65,6
|
||||
Owner: Ordos
|
||||
APower3: wind_trap
|
||||
Location: 7,7
|
||||
Owner: Atreides
|
||||
APower4: wind_trap
|
||||
Location: 9,7
|
||||
Owner: Atreides
|
||||
APower5: wind_trap
|
||||
Location: 11,7
|
||||
Owner: Atreides
|
||||
APower6: wind_trap
|
||||
Location: 13,7
|
||||
Owner: Atreides
|
||||
APower7: wind_trap
|
||||
Location: 15,7
|
||||
Owner: Atreides
|
||||
Actor22: wall
|
||||
Location: 65,7
|
||||
Owner: Ordos
|
||||
Actor23: trooper
|
||||
Location: 4,8
|
||||
Owner: Atreides
|
||||
Actor24: wall
|
||||
Location: 65,8
|
||||
Owner: Ordos
|
||||
Actor25: wall
|
||||
Location: 66,8
|
||||
Owner: Ordos
|
||||
Actor26: wall
|
||||
Location: 67,8
|
||||
Owner: Ordos
|
||||
Actor27: wall
|
||||
Location: 68,8
|
||||
Owner: Ordos
|
||||
Actor28: wall
|
||||
Location: 69,8
|
||||
Owner: Ordos
|
||||
Actor29: wall
|
||||
Location: 70,8
|
||||
Owner: Ordos
|
||||
ABarracks: barracks
|
||||
Location: 2,9
|
||||
Owner: Atreides
|
||||
Actor31: wall
|
||||
Location: 70,9
|
||||
Owner: Ordos
|
||||
Actor32: wall
|
||||
Location: 79,9
|
||||
Owner: Ordos
|
||||
Actor33: wall
|
||||
Location: 80,9
|
||||
Owner: Ordos
|
||||
Actor34: wall
|
||||
Location: 81,9
|
||||
Owner: Ordos
|
||||
APower8: wind_trap
|
||||
Location: 10,10
|
||||
Owner: Atreides
|
||||
Actor36: wall
|
||||
Location: 70,10
|
||||
Owner: Ordos
|
||||
Actor37: wall
|
||||
Location: 79,10
|
||||
Owner: Ordos
|
||||
AResearch: research_centre
|
||||
Location: 5,11
|
||||
Owner: Atreides
|
||||
Actor39: wall
|
||||
Location: 70,11
|
||||
Owner: Ordos
|
||||
Actor40: wall
|
||||
Location: 79,11
|
||||
Owner: Ordos
|
||||
APower9: wind_trap
|
||||
Location: 2,12
|
||||
Owner: Atreides
|
||||
Actor42: combat_tank_a
|
||||
Location: 8,12
|
||||
Owner: Atreides
|
||||
ARepair: repair_pad
|
||||
Location: 13,12
|
||||
Owner: Atreides
|
||||
ARock2: large_gun_turret
|
||||
Location: 16,12
|
||||
Facing: 160
|
||||
Owner: Atreides
|
||||
Actor45: wall
|
||||
Location: 70,12
|
||||
Owner: Ordos
|
||||
OGunt1: medium_gun_turret
|
||||
Location: 71,12
|
||||
Owner: Ordos
|
||||
OGunt2: medium_gun_turret
|
||||
Location: 78,12
|
||||
Owner: Ordos
|
||||
Actor48: wall
|
||||
Location: 79,12
|
||||
Owner: Ordos
|
||||
APower10: wind_trap
|
||||
Location: 2,15
|
||||
Owner: Atreides
|
||||
APower11: wind_trap
|
||||
Location: 4,15
|
||||
Owner: Atreides
|
||||
APower12: wind_trap
|
||||
Location: 6,15
|
||||
Owner: Atreides
|
||||
Actor52: combat_tank_a
|
||||
Location: 11,17
|
||||
Owner: Atreides
|
||||
Actor53: combat_tank_a
|
||||
Location: 8,18
|
||||
Owner: Atreides
|
||||
Actor54: wall
|
||||
Location: 10,19
|
||||
Owner: Atreides
|
||||
Actor55: wall
|
||||
Location: 11,19
|
||||
Owner: Atreides
|
||||
ARefinery: refinery
|
||||
Location: 2,20
|
||||
Owner: Atreides
|
||||
Actor57: wall
|
||||
Location: 10,20
|
||||
Owner: Atreides
|
||||
Actor58: combat_tank_a
|
||||
Location: 6,21
|
||||
Owner: Atreides
|
||||
Actor59: wall
|
||||
Location: 10,21
|
||||
Owner: Atreides
|
||||
Actor60: spicebloom.spawnpoint
|
||||
Location: 38,21
|
||||
Owner: Neutral
|
||||
Actor61: wall
|
||||
Location: 10,22
|
||||
Owner: Atreides
|
||||
Actor62: carryall
|
||||
Location: 4,23
|
||||
Owner: Atreides
|
||||
Actor63: harvester
|
||||
Location: 5,23
|
||||
Owner: Atreides
|
||||
ARock3: large_gun_turret
|
||||
Location: 10,23
|
||||
Facing: 192
|
||||
Owner: Atreides
|
||||
Actor65: wall
|
||||
Location: 11,23
|
||||
Owner: Atreides
|
||||
AGunt1: medium_gun_turret
|
||||
Location: 12,23
|
||||
Facing: 192
|
||||
Owner: Atreides
|
||||
Actor67: wormspawner
|
||||
Location: 26,23
|
||||
Owner: Creeps
|
||||
Actor68: siege_tank
|
||||
Location: 5,24
|
||||
Owner: Atreides
|
||||
Actor69: spicebloom.spawnpoint
|
||||
Location: 67,24
|
||||
Owner: Neutral
|
||||
Actor70: wall
|
||||
Location: 2,25
|
||||
Owner: Atreides
|
||||
Actor71: wall
|
||||
Location: 3,25
|
||||
Owner: Atreides
|
||||
Actor72: wall
|
||||
Location: 4,25
|
||||
Owner: Atreides
|
||||
Actor73: wall
|
||||
Location: 5,25
|
||||
Owner: Atreides
|
||||
Actor74: wall
|
||||
Location: 6,25
|
||||
Owner: Atreides
|
||||
ARock4: large_gun_turret
|
||||
Location: 7,25
|
||||
Owner: Atreides
|
||||
Actor76: wall
|
||||
Location: 7,26
|
||||
Owner: Atreides
|
||||
AGunt2: medium_gun_turret
|
||||
Location: 7,27
|
||||
Owner: Atreides
|
||||
Actor78: spicebloom.spawnpoint
|
||||
Location: 76,30
|
||||
Owner: Neutral
|
||||
Actor79: spicebloom.spawnpoint
|
||||
Location: 32,33
|
||||
Owner: Neutral
|
||||
Actor80: spicebloom.spawnpoint
|
||||
Location: 56,33
|
||||
Owner: Neutral
|
||||
Actor81: spicebloom.spawnpoint
|
||||
Location: 7,37
|
||||
Owner: Neutral
|
||||
Actor82: spicebloom.spawnpoint
|
||||
Location: 44,48
|
||||
Owner: Neutral
|
||||
Actor83: spicebloom.spawnpoint
|
||||
Location: 77,48
|
||||
Owner: Neutral
|
||||
Actor84: trooper
|
||||
Location: 78,50
|
||||
Owner: Ordos
|
||||
Actor85: trooper
|
||||
Location: 76,53
|
||||
Owner: Ordos
|
||||
Actor86: trooper
|
||||
Location: 77,53
|
||||
Owner: Ordos
|
||||
OStarport: starport
|
||||
Location: 79,53
|
||||
Owner: Ordos
|
||||
Actor88: trooper
|
||||
Location: 77,55
|
||||
Owner: Ordos
|
||||
Actor89: raider
|
||||
Location: 78,56
|
||||
Owner: Ordos
|
||||
HGunt1: medium_gun_turret
|
||||
Location: 30,57
|
||||
Facing: 0
|
||||
Owner: Harkonnen
|
||||
HGunt2: medium_gun_turret
|
||||
Location: 34,57
|
||||
Facing: 0
|
||||
Owner: Harkonnen
|
||||
Actor92: wall
|
||||
Location: 30,58
|
||||
Owner: Harkonnen
|
||||
Actor93: combat_tank_h
|
||||
Location: 32,58
|
||||
Owner: Harkonnen
|
||||
Actor94: wall
|
||||
Location: 34,58
|
||||
Owner: Harkonnen
|
||||
Actor95: wall
|
||||
Location: 30,59
|
||||
Owner: Harkonnen
|
||||
Actor96: trooper
|
||||
Location: 33,59
|
||||
Owner: Harkonnen
|
||||
Actor97: wall
|
||||
Location: 34,59
|
||||
Owner: Harkonnen
|
||||
Actor98: wall
|
||||
Location: 30,60
|
||||
Owner: Harkonnen
|
||||
Actor99: trooper
|
||||
Location: 31,60
|
||||
Owner: Harkonnen
|
||||
Actor100: wall
|
||||
Location: 34,60
|
||||
Owner: Harkonnen
|
||||
Actor101: wall
|
||||
Location: 29,61
|
||||
Owner: Harkonnen
|
||||
Actor102: wall
|
||||
Location: 30,61
|
||||
Owner: Harkonnen
|
||||
Actor103: wall
|
||||
Location: 34,61
|
||||
Owner: Harkonnen
|
||||
Actor104: wall
|
||||
Location: 35,61
|
||||
Owner: Harkonnen
|
||||
Actor105: wall
|
||||
Location: 29,62
|
||||
Owner: Harkonnen
|
||||
Actor106: wall
|
||||
Location: 35,62
|
||||
Owner: Harkonnen
|
||||
Actor107: wall
|
||||
Location: 29,63
|
||||
Owner: Harkonnen
|
||||
Actor108: wall
|
||||
Location: 35,63
|
||||
Owner: Harkonnen
|
||||
Actor109: wall
|
||||
Location: 36,63
|
||||
Owner: Harkonnen
|
||||
Actor110: wall
|
||||
Location: 37,63
|
||||
Owner: Harkonnen
|
||||
Actor111: harvester
|
||||
Location: 31,65
|
||||
Owner: Harkonnen
|
||||
HSilo1: silo
|
||||
Location: 35,65
|
||||
Owner: Harkonnen
|
||||
Actor113: carryall
|
||||
Location: 32,66
|
||||
Owner: Harkonnen
|
||||
HSilo2: silo
|
||||
Location: 35,66
|
||||
Owner: Harkonnen
|
||||
HRefinery: refinery
|
||||
Location: 30,67
|
||||
Owner: Harkonnen
|
||||
HSilo3: silo
|
||||
Location: 35,67
|
||||
Owner: Harkonnen
|
||||
Actor117: spicebloom.spawnpoint
|
||||
Location: 22,68
|
||||
Owner: Neutral
|
||||
HOutpost: outpost
|
||||
Location: 35,70
|
||||
Owner: Harkonnen
|
||||
HPower1: wind_trap
|
||||
Location: 31,71
|
||||
Owner: Harkonnen
|
||||
HPower2: wind_trap
|
||||
Location: 34,73
|
||||
Owner: Harkonnen
|
||||
HPower3: wind_trap
|
||||
Location: 36,73
|
||||
Owner: Harkonnen
|
||||
HPower4: wind_trap
|
||||
Location: 38,73
|
||||
Owner: Harkonnen
|
||||
Actor123: wall
|
||||
Location: 24,74
|
||||
Owner: Harkonnen
|
||||
HPower5: wind_trap
|
||||
Location: 26,74
|
||||
Owner: Harkonnen
|
||||
HPower6: wind_trap
|
||||
Location: 28,74
|
||||
Owner: Harkonnen
|
||||
HPower7: wind_trap
|
||||
Location: 30,74
|
||||
Owner: Harkonnen
|
||||
Actor127: wall
|
||||
Location: 24,75
|
||||
Owner: Harkonnen
|
||||
Actor128: spicebloom.spawnpoint
|
||||
Location: 58,75
|
||||
Owner: Neutral
|
||||
HGunt3: medium_gun_turret
|
||||
Location: 19,76
|
||||
Facing: 32
|
||||
Owner: Harkonnen
|
||||
Actor130: wall
|
||||
Location: 20,76
|
||||
Owner: Harkonnen
|
||||
Actor131: wall
|
||||
Location: 21,76
|
||||
Owner: Harkonnen
|
||||
Actor132: wall
|
||||
Location: 22,76
|
||||
Owner: Harkonnen
|
||||
Actor133: wall
|
||||
Location: 23,76
|
||||
Owner: Harkonnen
|
||||
Actor134: wall
|
||||
Location: 24,76
|
||||
Owner: Harkonnen
|
||||
Actor135: wall
|
||||
Location: 19,77
|
||||
Owner: Harkonnen
|
||||
HHeavyFactory: heavy_factory
|
||||
Location: 33,77
|
||||
Owner: Harkonnen
|
||||
HRock: large_gun_turret
|
||||
Location: 38,77
|
||||
Facing: 192
|
||||
Owner: Harkonnen
|
||||
HPower8: wind_trap
|
||||
Location: 23,78
|
||||
Owner: Harkonnen
|
||||
HConyard: construction_yard
|
||||
Location: 25,78
|
||||
Owner: Harkonnen
|
||||
HPower9: wind_trap
|
||||
Location: 28,78
|
||||
Owner: Harkonnen
|
||||
HPower10: wind_trap
|
||||
Location: 30,78
|
||||
Owner: Harkonnen
|
||||
Actor142: combat_tank_h
|
||||
Location: 20,79
|
||||
Owner: Harkonnen
|
||||
HBarracks: barracks
|
||||
Location: 36,79
|
||||
Owner: Harkonnen
|
||||
HGunt4: medium_gun_turret
|
||||
Location: 19,80
|
||||
Facing: 64
|
||||
Owner: Harkonnen
|
||||
Actor145: wall
|
||||
Location: 39,80
|
||||
Owner: Harkonnen
|
||||
Actor146: wall
|
||||
Location: 19,81
|
||||
Owner: Harkonnen
|
||||
Actor147: wall
|
||||
Location: 39,81
|
||||
Owner: Harkonnen
|
||||
AtreidesRally1: waypoint
|
||||
Owner: Neutral
|
||||
Location: 8,22
|
||||
AtreidesEntry1: waypoint
|
||||
Owner: Neutral
|
||||
Location: 2,22
|
||||
HarkonnenRally1: waypoint
|
||||
Owner: Neutral
|
||||
Location: 32,64
|
||||
HarkonnenEntry1: waypoint
|
||||
Owner: Neutral
|
||||
Location: 32,81
|
||||
AtreidesRally2: waypoint
|
||||
Owner: Neutral
|
||||
Location: 77,76
|
||||
AtreidesEntry2: waypoint
|
||||
Owner: Neutral
|
||||
Location: 77,81
|
||||
AtreidesRally3: waypoint
|
||||
Owner: Neutral
|
||||
Location: 36,34
|
||||
AtreidesEntry3: waypoint
|
||||
Owner: Neutral
|
||||
Location: 36,2
|
||||
AtreidesRally4: waypoint
|
||||
Owner: Neutral
|
||||
Location: 68,41
|
||||
AtreidesEntry4: waypoint
|
||||
Owner: Neutral
|
||||
Location: 81,41
|
||||
HarkonnenRally2: waypoint
|
||||
Owner: Neutral
|
||||
Location: 44,59
|
||||
HarkonnenEntry2: waypoint
|
||||
Owner: Neutral
|
||||
Location: 44,81
|
||||
HarkonnenRally3: waypoint
|
||||
Owner: Neutral
|
||||
Location: 77,77
|
||||
HarkonnenEntry3: waypoint
|
||||
Owner: Neutral
|
||||
Location: 81,77
|
||||
HarkonnenRally4: waypoint
|
||||
Owner: Neutral
|
||||
Location: 56,50
|
||||
HarkonnenEntry4: waypoint
|
||||
Owner: Neutral
|
||||
Location: 56,81
|
||||
HarkonnenRally5: waypoint
|
||||
Owner: Neutral
|
||||
Location: 54,41
|
||||
HarkonnenEntry5: waypoint
|
||||
Owner: Neutral
|
||||
Location: 81,41
|
||||
HarkonnenRally6: waypoint
|
||||
Owner: Neutral
|
||||
Location: 52,33
|
||||
HarkonnenEntry6: waypoint
|
||||
Owner: Neutral
|
||||
Location: 81,33
|
||||
HarkonnenRally7: waypoint
|
||||
Owner: Neutral
|
||||
Location: 34,26
|
||||
HarkonnenEntry7: waypoint
|
||||
Owner: Neutral
|
||||
Location: 34,2
|
||||
|
||||
Rules: d2k|rules/campaign-rules.yaml, d2k|rules/campaign-tooltips.yaml, d2k|rules/campaign-palettes.yaml, rules.yaml
|
||||
54
mods/d2k/maps/ordos-06a/ordos06a-AI.lua
Normal file
@@ -0,0 +1,54 @@
|
||||
--[[
|
||||
Copyright 2007-2020 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.
|
||||
]]
|
||||
|
||||
AttackGroupSize =
|
||||
{
|
||||
easy = 6,
|
||||
normal = 8,
|
||||
hard = 10
|
||||
}
|
||||
|
||||
ProductionDelays =
|
||||
{
|
||||
easy = { DateTime.Seconds(16), DateTime.Seconds(30) },
|
||||
normal = { DateTime.Seconds(2), DateTime.Seconds(5) },
|
||||
hard = { DateTime.Seconds(1), DateTime.Seconds(3) }
|
||||
}
|
||||
|
||||
EnemyInfantryTypes = { "light_inf", "light_inf", "light_inf", "trooper", "trooper" }
|
||||
EnemyVehicleTypes = { "trike", "trike", "quad" }
|
||||
|
||||
AtreidesTankTypes = { "combat_tank_a", "siege_tank", "missile_tank" }
|
||||
HarkonnenTankTypes = { "combat_tank_h", "siege_tank" }
|
||||
|
||||
InitAIUnits = function(house)
|
||||
LastHarvesterEaten[house] = true
|
||||
IdlingUnits[house] = Reinforcements.Reinforce(house, InitialReinforcements[house.InternalName], InitialReinforcementsPaths[house.InternalName])
|
||||
|
||||
DefendAndRepairBase(house, Base[house.InternalName], 0.75, AttackGroupSize[Difficulty])
|
||||
end
|
||||
|
||||
ActivateAI = function()
|
||||
InitAIUnits(atreides)
|
||||
InitAIUnits(harkonnen)
|
||||
|
||||
local delay = function() return Utils.RandomInteger(ProductionDelays[Difficulty][1], ProductionDelays[Difficulty][2] + 1) end
|
||||
local infantryToBuild = function() return { Utils.Random(EnemyInfantryTypes) } end
|
||||
local vehiclesToBuild = function() return { Utils.Random(EnemyVehicleTypes) } end
|
||||
local tanksToBuildAtreides = function() return { Utils.Random(AtreidesTankTypes) } end
|
||||
local tanksToBuildHarkonnen = function() return { Utils.Random(HarkonnenTankTypes) } end
|
||||
local attackTresholdSize = AttackGroupSize[Difficulty] * 2.5
|
||||
|
||||
ProduceUnits(atreides, ABarracks, delay, infantryToBuild, AttackGroupSize[Difficulty], attackTresholdSize)
|
||||
ProduceUnits(atreides, ALightFactory, delay, vehiclesToBuild, AttackGroupSize[Difficulty], attackTresholdSize)
|
||||
ProduceUnits(atreides, AHeavyFactory, delay, tanksToBuildAtreides, AttackGroupSize[Difficulty], attackTresholdSize)
|
||||
|
||||
ProduceUnits(harkonnen, HBarracks, delay, infantryToBuild, AttackGroupSize[Difficulty], attackTresholdSize)
|
||||
ProduceUnits(harkonnen, HHeavyFactory, delay, tanksToBuildHarkonnen, AttackGroupSize[Difficulty], attackTresholdSize)
|
||||
end
|
||||
275
mods/d2k/maps/ordos-06a/ordos06a.lua
Normal file
@@ -0,0 +1,275 @@
|
||||
--[[
|
||||
Copyright 2007-2020 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.
|
||||
]]
|
||||
|
||||
Base =
|
||||
{
|
||||
Atreides = { AConyard, APower1, APower2, APower3, APower4, APower5, APower6, APower7, APower8, APower9, APower10, APower11, APower12, ABarracks, ARefinery, ALightFactory, AHeavyFactory, ARepair, AResearch, AGunt1, AGunt2, ARock1, ARock2, ARock3, ARock4 },
|
||||
Harkonnen = { HConyard, HPower1, HPower2, HPower3, HPower4, HPower5, HPower6, HPower7, HPower8, HPower9, HPower10, HBarracks, HRefinery, HOutpost, HHeavyFactory, HGunt1, HGunt2, HGunt3, HGunt4, HRock, HSilo1, HSilo2, HSilo3 }
|
||||
}
|
||||
|
||||
AtreidesReinforcements =
|
||||
{
|
||||
easy =
|
||||
{
|
||||
{ "light_inf", "light_inf", "light_inf", "trooper", "trooper" },
|
||||
{ "quad", "quad", "combat_tank_a" },
|
||||
{ "light_inf", "light_inf", "light_inf", "trooper", "trooper", "quad", "quad" }
|
||||
},
|
||||
|
||||
normal =
|
||||
{
|
||||
{ "light_inf", "light_inf", "light_inf", "trooper", "trooper", "quad", "quad" },
|
||||
{ "quad", "quad", "combat_tank_a", "combat_tank_a" },
|
||||
{ "light_inf", "light_inf", "light_inf", "trooper", "trooper", "quad", "quad", "quad" },
|
||||
{ "combat_tank_a", "combat_tank_a", "combat_tank_a" }
|
||||
},
|
||||
|
||||
hard =
|
||||
{
|
||||
{ "light_inf", "light_inf", "light_inf", "trooper", "trooper", "trooper", "quad", "quad" },
|
||||
{ "quad", "quad", "quad", "combat_tank_a", "combat_tank_a" },
|
||||
{ "light_inf", "light_inf", "light_inf", "trooper", "trooper", "quad", "quad", "quad", "quad" },
|
||||
{ "combat_tank_a", "combat_tank_a", "combat_tank_a", "quad" },
|
||||
{ "combat_tank_a", "combat_tank_a", "missile_tank", "siege_tank" }
|
||||
}
|
||||
}
|
||||
|
||||
HarkonnenReinforcements =
|
||||
{
|
||||
easy =
|
||||
{
|
||||
{ "quad", "trike", "trike" },
|
||||
{ "light_inf", "light_inf", "light_inf", "trooper", "trooper" },
|
||||
{ "combat_tank_h", "quad" },
|
||||
{ "trooper", "trooper", "trooper", "trooper", "trooper" }
|
||||
},
|
||||
|
||||
normal =
|
||||
{
|
||||
{ "combat_tank_h", "combat_tank_h", "trike", "trike" },
|
||||
{ "light_inf", "light_inf", "light_inf", "light_inf", "light_inf", "trooper", "trooper" },
|
||||
{ "combat_tank_h", "quad", "quad" },
|
||||
{ "trooper", "trooper", "trooper", "trooper", "trooper", "trooper", "trooper" },
|
||||
{ "trike", "trike", "quad", "siege_tank" }
|
||||
},
|
||||
|
||||
hard =
|
||||
{
|
||||
{ "combat_tank_h", "combat_tank_h", "trike", "trike", "trike" },
|
||||
{ "light_inf", "light_inf", "light_inf", "light_inf", "light_inf", "light_inf", "trooper", "trooper" },
|
||||
{ "combat_tank_h", "combat_tank_h", "quad", "quad" },
|
||||
{ "trooper", "trooper", "trooper", "trooper", "trooper", "trooper", "trooper", "trooper" },
|
||||
{ "trike", "trike", "quad", "quad", "siege_tank" },
|
||||
{ "missile_tank", "missile_tank", "missile_tank", "missile_tank" }
|
||||
}
|
||||
}
|
||||
|
||||
IxianReinforcements =
|
||||
{
|
||||
easy = { "deviator", "deviator", "missile_tank", "missile_tank", "missile_tank", "siege_tank", "siege_tank", "combat_tank_o", "combat_tank_o" },
|
||||
normal = { "deviator", "deviator", "missile_tank", "missile_tank", "missile_tank", "siege_tank", "siege_tank", "combat_tank_o" },
|
||||
hard = { "deviator", "deviator", "missile_tank", "missile_tank", "siege_tank", "siege_tank", "combat_tank_o" }
|
||||
}
|
||||
|
||||
EnemyAttackDelay =
|
||||
{
|
||||
easy = DateTime.Minutes(5) + DateTime.Seconds(15),
|
||||
normal = DateTime.Minutes(3) + DateTime.Seconds(15),
|
||||
hard = DateTime.Minutes(1) + DateTime.Seconds(30)
|
||||
}
|
||||
|
||||
AtreidesPaths =
|
||||
{
|
||||
{ AtreidesEntry2.Location, AtreidesRally2.Location },
|
||||
{ AtreidesEntry3.Location, AtreidesRally3.Location },
|
||||
{ AtreidesEntry4.Location, AtreidesRally4.Location }
|
||||
}
|
||||
|
||||
HarkonnenPaths =
|
||||
{
|
||||
{ HarkonnenEntry2.Location, HarkonnenRally2.Location },
|
||||
{ HarkonnenEntry3.Location, HarkonnenRally3.Location },
|
||||
{ HarkonnenEntry4.Location, HarkonnenRally4.Location },
|
||||
{ HarkonnenEntry5.Location, HarkonnenRally5.Location },
|
||||
{ HarkonnenEntry6.Location, HarkonnenRally6.Location },
|
||||
{ HarkonnenEntry7.Location, HarkonnenRally7.Location }
|
||||
}
|
||||
|
||||
AtreidesAttackWaves =
|
||||
{
|
||||
easy = 3,
|
||||
normal = 4,
|
||||
hard = 5
|
||||
}
|
||||
|
||||
HarkonnenAttackWaves =
|
||||
{
|
||||
easy = 4,
|
||||
normal = 5,
|
||||
hard = 6
|
||||
}
|
||||
|
||||
InitialReinforcements =
|
||||
{
|
||||
Atreides = { "combat_tank_a", "quad", "quad", "trike", "trike" },
|
||||
Harkonnen = { "trooper", "trooper", "trooper", "trooper", "trooper", "combat_tank_h" }
|
||||
}
|
||||
|
||||
InitialReinforcementsPaths =
|
||||
{
|
||||
Atreides = { AtreidesEntry1.Location, AtreidesRally1.Location },
|
||||
Harkonnen = { HarkonnenEntry1.Location, HarkonnenRally1.Location }
|
||||
}
|
||||
|
||||
InitialContrabandTimes =
|
||||
{
|
||||
easy = DateTime.Minutes(10),
|
||||
normal = DateTime.Minutes(15),
|
||||
hard = DateTime.Minutes(20)
|
||||
}
|
||||
|
||||
ContrabandTimes =
|
||||
{
|
||||
easy = DateTime.Minutes(4),
|
||||
normal = DateTime.Minutes(6),
|
||||
hard = DateTime.Minutes(7)
|
||||
}
|
||||
|
||||
SendContraband = function()
|
||||
Media.PlaySpeechNotification(player, "Reinforce")
|
||||
|
||||
for i = 0, 6 do
|
||||
local c = player.Color
|
||||
if i % 2 == 0 then
|
||||
c = HSLColor.White
|
||||
end
|
||||
|
||||
Trigger.AfterDelay(DateTime.Seconds(i), function() UserInterface.SetMissionText("Ixian reinforcements have arrived!", c) end)
|
||||
end
|
||||
Trigger.AfterDelay(DateTime.Seconds(6), function()
|
||||
TimerTicks = ContrabandTimes[Difficulty]
|
||||
end)
|
||||
|
||||
local entryPath = { CPos.New(82, OStarport.Location.Y + 1), OStarport.Location + CVec.New(1, 1) }
|
||||
local exitPath = { CPos.New(2, OStarport.Location.Y + 1) }
|
||||
Reinforcements.ReinforceWithTransport(player, "frigate", IxianReinforcements[Difficulty], entryPath, exitPath)
|
||||
end
|
||||
|
||||
Hunt = function(house)
|
||||
Trigger.OnAllKilledOrCaptured(Base[house.InternalName], function()
|
||||
Utils.Do(house.GetGroundAttackers(), IdleHunt)
|
||||
end)
|
||||
end
|
||||
|
||||
CheckHarvester = function(house)
|
||||
if DateTime.GameTime % DateTime.Seconds(10) == 0 and LastHarvesterEaten[house] then
|
||||
local units = house.GetActorsByType("harvester")
|
||||
|
||||
if #units > 0 then
|
||||
LastHarvesterEaten[house] = false
|
||||
ProtectHarvester(units[1], house, AttackGroupSize[Difficulty])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Tick = function()
|
||||
if not player.IsObjectiveCompleted(KillAtreides) and atreides.HasNoRequiredUnits() then
|
||||
Media.DisplayMessage("The Atreides have been annihilated!", "Mentat")
|
||||
player.MarkCompletedObjective(KillAtreides)
|
||||
DestroyCarryalls(atreides)
|
||||
|
||||
if player.IsObjectiveCompleted(KillHarkonnen) then
|
||||
player.MarkCompletedObjective(GuardStarport)
|
||||
end
|
||||
end
|
||||
|
||||
if not player.IsObjectiveCompleted(KillHarkonnen) and harkonnen.HasNoRequiredUnits() then
|
||||
Media.DisplayMessage("The Harkonnen have been annihilated!", "Mentat")
|
||||
player.MarkCompletedObjective(KillHarkonnen)
|
||||
DestroyCarryalls(harkonnen)
|
||||
|
||||
if player.IsObjectiveCompleted(KillAtreides) then
|
||||
player.MarkCompletedObjective(GuardStarport)
|
||||
end
|
||||
end
|
||||
|
||||
if TimerTicks and TimerTicks > 0 then
|
||||
TimerTicks = TimerTicks - 1
|
||||
|
||||
if TimerTicks == 0 then
|
||||
if not FirstIxiansArrived then
|
||||
Media.DisplayMessage("Deliveries beginning to arrive. Massive reinforcements expected!", "Mentat")
|
||||
end
|
||||
|
||||
FirstIxiansArrived = true
|
||||
SendContraband()
|
||||
else
|
||||
local text = "Initial"
|
||||
if FirstIxiansArrived then
|
||||
text = "Additional"
|
||||
end
|
||||
|
||||
UserInterface.SetMissionText(text .. " reinforcements will arrive in " .. Utils.FormatTime(TimerTicks), player.Color)
|
||||
end
|
||||
end
|
||||
|
||||
CheckHarvester(atreides)
|
||||
CheckHarvester(harkonnen)
|
||||
end
|
||||
|
||||
WorldLoaded = function()
|
||||
atreides = Player.GetPlayer("Atreides")
|
||||
harkonnen = Player.GetPlayer("Harkonnen")
|
||||
player = Player.GetPlayer("Ordos")
|
||||
|
||||
InitObjectives(player)
|
||||
GuardStarport = player.AddObjective("Defend the Starport.")
|
||||
KillAtreides = player.AddObjective("Destroy the Atreides.")
|
||||
KillHarkonnen = player.AddObjective("Destroy the Harkonnen.")
|
||||
|
||||
Camera.Position = OConyard.CenterPosition
|
||||
EnemyAttackLocations = { OConyard.Location, OStarport.Location }
|
||||
|
||||
Trigger.OnRemovedFromWorld(OStarport, function()
|
||||
player.MarkFailedObjective(GuardStarport)
|
||||
end)
|
||||
|
||||
Trigger.AfterDelay(DateTime.Seconds(2), function()
|
||||
TimerTicks = InitialContrabandTimes[Difficulty]
|
||||
Media.DisplayMessage("The first batch of Ixian reinforcements will arrive in " .. Utils.FormatTime(TimerTicks) .. ".", "Mentat")
|
||||
end)
|
||||
|
||||
Hunt(atreides)
|
||||
Hunt(harkonnen)
|
||||
|
||||
local atreidesPath = function() return Utils.Random(AtreidesPaths) end
|
||||
local harkonnenPath = function() return Utils.Random(HarkonnenPaths) end
|
||||
local atreidesCondition = function() return player.IsObjectiveCompleted(KillAtreides) end
|
||||
local harkonnenCondition = function() return player.IsObjectiveCompleted(KillHarkonnen) end
|
||||
local huntFunction = function(unit)
|
||||
unit.AttackMove(Utils.Random(EnemyAttackLocations))
|
||||
IdleHunt(unit)
|
||||
end
|
||||
local announcementFunction = function()
|
||||
Media.DisplayMessage("Enemy reinforcements have arrived.", "Mentat")
|
||||
end
|
||||
|
||||
SendCarryallReinforcements(atreides, 0, AtreidesAttackWaves[Difficulty], EnemyAttackDelay[Difficulty], atreidesPath, AtreidesReinforcements[Difficulty], atreidesCondition, huntFunction, announcementFunction)
|
||||
|
||||
Trigger.AfterDelay(Utils.RandomInteger(DateTime.Seconds(45), DateTime.Minutes(1) + DateTime.Seconds(15)), function()
|
||||
SendCarryallReinforcements(harkonnen, 0, HarkonnenAttackWaves[Difficulty], EnemyAttackDelay[Difficulty], harkonnenPath, HarkonnenReinforcements[Difficulty], harkonnenCondition, huntFunction, announcementFunction)
|
||||
end)
|
||||
|
||||
Actor.Create("upgrade.barracks", true, { Owner = atreides })
|
||||
Actor.Create("upgrade.light", true, { Owner = atreides })
|
||||
Actor.Create("upgrade.heavy", true, { Owner = atreides })
|
||||
Actor.Create("upgrade.barracks", true, { Owner = harkonnen })
|
||||
Actor.Create("upgrade.heavy", true, { Owner = harkonnen })
|
||||
Trigger.AfterDelay(0, ActivateAI)
|
||||
end
|
||||