Compare commits
37 Commits
devtest-20
...
devtest-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2223578c16 | ||
|
|
c3f2f513b5 | ||
|
|
5091280083 | ||
|
|
06fc82b8bd | ||
|
|
e4dccccc90 | ||
|
|
f9c7b0afe9 | ||
|
|
c18d509ddd | ||
|
|
1782794bac | ||
|
|
5e74e58b22 | ||
|
|
fef7a018f2 | ||
|
|
1d4891b017 | ||
|
|
e9a803e3c1 | ||
|
|
4abe7d5895 | ||
|
|
cd68db5c11 | ||
|
|
ae9f437bb8 | ||
|
|
a047893049 | ||
|
|
5b2733decf | ||
|
|
ff60540fac | ||
|
|
a68a91bb39 | ||
|
|
0bf8d2241f | ||
|
|
06850e6271 | ||
|
|
62fa3b7c9c | ||
|
|
ce09b402d0 | ||
|
|
6e7ad9df25 | ||
|
|
fb20479379 | ||
|
|
9d181e88d2 | ||
|
|
e90fc1ef39 | ||
|
|
ce013f17d6 | ||
|
|
78253ce284 | ||
|
|
2671e40c1d | ||
|
|
04cda69ef9 | ||
|
|
b4c483ce1a | ||
|
|
9a9f58d744 | ||
|
|
d38fe542a2 | ||
|
|
80503fbf36 | ||
|
|
99a23b4056 | ||
|
|
13a7de4b6b |
13
.github/workflows/ci.yaml
vendored
13
.github/workflows/ci.yaml
vendored
@@ -18,7 +18,6 @@ jobs:
|
||||
run: |
|
||||
mono --version
|
||||
make check
|
||||
mono ~/.nuget/packages/nunit.consolerunner/3.11.1/tools/nunit3-console.exe --noresult bin/OpenRA.Test.dll
|
||||
|
||||
- name: Check Mods
|
||||
run: |
|
||||
@@ -27,18 +26,26 @@ jobs:
|
||||
make test
|
||||
|
||||
windows:
|
||||
name: Windows (Framework 4.7)
|
||||
name: Windows (Net 5.0)
|
||||
runs-on: windows-2019
|
||||
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install .NET 5
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '5.0.x'
|
||||
|
||||
- name: Check Code
|
||||
shell: powershell
|
||||
run: |
|
||||
# Work around runtime failures on the GH Actions runner
|
||||
dotnet nuget locals all --clear
|
||||
.\make.ps1 check
|
||||
Invoke-Expression "$home\.nuget\packages\nunit.consolerunner\3.11.1\tools\nunit3-console.exe --noresult bin/OpenRA.Test.dll"
|
||||
dotnet build OpenRA.Test\OpenRA.Test.csproj -c Debug --nologo -p:TargetPlatform=win-x64
|
||||
dotnet test bin\OpenRA.Test.dll --test-adapter-path:.
|
||||
|
||||
- name: Check Mods
|
||||
run: |
|
||||
|
||||
12
.github/workflows/packaging.yml
vendored
12
.github/workflows/packaging.yml
vendored
@@ -39,6 +39,11 @@ jobs:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install .NET 5
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '5.0.x'
|
||||
|
||||
- name: Prepare Environment
|
||||
run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
|
||||
|
||||
@@ -69,10 +74,15 @@ jobs:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install .NET 5
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '5.0.x'
|
||||
|
||||
- name: Prepare Environment
|
||||
run: |
|
||||
echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
|
||||
sudo apt install nsis
|
||||
sudo apt install nsis wine64
|
||||
|
||||
- name: Package Installers
|
||||
run: |
|
||||
|
||||
20
.vscode/launch.json
vendored
20
.vscode/launch.json
vendored
@@ -11,9 +11,8 @@
|
||||
"type": "mono"
|
||||
},
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=cnc"],
|
||||
"program": "${workspaceRoot}/bin/OpenRA.exe",
|
||||
"args": ["Game.Mod=cnc", "Engine.EngineDir=.."],
|
||||
"preLaunchTask": "build",
|
||||
},
|
||||
{
|
||||
@@ -26,9 +25,8 @@
|
||||
"type": "mono"
|
||||
},
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=ra"],
|
||||
"program": "${workspaceRoot}/bin/OpenRA.exe",
|
||||
"args": ["Game.Mod=ra", "Engine.EngineDir=.."],
|
||||
"preLaunchTask": "build",
|
||||
},
|
||||
{
|
||||
@@ -41,9 +39,8 @@
|
||||
"type": "mono"
|
||||
},
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=d2k"],
|
||||
"program": "${workspaceRoot}/bin/OpenRA.exe",
|
||||
"args": ["Game.Mod=d2k", "Engine.EngineDir=.."],
|
||||
"preLaunchTask": "build",
|
||||
},
|
||||
{
|
||||
@@ -56,9 +53,8 @@
|
||||
"type": "mono"
|
||||
},
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=ts"],
|
||||
"program": "${workspaceRoot}/bin/OpenRA.exe",
|
||||
"args": ["Game.Mod=ts", "Engine.EngineDir=.."],
|
||||
"preLaunchTask": "build",
|
||||
},
|
||||
]
|
||||
|
||||
10
INSTALL.md
10
INSTALL.md
@@ -8,18 +8,16 @@ Windows
|
||||
|
||||
Compiling OpenRA requires the following dependencies:
|
||||
* [Windows PowerShell >= 4.0](http://microsoft.com/powershell) (included by default in recent Windows 10 versions)
|
||||
* [.NET Framework 4.7.2 (Developer Pack)](https://dotnet.microsoft.com/download/dotnet-framework/net472) (or via Visual Studio 2017)
|
||||
* [.NET Core 2.2 SDK](https://dotnet.microsoft.com/download/dotnet-core/2.2) (or via Visual Studio 2017)
|
||||
* [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0) (or via Visual Studio)
|
||||
|
||||
|
||||
To compile OpenRA, open the `OpenRA.sln` solution in the main folder, build it from the command-line with MSBuild or use the Makefile analogue command `make all` scripted in PowerShell syntax.
|
||||
To compile OpenRA, open the `OpenRA.sln` solution in the main folder, build it from the command-line with `dotnet` or use the Makefile analogue command `make all` scripted in PowerShell syntax.
|
||||
|
||||
Run the game with `launch-game.cmd`. It can be handed arguments that specify the exact mod one wishes to run, for example, run `launch-game.cmd Game.Mod=ra` to launch Red Alert, `launch-game.cmd Game.Mod=cnc` to start Tiberian dawn or `launch-game.cmd Game.Mod=d2k` to launch Dune 2000.
|
||||
|
||||
Linux
|
||||
=====
|
||||
|
||||
Mono, version 5.18 or later, is required to compile OpenRA. You can add the [upstream mono repository](https://www.mono-project.com/download/stable/#download-lin) for your distro to obtain the latest version if your system packages are not sufficient.
|
||||
Mono, version 6.4 or later, is required to compile OpenRA. You can add the [upstream mono repository](https://www.mono-project.com/download/stable/#download-lin) for your distro to obtain the latest version if your system packages are not sufficient.
|
||||
|
||||
To compile OpenRA, run `make` from the command line. After this one can run the game with `./launch-game.sh`. It is also possible to specify the mod you wish to run from the command line, e.g. with `./launch-game.sh Game.Mod=ts` if you wish to try the experimental Tiberian Sun mod.
|
||||
|
||||
@@ -99,7 +97,7 @@ macOS
|
||||
=====
|
||||
|
||||
Before compiling OpenRA you must install the following dependencies:
|
||||
* [Mono >= 5.18](https://www.mono-project.com/download/stable/#download-mac)
|
||||
* [Mono >= 6.4](https://www.mono-project.com/download/stable/#download-mac)
|
||||
|
||||
To compile OpenRA, run `make` from the command line. Run with `./launch-game.sh`.
|
||||
|
||||
|
||||
23
Makefile
23
Makefile
@@ -25,18 +25,6 @@
|
||||
# make help
|
||||
#
|
||||
|
||||
############################## TOOLCHAIN ###############################
|
||||
#
|
||||
# List of .NET assemblies that we can guarantee exist
|
||||
WHITELISTED_OPENRA_ASSEMBLIES = OpenRA.exe OpenRA.Utility.exe OpenRA.Server.exe OpenRA.Platforms.Default.dll OpenRA.Game.dll OpenRA.Mods.Common.dll OpenRA.Mods.Cnc.dll OpenRA.Mods.D2k.dll
|
||||
|
||||
# These are explicitly shipped alongside our core files by the packaging script
|
||||
WHITELISTED_THIRDPARTY_ASSEMBLIES = ICSharpCode.SharpZipLib.dll FuzzyLogicLibrary.dll Eluant.dll BeaconLib.dll Open.Nat.dll SDL2-CS.dll OpenAL-CS.Core.dll DiscordRPC.dll Newtonsoft.Json.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
|
||||
WHITELISTED_CORE_ASSEMBLIES = mscorlib.dll System.dll System.Configuration.dll System.Core.dll System.Numerics.dll System.Security.dll System.Xml.dll Mono.Security.dll netstandard.dll
|
||||
|
||||
######################### UTILITIES/SETTINGS ###########################
|
||||
#
|
||||
# Install locations for local installs and downstream packaging
|
||||
@@ -78,13 +66,13 @@ endif
|
||||
endif
|
||||
endif
|
||||
|
||||
OPENRA_UTILITY = ENGINE_DIR=".." $(MONO) --debug bin/OpenRA.Utility.exe
|
||||
OPENRA_UTILITY = ENGINE_DIR=".." $(MONO) --debug bin/OpenRA.Utility.dll
|
||||
|
||||
##################### DEVELOPMENT BUILDS AND TESTS #####################
|
||||
#
|
||||
all:
|
||||
@command -v $(firstword $(MSBUILD)) >/dev/null || (echo "OpenRA requires the '$(MSBUILD)' tool provided by Mono >= 5.18."; exit 1)
|
||||
@$(MSBUILD) -t:Build -restore -p:Configuration=Release -p:TargetPlatform=$(TARGETPLATFORM)
|
||||
@$(MSBUILD) -t:Build -restore -p:Configuration=Release -p:TargetPlatform=$(TARGETPLATFORM) -p:Mono=true -p:DefineConstants="MONO"
|
||||
ifeq ($(TARGETPLATFORM), unix-generic)
|
||||
@./configure-system-libraries.sh
|
||||
endif
|
||||
@@ -92,16 +80,13 @@ endif
|
||||
|
||||
clean:
|
||||
@-$(RM_RF) ./bin ./*/bin ./*/obj
|
||||
@$(MSBUILD) -t:Clean
|
||||
@$(MSBUILD) -t:Clean -p:Mono=true
|
||||
@-$(RM_F) IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP
|
||||
|
||||
check:
|
||||
@echo
|
||||
@echo "Compiling in debug mode..."
|
||||
@$(MSBUILD) -t:build -restore -p:Configuration=Debug
|
||||
@echo
|
||||
@echo "Checking runtime assemblies..."
|
||||
@$(OPENRA_UTILITY) all --check-runtime-assemblies $(WHITELISTED_OPENRA_ASSEMBLIES) $(WHITELISTED_THIRDPARTY_ASSEMBLIES) $(WHITELISTED_CORE_ASSEMBLIES)
|
||||
@$(MSBUILD) -t:build -restore -p:Configuration=Debug -p:TargetPlatform=$(TARGETPLATFORM) -p:Mono=true -p:DefineConstants="MONO"
|
||||
@echo
|
||||
@echo "Checking for explicit interface violations..."
|
||||
@$(OPENRA_UTILITY) all --check-explicit-interfaces
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace OpenRA.Activities
|
||||
// drop valid activities queued after it. Walk the queue until we find a valid activity or
|
||||
// (more likely) run out of activities.
|
||||
while (first != null && first.State == ActivityState.Done)
|
||||
first = first.NextActivity;
|
||||
first = first.nextActivity;
|
||||
|
||||
return first;
|
||||
}
|
||||
@@ -120,7 +120,8 @@ namespace OpenRA.Activities
|
||||
lastRun = Tick(self);
|
||||
|
||||
// Avoid a single tick delay if the childactivity was just queued.
|
||||
if (ChildActivity != null && ChildActivity.State == ActivityState.Queued)
|
||||
var ca = ChildActivity;
|
||||
if (ca != null && ca.State == ActivityState.Queued)
|
||||
{
|
||||
if (ChildHasPriority)
|
||||
lastRun = TickChild(self) && finishing;
|
||||
@@ -206,18 +207,18 @@ namespace OpenRA.Activities
|
||||
|
||||
public void Queue(Activity activity)
|
||||
{
|
||||
if (NextActivity != null)
|
||||
NextActivity.Queue(activity);
|
||||
else
|
||||
NextActivity = activity;
|
||||
var it = this;
|
||||
while (it.nextActivity != null)
|
||||
it = it.nextActivity;
|
||||
it.nextActivity = activity;
|
||||
}
|
||||
|
||||
public void QueueChild(Activity activity)
|
||||
{
|
||||
if (ChildActivity != null)
|
||||
ChildActivity.Queue(activity);
|
||||
if (childActivity != null)
|
||||
childActivity.Queue(activity);
|
||||
else
|
||||
ChildActivity = activity;
|
||||
childActivity = activity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -269,15 +270,21 @@ namespace OpenRA.Activities
|
||||
|
||||
public IEnumerable<T> ActivitiesImplementing<T>(bool includeChildren = true) where T : IActivityInterface
|
||||
{
|
||||
if (includeChildren && ChildActivity != null)
|
||||
foreach (var a in ChildActivity.ActivitiesImplementing<T>())
|
||||
yield return a;
|
||||
// Skips Done child and next activities
|
||||
if (includeChildren)
|
||||
{
|
||||
var ca = ChildActivity;
|
||||
if (ca != null)
|
||||
foreach (var a in ca.ActivitiesImplementing<T>())
|
||||
yield return a;
|
||||
}
|
||||
|
||||
if (this is T)
|
||||
yield return (T)(object)this;
|
||||
|
||||
if (NextActivity != null)
|
||||
foreach (var a in NextActivity.ActivitiesImplementing<T>())
|
||||
var na = NextActivity;
|
||||
if (na != null)
|
||||
foreach (var a in na.ActivitiesImplementing<T>())
|
||||
yield return a;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,17 +66,11 @@ namespace OpenRA
|
||||
|
||||
static readonly ConcurrentCache<Type, FieldLoadInfo[]> TypeLoadInfo =
|
||||
new ConcurrentCache<Type, FieldLoadInfo[]>(BuildTypeLoadInfo);
|
||||
static readonly ConcurrentCache<MemberInfo, bool> MemberHasTranslateAttribute =
|
||||
new ConcurrentCache<MemberInfo, bool>(member => member.HasAttribute<TranslateAttribute>());
|
||||
|
||||
static readonly ConcurrentCache<string, BooleanExpression> BooleanExpressionCache =
|
||||
new ConcurrentCache<string, BooleanExpression>(expression => new BooleanExpression(expression));
|
||||
static readonly ConcurrentCache<string, IntegerExpression> IntegerExpressionCache =
|
||||
new ConcurrentCache<string, IntegerExpression>(expression => new IntegerExpression(expression));
|
||||
|
||||
static readonly object TranslationsLock = new object();
|
||||
static Dictionary<string, string> translations;
|
||||
|
||||
public static void Load(object self, MiniYaml my)
|
||||
{
|
||||
var loadInfo = TypeLoadInfo[self.GetType()];
|
||||
@@ -213,11 +207,7 @@ namespace OpenRA
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
else if (fieldType == typeof(string))
|
||||
{
|
||||
if (field != null && MemberHasTranslateAttribute[field] && value != null)
|
||||
return Regex.Replace(value, "@[^@]+@", m => Translate(m.Value.Substring(1, m.Value.Length - 2)), RegexOptions.Compiled);
|
||||
return value;
|
||||
}
|
||||
else if (fieldType == typeof(Color))
|
||||
{
|
||||
if (value != null && Color.TryParse(value, out var color))
|
||||
@@ -694,34 +684,8 @@ namespace OpenRA
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static string Translate(string key)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key))
|
||||
return key;
|
||||
|
||||
lock (TranslationsLock)
|
||||
{
|
||||
if (translations == null)
|
||||
return key;
|
||||
|
||||
if (!translations.TryGetValue(key, out var value))
|
||||
return key;
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetTranslations(IDictionary<string, string> translations)
|
||||
{
|
||||
lock (TranslationsLock)
|
||||
FieldLoader.translations = new Dictionary<string, string>(translations);
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class TranslateAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public sealed class FieldFromYamlKeyAttribute : FieldLoader.SerializeAttribute
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@ using System.Net;
|
||||
using System.Text;
|
||||
using ICSharpCode.SharpZipLib.Checksum;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
@@ -25,12 +26,15 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
static readonly byte[] Signature = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
|
||||
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
public Color[] Palette { get; set; }
|
||||
public byte[] Data { get; set; }
|
||||
public int Width { get; private set; }
|
||||
public int Height { get; private set; }
|
||||
public Color[] Palette { get; private set; }
|
||||
public byte[] Data { get; private set; }
|
||||
public SpriteFrameType Type { get; private set; }
|
||||
public Dictionary<string, string> EmbeddedData = new Dictionary<string, string>();
|
||||
|
||||
public int PixelStride { get { return Type == SpriteFrameType.Indexed8 ? 1 : Type == SpriteFrameType.Rgb24 ? 3 : 4; } }
|
||||
|
||||
public Png(Stream s)
|
||||
{
|
||||
if (!Verify(s))
|
||||
@@ -38,9 +42,8 @@ namespace OpenRA.FileFormats
|
||||
|
||||
s.Position += 8;
|
||||
var headerParsed = false;
|
||||
var isPaletted = false;
|
||||
var is24Bit = false;
|
||||
var data = new List<byte>();
|
||||
Type = SpriteFrameType.Rgba32;
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -65,14 +68,12 @@ namespace OpenRA.FileFormats
|
||||
|
||||
var bitDepth = ms.ReadUInt8();
|
||||
var colorType = (PngColorType)ms.ReadByte();
|
||||
isPaletted = IsPaletted(bitDepth, colorType);
|
||||
is24Bit = colorType == PngColorType.Color;
|
||||
if (IsPaletted(bitDepth, colorType))
|
||||
Type = SpriteFrameType.Indexed8;
|
||||
else if (colorType == PngColorType.Color)
|
||||
Type = SpriteFrameType.Rgb24;
|
||||
|
||||
var dataLength = Width * Height;
|
||||
if (!isPaletted)
|
||||
dataLength *= 4;
|
||||
|
||||
Data = new byte[dataLength];
|
||||
Data = new byte[Width * Height * PixelStride];
|
||||
|
||||
var compression = ms.ReadByte();
|
||||
/*var filter = */ms.ReadByte();
|
||||
@@ -133,39 +134,28 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
using (var ds = new InflaterInputStream(ns))
|
||||
{
|
||||
var pxStride = isPaletted ? 1 : is24Bit ? 3 : 4;
|
||||
var srcStride = Width * pxStride;
|
||||
var destStride = Width * (isPaletted ? 1 : 4);
|
||||
var pxStride = PixelStride;
|
||||
var rowStride = Width * pxStride;
|
||||
|
||||
var prevLine = new byte[srcStride];
|
||||
var prevLine = new byte[rowStride];
|
||||
for (var y = 0; y < Height; y++)
|
||||
{
|
||||
var filter = (PngFilter)ds.ReadByte();
|
||||
var line = ds.ReadBytes(srcStride);
|
||||
var line = ds.ReadBytes(rowStride);
|
||||
|
||||
for (var i = 0; i < srcStride; i++)
|
||||
for (var i = 0; i < rowStride; i++)
|
||||
line[i] = i < pxStride
|
||||
? UnapplyFilter(filter, line[i], 0, prevLine[i], 0)
|
||||
: UnapplyFilter(filter, line[i], line[i - pxStride], prevLine[i], prevLine[i - pxStride]);
|
||||
|
||||
if (is24Bit)
|
||||
{
|
||||
// Fold alpha channel into RGB data
|
||||
for (var i = 0; i < line.Length / 3; i++)
|
||||
{
|
||||
Array.Copy(line, 3 * i, Data, y * destStride + 4 * i, 3);
|
||||
Data[y * destStride + 4 * i + 3] = 255;
|
||||
}
|
||||
}
|
||||
else
|
||||
Array.Copy(line, 0, Data, y * destStride, line.Length);
|
||||
Array.Copy(line, 0, Data, y * rowStride, rowStride);
|
||||
|
||||
prevLine = line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isPaletted && Palette == null)
|
||||
if (Type == SpriteFrameType.Indexed8 && Palette == null)
|
||||
throw new InvalidDataException("Non-Palette indexed PNG are not supported.");
|
||||
|
||||
return;
|
||||
@@ -175,7 +165,7 @@ namespace OpenRA.FileFormats
|
||||
}
|
||||
}
|
||||
|
||||
public Png(byte[] data, int width, int height, Color[] palette = null,
|
||||
public Png(byte[] data, SpriteFrameType type, int width, int height, Color[] palette = null,
|
||||
Dictionary<string, string> embeddedData = null)
|
||||
{
|
||||
var expectLength = width * height;
|
||||
@@ -185,11 +175,46 @@ namespace OpenRA.FileFormats
|
||||
if (data.Length != expectLength)
|
||||
throw new InvalidDataException("Input data does not match expected length");
|
||||
|
||||
Type = type;
|
||||
Width = width;
|
||||
Height = height;
|
||||
|
||||
Palette = palette;
|
||||
Data = data;
|
||||
switch (type)
|
||||
{
|
||||
case SpriteFrameType.Indexed8:
|
||||
case SpriteFrameType.Rgba32:
|
||||
case SpriteFrameType.Rgb24:
|
||||
{
|
||||
// Data is already in a compatible format
|
||||
Data = data;
|
||||
if (type == SpriteFrameType.Indexed8)
|
||||
Palette = palette;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SpriteFrameType.Bgra32:
|
||||
case SpriteFrameType.Bgr24:
|
||||
{
|
||||
// Convert to big endian
|
||||
Data = new byte[data.Length];
|
||||
var stride = PixelStride;
|
||||
for (var i = 0; i < width * height; i++)
|
||||
{
|
||||
Data[stride * i] = data[stride * i + 2];
|
||||
Data[stride * i + 1] = data[stride * i + 1];
|
||||
Data[stride * i + 2] = data[stride * i + 0];
|
||||
|
||||
if (type == SpriteFrameType.Bgra32)
|
||||
Data[stride * i + 3] = data[stride * i + 3];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new InvalidDataException("Unhandled SpriteFrameType {0}".F(type));
|
||||
}
|
||||
|
||||
if (embeddedData != null)
|
||||
EmbeddedData = embeddedData;
|
||||
@@ -274,9 +299,8 @@ namespace OpenRA.FileFormats
|
||||
header.Write(IPAddress.HostToNetworkOrder(Height));
|
||||
header.WriteByte(8); // Bit depth
|
||||
|
||||
var colorType = Palette != null
|
||||
? PngColorType.Indexed | PngColorType.Color
|
||||
: PngColorType.Color | PngColorType.Alpha;
|
||||
var colorType = Type == SpriteFrameType.Indexed8 ? PngColorType.Indexed | PngColorType.Color :
|
||||
Type == SpriteFrameType.Rgb24 ? PngColorType.Color : PngColorType.Color | PngColorType.Alpha;
|
||||
header.WriteByte((byte)colorType);
|
||||
|
||||
header.WriteByte(0); // Compression
|
||||
@@ -286,7 +310,7 @@ namespace OpenRA.FileFormats
|
||||
WritePngChunk(output, "IHDR", header);
|
||||
}
|
||||
|
||||
bool alphaPalette = false;
|
||||
var alphaPalette = false;
|
||||
if (Palette != null)
|
||||
{
|
||||
using (var palette = new MemoryStream())
|
||||
@@ -318,12 +342,12 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
using (var compressed = new DeflaterOutputStream(data))
|
||||
{
|
||||
var stride = Width * (Palette != null ? 1 : 4);
|
||||
var rowStride = Width * PixelStride;
|
||||
for (var y = 0; y < Height; y++)
|
||||
{
|
||||
// Write uncompressed scanlines for simplicity
|
||||
compressed.WriteByte(0);
|
||||
compressed.Write(Data, y * stride, stride);
|
||||
compressed.Write(Data, y * rowStride, rowStride);
|
||||
}
|
||||
|
||||
compressed.Flush();
|
||||
|
||||
@@ -297,6 +297,7 @@ namespace OpenRA
|
||||
EngineVersion = "Unknown";
|
||||
|
||||
Console.WriteLine("Engine version is {0}", EngineVersion);
|
||||
Console.WriteLine("Runtime: {0}", Platform.RuntimeVersion);
|
||||
|
||||
// Special case handling of Game.Mod argument: if it matches a real filesystem path
|
||||
// then we use this to override the mod search path, and replace it with the mod id
|
||||
@@ -329,9 +330,16 @@ namespace OpenRA
|
||||
try
|
||||
{
|
||||
var rendererPath = Path.Combine(Platform.BinDir, "OpenRA.Platforms." + p + ".dll");
|
||||
var assembly = Assembly.LoadFile(rendererPath);
|
||||
|
||||
#if !MONO
|
||||
var loader = new AssemblyLoader(rendererPath);
|
||||
var platformType = loader.LoadDefaultAssembly().GetTypes().SingleOrDefault(t => typeof(IPlatform).IsAssignableFrom(t));
|
||||
|
||||
#else
|
||||
var assembly = Assembly.LoadFile(rendererPath);
|
||||
var platformType = assembly.GetTypes().SingleOrDefault(t => typeof(IPlatform).IsAssignableFrom(t));
|
||||
#endif
|
||||
|
||||
if (platformType == null)
|
||||
throw new InvalidOperationException("Platform dll must include exactly one IPlatform implementation.");
|
||||
|
||||
@@ -476,6 +484,8 @@ namespace OpenRA
|
||||
|
||||
ChromeMetrics.TryGet("ChatMessageColor", out chatMessageColor);
|
||||
ChromeMetrics.TryGet("SystemMessageColor", out systemMessageColor);
|
||||
if (!ChromeMetrics.TryGet("SystemMessageLabel", out systemMessageLabel))
|
||||
systemMessageLabel = "Battlefield Control";
|
||||
|
||||
ModData.LoadScreen.StartGame(args);
|
||||
}
|
||||
@@ -545,6 +555,8 @@ namespace OpenRA
|
||||
static volatile ActionQueue delayedActions = new ActionQueue();
|
||||
static Color systemMessageColor = Color.White;
|
||||
static Color chatMessageColor = Color.White;
|
||||
static string systemMessageLabel;
|
||||
|
||||
public static void RunAfterTick(Action a) { delayedActions.Add(a, RunTime); }
|
||||
public static void RunAfterDelay(int delayMilliseconds, Action a) { delayedActions.Add(a, RunTime + delayMilliseconds); }
|
||||
|
||||
@@ -876,7 +888,7 @@ namespace OpenRA
|
||||
|
||||
public static void AddSystemLine(string text)
|
||||
{
|
||||
AddSystemLine("Battlefield Control", text);
|
||||
AddSystemLine(systemMessageLabel, text);
|
||||
}
|
||||
|
||||
public static void AddSystemLine(string name, string text)
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace OpenRA
|
||||
{
|
||||
public class GameSpeed
|
||||
{
|
||||
[Translate]
|
||||
public readonly string Name = "Default";
|
||||
public readonly int Timestep = 40;
|
||||
public readonly int OrderLatency = 3;
|
||||
|
||||
@@ -69,9 +69,16 @@ namespace OpenRA.Graphics
|
||||
// Hotspot is specified relative to the center of the frame
|
||||
var hotspot = f.Offset.ToInt2() - kv.Value.Hotspot - new int2(f.Size) / 2;
|
||||
|
||||
// SheetBuilder expects data in BGRA
|
||||
var data = FrameToBGRA(kv.Key, f, palette);
|
||||
c.Sprites[c.Length++] = sheetBuilder.Add(data, f.Size, 0, hotspot);
|
||||
// Resolve indexed data to real colours
|
||||
var data = f.Data;
|
||||
var type = f.Type;
|
||||
if (type == SpriteFrameType.Indexed8)
|
||||
{
|
||||
data = ConvertIndexedToBgra(kv.Key, f, palette);
|
||||
type = SpriteFrameType.Bgra32;
|
||||
}
|
||||
|
||||
c.Sprites[c.Length++] = sheetBuilder.Add(data, type, f.Size, 0, hotspot);
|
||||
|
||||
// Bounds relative to the hotspot
|
||||
c.Bounds = Rectangle.Union(c.Bounds, new Rectangle(hotspot, f.Size));
|
||||
@@ -217,33 +224,27 @@ namespace OpenRA.Graphics
|
||||
Update();
|
||||
}
|
||||
|
||||
public static byte[] FrameToBGRA(string name, ISpriteFrame frame, ImmutablePalette palette)
|
||||
public static byte[] ConvertIndexedToBgra(string name, ISpriteFrame frame, ImmutablePalette palette)
|
||||
{
|
||||
// Data is already in BGRA format
|
||||
if (frame.Type == SpriteFrameType.BGRA)
|
||||
return frame.Data;
|
||||
if (frame.Type != SpriteFrameType.Indexed8)
|
||||
throw new ArgumentException("ConvertIndexedToBgra requires input frames to be indexed.", nameof(frame));
|
||||
|
||||
// Cursors may be either native BGRA or Indexed.
|
||||
// Indexed sprites are converted to BGRA using the referenced palette.
|
||||
// All palettes must be explicitly referenced, even if they are embedded in the sprite.
|
||||
if (frame.Type == SpriteFrameType.Indexed && palette == null)
|
||||
if (palette == null)
|
||||
throw new InvalidOperationException("Cursor sequence `{0}` attempted to load an indexed sprite but does not define Palette".F(name));
|
||||
|
||||
var width = frame.Size.Width;
|
||||
var height = frame.Size.Height;
|
||||
var data = new byte[4 * width * height];
|
||||
for (var j = 0; j < height; j++)
|
||||
unsafe
|
||||
{
|
||||
for (var i = 0; i < width; i++)
|
||||
// Cast the data to an int array so we can copy the src data directly
|
||||
fixed (byte* bd = &data[0])
|
||||
{
|
||||
var rgba = palette[frame.Data[j * width + i]];
|
||||
var k = 4 * (j * width + i);
|
||||
|
||||
// Convert RGBA to BGRA
|
||||
data[k] = (byte)(rgba >> 16);
|
||||
data[k + 1] = (byte)(rgba >> 8);
|
||||
data[k + 2] = (byte)(rgba >> 0);
|
||||
data[k + 3] = (byte)(rgba >> 24);
|
||||
var rgba = (uint*)bd;
|
||||
for (var j = 0; j < height; j++)
|
||||
for (var i = 0; i < width; i++)
|
||||
rgba[j * width + i] = palette[frame.Data[j * width + i]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public interface IModelCache : IDisposable
|
||||
{
|
||||
IModel GetModel(string model);
|
||||
IModel GetModelSequence(string model, string sequence);
|
||||
bool HasModelSequence(string model, string sequence);
|
||||
IVertexBuffer<Vertex> VertexBuffer { get; }
|
||||
@@ -66,6 +67,11 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public IModel GetModel(string model)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IModel GetModelSequence(string model, string sequence)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
||||
@@ -79,21 +79,17 @@ namespace OpenRA.Graphics
|
||||
|
||||
public Png AsPng()
|
||||
{
|
||||
var data = GetData();
|
||||
if (Type == SheetType.Indexed)
|
||||
throw new InvalidOperationException("AsPng() cannot be called on Indexed sheets.");
|
||||
|
||||
// Convert BGRA to RGBA
|
||||
for (var i = 0; i < Size.Width * Size.Height; i++)
|
||||
{
|
||||
var temp = data[i * 4];
|
||||
data[i * 4] = data[i * 4 + 2];
|
||||
data[i * 4 + 2] = temp;
|
||||
}
|
||||
|
||||
return new Png(data, Size.Width, Size.Height);
|
||||
return new Png(GetData(), SpriteFrameType.Bgra32, Size.Width, Size.Height);
|
||||
}
|
||||
|
||||
public Png AsPng(TextureChannel channel, IPalette pal)
|
||||
{
|
||||
if (Type != SheetType.Indexed)
|
||||
throw new InvalidOperationException("AsPng(TextureChannel, IPalette) can only be called on Indexed sheets.");
|
||||
|
||||
var d = GetData();
|
||||
var plane = new byte[Size.Width * Size.Height];
|
||||
var dataStride = 4 * Size.Width;
|
||||
@@ -107,7 +103,7 @@ namespace OpenRA.Graphics
|
||||
for (var i = 0; i < Palette.Size; i++)
|
||||
palColors[i] = pal.GetColor(i);
|
||||
|
||||
return new Png(plane, Size.Width, Size.Height, palColors);
|
||||
return new Png(plane, SpriteFrameType.Bgra32, Size.Width, Size.Height, palColors);
|
||||
}
|
||||
|
||||
public void CreateBuffer()
|
||||
|
||||
@@ -52,8 +52,15 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case SpriteFrameType.Indexed: return SheetType.Indexed;
|
||||
case SpriteFrameType.BGRA: return SheetType.BGRA;
|
||||
case SpriteFrameType.Indexed8:
|
||||
return SheetType.Indexed;
|
||||
|
||||
// Util.FastCopyIntoChannel will automatically convert these to BGRA
|
||||
case SpriteFrameType.Bgra32:
|
||||
case SpriteFrameType.Bgr24:
|
||||
case SpriteFrameType.Rgba32:
|
||||
case SpriteFrameType.Rgb24:
|
||||
return SheetType.BGRA;
|
||||
default: throw new NotImplementedException("Unknown SpriteFrameType {0}".F(t));
|
||||
}
|
||||
}
|
||||
@@ -74,16 +81,16 @@ namespace OpenRA.Graphics
|
||||
this.margin = margin;
|
||||
}
|
||||
|
||||
public Sprite Add(ISpriteFrame frame) { return Add(frame.Data, frame.Size, 0, frame.Offset); }
|
||||
public Sprite Add(byte[] src, Size size) { return Add(src, size, 0, float3.Zero); }
|
||||
public Sprite Add(byte[] src, Size size, float zRamp, in float3 spriteOffset)
|
||||
public Sprite Add(ISpriteFrame frame) { return Add(frame.Data, frame.Type, frame.Size, 0, frame.Offset); }
|
||||
public Sprite Add(byte[] src, SpriteFrameType type, Size size) { return Add(src, type, size, 0, float3.Zero); }
|
||||
public Sprite Add(byte[] src, SpriteFrameType type, Size size, float zRamp, in float3 spriteOffset)
|
||||
{
|
||||
// Don't bother allocating empty sprites
|
||||
if (size.Width == 0 || size.Height == 0)
|
||||
return new Sprite(current, Rectangle.Empty, 0, spriteOffset, channel, BlendMode.Alpha);
|
||||
|
||||
var rect = Allocate(size, zRamp, spriteOffset);
|
||||
Util.FastCopyIntoChannel(rect, src);
|
||||
Util.FastCopyIntoChannel(rect, src, type);
|
||||
current.CommitBufferedData();
|
||||
return rect;
|
||||
}
|
||||
@@ -96,15 +103,6 @@ namespace OpenRA.Graphics
|
||||
return rect;
|
||||
}
|
||||
|
||||
public Sprite Add(Size size, byte paletteIndex)
|
||||
{
|
||||
var data = new byte[size.Width * size.Height];
|
||||
for (var i = 0; i < data.Length; i++)
|
||||
data[i] = paletteIndex;
|
||||
|
||||
return Add(data, size);
|
||||
}
|
||||
|
||||
TextureChannel? NextChannel(TextureChannel t)
|
||||
{
|
||||
var nextChannel = (int)t + (int)Type;
|
||||
|
||||
@@ -18,7 +18,29 @@ using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public enum SpriteFrameType { Indexed, BGRA }
|
||||
/// <summary>
|
||||
/// Describes the format of the pixel data in a ISpriteFrame.
|
||||
/// Note that the channel order is defined for little-endian bytes, so BGRA corresponds
|
||||
/// to a 32bit ARGB value, such as that returned by Color.ToArgb()!
|
||||
/// </summary>
|
||||
public enum SpriteFrameType
|
||||
{
|
||||
// 8 bit index into an external palette
|
||||
Indexed8,
|
||||
|
||||
// 32 bit color such as returned by Color.ToArgb() or the bmp file format
|
||||
// (remember that little-endian systems place the little bits in the first byte!)
|
||||
Bgra32,
|
||||
|
||||
// Like BGRA, but without an alpha channel
|
||||
Bgr24,
|
||||
|
||||
// 32 bit color in big-endian format, like png
|
||||
Rgba32,
|
||||
|
||||
// Like RGBA, but without an alpha channel
|
||||
Rgb24
|
||||
}
|
||||
|
||||
public interface ISpriteLoader
|
||||
{
|
||||
@@ -47,7 +69,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public class SpriteCache
|
||||
{
|
||||
public readonly Cache<SpriteFrameType, SheetBuilder> SheetBuilders;
|
||||
public readonly Cache<SheetType, SheetBuilder> SheetBuilders;
|
||||
readonly ISpriteLoader[] loaders;
|
||||
readonly IReadOnlyFileSystem fileSystem;
|
||||
|
||||
@@ -57,7 +79,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders)
|
||||
{
|
||||
SheetBuilders = new Cache<SpriteFrameType, SheetBuilder>(t => new SheetBuilder(SheetBuilder.FrameTypeToSheetType(t)));
|
||||
SheetBuilders = new Cache<SheetType, SheetBuilder>(t => new SheetBuilder(t));
|
||||
|
||||
this.fileSystem = fileSystem;
|
||||
this.loaders = loaders;
|
||||
@@ -103,7 +125,7 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
if (unloaded[i] != null)
|
||||
{
|
||||
sprite[i] = SheetBuilders[unloaded[i].Type].Add(unloaded[i]);
|
||||
sprite[i] = SheetBuilders[SheetBuilder.FrameTypeToSheetType(unloaded[i].Type)].Add(unloaded[i]);
|
||||
unloaded[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,12 +107,13 @@ namespace OpenRA.Graphics
|
||||
throw new YamlException("Sprite type mismatch. Terrain sprites must all be either Indexed or RGBA.");
|
||||
|
||||
var s = sheetBuilder.Allocate(f.Size, zRamp, offset);
|
||||
Util.FastCopyIntoChannel(s, f.Data);
|
||||
Util.FastCopyIntoChannel(s, f.Data, f.Type);
|
||||
|
||||
if (tileset.EnableDepth)
|
||||
{
|
||||
var ss = sheetBuilder.Allocate(f.Size, zRamp, offset);
|
||||
Util.FastCopyIntoChannel(ss, allFrames[j + frameCount].Data);
|
||||
var depthFrame = allFrames[j + frameCount];
|
||||
Util.FastCopyIntoChannel(ss, depthFrame.Data, depthFrame.Type);
|
||||
|
||||
// s and ss are guaranteed to use the same sheet
|
||||
// because of the custom terrain sheet allocation
|
||||
@@ -136,7 +137,10 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
|
||||
// 1x1px transparent tile
|
||||
missingTile = sheetBuilder.Add(new byte[sheetBuilder.Type == SheetType.BGRA ? 4 : 1], new Size(1, 1));
|
||||
if (sheetBuilder.Type == SheetType.BGRA)
|
||||
missingTile = sheetBuilder.Add(new byte[4], SpriteFrameType.Bgra32, new Size(1, 1));
|
||||
else
|
||||
missingTile = sheetBuilder.Add(new byte[1], SpriteFrameType.Indexed8, new Size(1, 1));
|
||||
|
||||
Sheet.ReleaseBuffer();
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace OpenRA.Graphics
|
||||
vertices[nv + 5] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC, tint);
|
||||
}
|
||||
|
||||
public static void FastCopyIntoChannel(Sprite dest, byte[] src)
|
||||
public static void FastCopyIntoChannel(Sprite dest, byte[] src, SpriteFrameType srcType)
|
||||
{
|
||||
var destData = dest.Sheet.GetData();
|
||||
var width = dest.Bounds.Width;
|
||||
@@ -85,12 +85,34 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
for (var i = 0; i < width; i++)
|
||||
{
|
||||
var r = src[k++];
|
||||
var g = src[k++];
|
||||
var b = src[k++];
|
||||
var a = src[k++];
|
||||
var cc = Color.FromArgb(a, r, g, b);
|
||||
byte r, g, b, a;
|
||||
switch (srcType)
|
||||
{
|
||||
case SpriteFrameType.Bgra32:
|
||||
case SpriteFrameType.Bgr24:
|
||||
{
|
||||
b = src[k++];
|
||||
g = src[k++];
|
||||
r = src[k++];
|
||||
a = srcType == SpriteFrameType.Bgra32 ? src[k++] : (byte)255;
|
||||
break;
|
||||
}
|
||||
|
||||
case SpriteFrameType.Rgba32:
|
||||
case SpriteFrameType.Rgb24:
|
||||
{
|
||||
r = src[k++];
|
||||
g = src[k++];
|
||||
b = src[k++];
|
||||
a = srcType == SpriteFrameType.Rgba32 ? src[k++] : (byte)255;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException("Unknown SpriteFrameType {0}".F(srcType));
|
||||
}
|
||||
|
||||
var cc = Color.FromArgb(a, r, g, b);
|
||||
data[(y + j) * destStride + x + i] = PremultiplyAlpha(cc).ToArgb();
|
||||
}
|
||||
}
|
||||
@@ -139,16 +161,29 @@ namespace OpenRA.Graphics
|
||||
for (var i = 0; i < width; i++)
|
||||
{
|
||||
Color cc;
|
||||
if (src.Palette == null)
|
||||
switch (src.Type)
|
||||
{
|
||||
var r = src.Data[k++];
|
||||
var g = src.Data[k++];
|
||||
var b = src.Data[k++];
|
||||
var a = src.Data[k++];
|
||||
cc = Color.FromArgb(a, r, g, b);
|
||||
case SpriteFrameType.Indexed8:
|
||||
{
|
||||
cc = src.Palette[src.Data[k++]];
|
||||
break;
|
||||
}
|
||||
|
||||
case SpriteFrameType.Rgba32:
|
||||
case SpriteFrameType.Rgb24:
|
||||
{
|
||||
var r = src.Data[k++];
|
||||
var g = src.Data[k++];
|
||||
var b = src.Data[k++];
|
||||
var a = src.Type == SpriteFrameType.Rgba32 ? src.Data[k++] : (byte)255;
|
||||
cc = Color.FromArgb(a, r, g, b);
|
||||
break;
|
||||
}
|
||||
|
||||
// Pngs don't support BGR[A], so no need to include them here
|
||||
default:
|
||||
throw new InvalidOperationException("Unknown SpriteFrameType {0}".F(src.Type));
|
||||
}
|
||||
else
|
||||
cc = src.Palette[src.Data[k++]];
|
||||
|
||||
data[(y + j) * destStride + x + i] = PremultiplyAlpha(cc).ToArgb();
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace OpenRA
|
||||
public readonly string[]
|
||||
Rules, ServerTraits,
|
||||
Sequences, ModelSequences, Cursors, Chrome, Assemblies, ChromeLayout,
|
||||
Weapons, Voices, Notifications, Music, Translations, TileSets,
|
||||
Weapons, Voices, Notifications, Music, TileSets,
|
||||
ChromeMetrics, MapCompatibility, Missions, Hotkeys;
|
||||
|
||||
public readonly IReadOnlyDictionary<string, string> Packages;
|
||||
@@ -128,7 +128,6 @@ namespace OpenRA
|
||||
Voices = YamlList(yaml, "Voices");
|
||||
Notifications = YamlList(yaml, "Notifications");
|
||||
Music = YamlList(yaml, "Music");
|
||||
Translations = YamlList(yaml, "Translations");
|
||||
TileSets = YamlList(yaml, "TileSets");
|
||||
ChromeMetrics = YamlList(yaml, "ChromeMetrics");
|
||||
Missions = YamlList(yaml, "Missions");
|
||||
|
||||
@@ -17,6 +17,7 @@ using System.Reflection;
|
||||
using System.Text;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Traits;
|
||||
@@ -173,7 +174,6 @@ namespace OpenRA
|
||||
new MapField("Voices", "VoiceDefinitions", required: false),
|
||||
new MapField("Music", "MusicDefinitions", required: false),
|
||||
new MapField("Notifications", "NotificationDefinitions", required: false),
|
||||
new MapField("Translations", "TranslationDefinitions", required: false)
|
||||
};
|
||||
|
||||
// Format versions
|
||||
@@ -204,7 +204,6 @@ namespace OpenRA
|
||||
public readonly MiniYaml VoiceDefinitions;
|
||||
public readonly MiniYaml MusicDefinitions;
|
||||
public readonly MiniYaml NotificationDefinitions;
|
||||
public readonly MiniYaml TranslationDefinitions;
|
||||
|
||||
public readonly Dictionary<CPos, TerrainTile> ReplacedInvalidTerrainTiles = new Dictionary<CPos, TerrainTile>();
|
||||
|
||||
@@ -780,7 +779,7 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
var png = new Png(minimapData, bitmapWidth, height);
|
||||
var png = new Png(minimapData, SpriteFrameType.Bgra32, bitmapWidth, height);
|
||||
return png.Save();
|
||||
}
|
||||
|
||||
|
||||
@@ -138,42 +138,6 @@ namespace OpenRA
|
||||
|
||||
public IEnumerable<string> Languages { get; private set; }
|
||||
|
||||
void LoadTranslations(Map map)
|
||||
{
|
||||
var selectedTranslations = new Dictionary<string, string>();
|
||||
var defaultTranslations = new Dictionary<string, string>();
|
||||
|
||||
if (!Manifest.Translations.Any())
|
||||
{
|
||||
Languages = new string[0];
|
||||
return;
|
||||
}
|
||||
|
||||
var yaml = MiniYaml.Load(map, Manifest.Translations, map.TranslationDefinitions);
|
||||
Languages = yaml.Select(t => t.Key).ToArray();
|
||||
|
||||
foreach (var y in yaml)
|
||||
{
|
||||
if (y.Key == Game.Settings.Graphics.Language)
|
||||
selectedTranslations = y.Value.ToDictionary(my => my.Value ?? "");
|
||||
else if (y.Key == Game.Settings.Graphics.DefaultLanguage)
|
||||
defaultTranslations = y.Value.ToDictionary(my => my.Value ?? "");
|
||||
}
|
||||
|
||||
var translations = new Dictionary<string, string>();
|
||||
foreach (var tkv in defaultTranslations.Concat(selectedTranslations))
|
||||
{
|
||||
if (translations.ContainsKey(tkv.Key))
|
||||
continue;
|
||||
if (selectedTranslations.ContainsKey(tkv.Key))
|
||||
translations.Add(tkv.Key, selectedTranslations[tkv.Key]);
|
||||
else
|
||||
translations.Add(tkv.Key, tkv.Value);
|
||||
}
|
||||
|
||||
FieldLoader.SetTranslations(translations);
|
||||
}
|
||||
|
||||
public Map PrepareMap(string uid)
|
||||
{
|
||||
LoadScreen?.Display();
|
||||
@@ -185,8 +149,6 @@ namespace OpenRA
|
||||
using (new Support.PerfTimer("Map"))
|
||||
map = new Map(this, MapCache[uid].Package);
|
||||
|
||||
LoadTranslations(map);
|
||||
|
||||
// Reinitialize all our assets
|
||||
InitializeLoaders(map);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
@@ -42,25 +43,48 @@ namespace OpenRA
|
||||
if (resolvedPath == null)
|
||||
throw new FileNotFoundException("Assembly `{0}` not found.".F(path));
|
||||
|
||||
// .NET doesn't provide any way of querying the metadata of an assembly without either:
|
||||
// (a) loading duplicate data into the application domain, breaking the world.
|
||||
// (b) crashing if the assembly has already been loaded.
|
||||
// We can't check the internal name of the assembly, so we'll work off the data instead
|
||||
var hash = CryptoUtil.SHA1Hash(File.ReadAllBytes(resolvedPath));
|
||||
|
||||
if (!ResolvedAssemblies.TryGetValue(hash, out var assembly))
|
||||
{
|
||||
assembly = Assembly.LoadFile(resolvedPath);
|
||||
ResolvedAssemblies.Add(hash, assembly);
|
||||
}
|
||||
|
||||
assemblyList.Add(assembly);
|
||||
LoadAssembly(assemblyList, resolvedPath);
|
||||
}
|
||||
|
||||
AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
|
||||
assemblies = assemblyList.SelectMany(asm => asm.GetNamespaces().Select(ns => (asm, ns))).ToArray();
|
||||
}
|
||||
|
||||
void LoadAssembly(List<Assembly> assemblyList, string resolvedPath)
|
||||
{
|
||||
// .NET doesn't provide any way of querying the metadata of an assembly without either:
|
||||
// (a) loading duplicate data into the application domain, breaking the world.
|
||||
// (b) crashing if the assembly has already been loaded.
|
||||
// We can't check the internal name of the assembly, so we'll work off the data instead
|
||||
var hash = CryptoUtil.SHA1Hash(File.ReadAllBytes(resolvedPath));
|
||||
|
||||
if (!ResolvedAssemblies.TryGetValue(hash, out var assembly))
|
||||
{
|
||||
#if MONO
|
||||
assembly = Assembly.LoadFile(resolvedPath);
|
||||
ResolvedAssemblies.Add(hash, assembly);
|
||||
|
||||
// Allow mods to use libraries.
|
||||
var assemblyPath = Path.GetDirectoryName(resolvedPath);
|
||||
if (assemblyPath != null)
|
||||
{
|
||||
foreach (var referencedAssembly in assembly.GetReferencedAssemblies())
|
||||
{
|
||||
var depedencyPath = Path.Combine(assemblyPath, referencedAssembly.Name + ".dll");
|
||||
if (File.Exists(depedencyPath))
|
||||
LoadAssembly(assemblyList, depedencyPath);
|
||||
}
|
||||
}
|
||||
#else
|
||||
var loader = new AssemblyLoader(resolvedPath);
|
||||
assembly = loader.LoadDefaultAssembly();
|
||||
ResolvedAssemblies.Add(hash, assembly);
|
||||
#endif
|
||||
}
|
||||
|
||||
assemblyList.Add(assembly);
|
||||
}
|
||||
|
||||
Assembly ResolveAssembly(object sender, ResolveEventArgs e)
|
||||
{
|
||||
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Optimize>true</Optimize>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
@@ -34,11 +34,16 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenRA-Eluant" Version="1.0.17" />
|
||||
<PackageReference Include="OpenRA-Open.NAT" Version="1.0.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
|
||||
<AdditionalFiles Include="../stylecop.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(Mono)' == ''">
|
||||
<PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="5.0.0-preview.3-runtime.20214.6" />
|
||||
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
<Target Name="DisableAnalyzers" BeforeTargets="CoreCompile" Condition="'$(Configuration)'=='Release'">
|
||||
<!-- Disable code style analysis on Release builds to improve compile-time performance -->
|
||||
<ItemGroup Condition="'$(Configuration)'=='Release'">
|
||||
|
||||
@@ -431,24 +431,15 @@ namespace OpenRA
|
||||
var srcWidth = screenSprite.Sheet.Size.Width;
|
||||
var destWidth = screenSprite.Bounds.Width;
|
||||
var destHeight = -screenSprite.Bounds.Height;
|
||||
var channelOrder = new[] { 2, 1, 0, 3 };
|
||||
|
||||
ThreadPool.QueueUserWorkItem(_ =>
|
||||
{
|
||||
// Convert BGRA to RGBA
|
||||
// Extract the screen rect from the (larger) backing surface
|
||||
var dest = new byte[4 * destWidth * destHeight];
|
||||
for (var y = 0; y < destHeight; y++)
|
||||
{
|
||||
for (var x = 0; x < destWidth; x++)
|
||||
{
|
||||
var destOffset = 4 * (y * destWidth + x);
|
||||
var srcOffset = 4 * (y * srcWidth + x);
|
||||
for (var i = 0; i < 4; i++)
|
||||
dest[destOffset + i] = src[srcOffset + channelOrder[i]];
|
||||
}
|
||||
}
|
||||
Array.Copy(src, 4 * y * srcWidth, dest, 4 * y * destWidth, 4 * destWidth);
|
||||
|
||||
new Png(dest, destWidth, destHeight).Save(path);
|
||||
new Png(dest, SpriteFrameType.Bgra32, destWidth, destHeight).Save(path);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -660,16 +660,28 @@ namespace OpenRA.Server
|
||||
}
|
||||
}
|
||||
|
||||
void DispatchOrdersToClient(Connection c, int client, int frame, byte[] data)
|
||||
byte[] CreateFrame(int client, int frame, byte[] data)
|
||||
{
|
||||
try
|
||||
using (var ms = new MemoryStream(data.Length + 12))
|
||||
{
|
||||
var ms = new MemoryStream(data.Length + 12);
|
||||
ms.WriteArray(BitConverter.GetBytes(data.Length + 4));
|
||||
ms.WriteArray(BitConverter.GetBytes(client));
|
||||
ms.WriteArray(BitConverter.GetBytes(frame));
|
||||
ms.WriteArray(data);
|
||||
SendData(c.Socket, ms.ToArray());
|
||||
return ms.GetBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
void DispatchOrdersToClient(Connection c, int client, int frame, byte[] data)
|
||||
{
|
||||
DispatchFrameToClient(c, client, CreateFrame(client, frame, data));
|
||||
}
|
||||
|
||||
void DispatchFrameToClient(Connection c, int client, byte[] frameData)
|
||||
{
|
||||
try
|
||||
{
|
||||
SendData(c.Socket, frameData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -777,8 +789,9 @@ namespace OpenRA.Server
|
||||
public void DispatchOrdersToClients(Connection conn, int frame, byte[] data)
|
||||
{
|
||||
var from = conn != null ? conn.PlayerIndex : 0;
|
||||
var frameData = CreateFrame(from, frame, data);
|
||||
foreach (var c in Conns.Except(conn).ToList())
|
||||
DispatchOrdersToClient(c, from, frame, data);
|
||||
DispatchFrameToClient(c, from, frameData);
|
||||
|
||||
if (recorder != null)
|
||||
{
|
||||
|
||||
@@ -190,9 +190,6 @@ namespace OpenRA
|
||||
|
||||
public int BatchSize = 8192;
|
||||
public int SheetSize = 2048;
|
||||
|
||||
public string Language = "english";
|
||||
public string DefaultLanguage = "english";
|
||||
}
|
||||
|
||||
public class SoundSettings
|
||||
|
||||
363
OpenRA.Game/Support/AssemblyLoader.cs
Normal file
363
OpenRA.Game/Support/AssemblyLoader.cs
Normal file
@@ -0,0 +1,363 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
// Not used/usable on Mono. Only used for Dotnet Core.
|
||||
// Based on https://github.com/natemcmaster/DotNetCorePlugins and used under the terms of the Apache 2.0 license
|
||||
#if !MONO
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Loader;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
|
||||
namespace OpenRA.Support
|
||||
{
|
||||
public class AssemblyLoader
|
||||
{
|
||||
readonly string mainAssembly;
|
||||
readonly AssemblyLoadContext context;
|
||||
|
||||
public Assembly LoadDefaultAssembly() => context.LoadFromAssemblyPath(mainAssembly);
|
||||
|
||||
public AssemblyLoader(string assemblyFile)
|
||||
{
|
||||
mainAssembly = assemblyFile;
|
||||
var baseDir = Path.GetDirectoryName(assemblyFile);
|
||||
|
||||
context = CreateLoadContext(baseDir, assemblyFile);
|
||||
}
|
||||
|
||||
static AssemblyLoadContext CreateLoadContext(string baseDir, string assemblyFile)
|
||||
{
|
||||
var depsJsonFile = Path.Combine(baseDir, Path.GetFileNameWithoutExtension(assemblyFile) + ".deps.json");
|
||||
|
||||
var builder = new AssemblyLoadContextBuilder();
|
||||
|
||||
builder.TryAddDependencyContext(depsJsonFile, out _);
|
||||
builder.SetBaseDirectory(baseDir);
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
|
||||
public class AssemblyLoadContextBuilder
|
||||
{
|
||||
readonly Dictionary<string, ManagedLibrary> managedLibraries = new Dictionary<string, ManagedLibrary>(StringComparer.Ordinal);
|
||||
readonly Dictionary<string, NativeLibrary> nativeLibraries = new Dictionary<string, NativeLibrary>(StringComparer.Ordinal);
|
||||
string basePath;
|
||||
|
||||
public AssemblyLoadContext Build()
|
||||
{
|
||||
return new ManagedLoadContext(basePath, managedLibraries, nativeLibraries);
|
||||
}
|
||||
|
||||
public AssemblyLoadContextBuilder SetBaseDirectory(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
throw new ArgumentException("Argument must not be null or empty.", nameof(path));
|
||||
|
||||
if (!Path.IsPathRooted(path))
|
||||
throw new ArgumentException("Argument must be a full path.", nameof(path));
|
||||
|
||||
basePath = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AssemblyLoadContextBuilder AddManagedLibrary(ManagedLibrary library)
|
||||
{
|
||||
managedLibraries.Add(library.Name.Name, library);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AssemblyLoadContextBuilder AddNativeLibrary(NativeLibrary library)
|
||||
{
|
||||
ValidateRelativePath(library.AppLocalPath);
|
||||
nativeLibraries.Add(library.Name, library);
|
||||
return this;
|
||||
}
|
||||
|
||||
static void ValidateRelativePath(string probingPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(probingPath))
|
||||
throw new ArgumentException("Value must not be null or empty.", nameof(probingPath));
|
||||
|
||||
if (Path.IsPathRooted(probingPath))
|
||||
throw new ArgumentException("Argument must be a relative path.", nameof(probingPath));
|
||||
}
|
||||
}
|
||||
|
||||
class ManagedLoadContext : AssemblyLoadContext
|
||||
{
|
||||
readonly string basePath;
|
||||
readonly Dictionary<string, ManagedLibrary> managedAssemblies;
|
||||
readonly Dictionary<string, NativeLibrary> nativeLibraries;
|
||||
|
||||
static readonly string[] NativeLibraryExtensions;
|
||||
static readonly string[] NativeLibraryPrefixes;
|
||||
|
||||
static readonly string[] ManagedAssemblyExtensions =
|
||||
{
|
||||
".dll",
|
||||
".ni.dll",
|
||||
".exe",
|
||||
".ni.exe"
|
||||
};
|
||||
|
||||
static ManagedLoadContext()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
NativeLibraryPrefixes = new[] { "" };
|
||||
NativeLibraryExtensions = new[] { ".dll" };
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
NativeLibraryPrefixes = new[] { "", "lib", };
|
||||
NativeLibraryExtensions = new[] { ".dylib" };
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
NativeLibraryPrefixes = new[] { "", "lib" };
|
||||
NativeLibraryExtensions = new[] { ".so", ".so.1" };
|
||||
}
|
||||
else
|
||||
{
|
||||
NativeLibraryPrefixes = Array.Empty<string>();
|
||||
NativeLibraryExtensions = Array.Empty<string>();
|
||||
}
|
||||
}
|
||||
|
||||
public ManagedLoadContext(string baseDirectory, Dictionary<string, ManagedLibrary> managedAssemblies, Dictionary<string, NativeLibrary> nativeLibraries)
|
||||
{
|
||||
basePath = baseDirectory ?? throw new ArgumentNullException(nameof(baseDirectory));
|
||||
this.managedAssemblies = managedAssemblies ?? throw new ArgumentNullException(nameof(managedAssemblies));
|
||||
this.nativeLibraries = nativeLibraries ?? throw new ArgumentNullException(nameof(nativeLibraries));
|
||||
}
|
||||
|
||||
protected override Assembly Load(AssemblyName assemblyName)
|
||||
{
|
||||
// If default context is preferred, check first for types in the default context unless the dependency has been declared as private
|
||||
try
|
||||
{
|
||||
var defaultAssembly = Default.LoadFromAssemblyName(assemblyName);
|
||||
if (defaultAssembly != null)
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Swallow errors in loading from the default context
|
||||
}
|
||||
|
||||
if (managedAssemblies.TryGetValue(assemblyName.Name, out var library) && SearchForLibrary(library, out var path))
|
||||
return LoadFromAssemblyPath(path);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
|
||||
{
|
||||
foreach (var prefix in NativeLibraryPrefixes)
|
||||
if (nativeLibraries.TryGetValue(prefix + unmanagedDllName, out var library) && SearchForLibrary(library, prefix, out var path))
|
||||
return LoadUnmanagedDllFromPath(path);
|
||||
|
||||
return base.LoadUnmanagedDll(unmanagedDllName);
|
||||
}
|
||||
|
||||
bool SearchForLibrary(ManagedLibrary library, out string path)
|
||||
{
|
||||
// 1. Search in base path
|
||||
foreach (var ext in ManagedAssemblyExtensions)
|
||||
{
|
||||
var local = Path.Combine(basePath, library.Name.Name + ext);
|
||||
if (File.Exists(local))
|
||||
{
|
||||
path = local;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
path = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SearchForLibrary(NativeLibrary library, string prefix, out string path)
|
||||
{
|
||||
// 1. Search in base path
|
||||
foreach (var ext in NativeLibraryExtensions)
|
||||
{
|
||||
var candidate = Path.Combine(basePath, $"{prefix}{library.Name}{ext}");
|
||||
if (File.Exists(candidate))
|
||||
{
|
||||
path = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Search in base path + app local (for portable deployments of netcoreapp)
|
||||
var local = Path.Combine(basePath, library.AppLocalPath);
|
||||
if (File.Exists(local))
|
||||
{
|
||||
path = local;
|
||||
return true;
|
||||
}
|
||||
|
||||
path = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class ManagedLibrary
|
||||
{
|
||||
public AssemblyName Name { get; private set; }
|
||||
|
||||
public static ManagedLibrary CreateFromPackage(string assetPath)
|
||||
{
|
||||
return new ManagedLibrary
|
||||
{
|
||||
Name = new AssemblyName(Path.GetFileNameWithoutExtension(assetPath))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class NativeLibrary
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
|
||||
public string AppLocalPath { get; private set; }
|
||||
|
||||
public static NativeLibrary CreateFromPackage(string assetPath)
|
||||
{
|
||||
return new NativeLibrary
|
||||
{
|
||||
Name = Path.GetFileNameWithoutExtension(assetPath),
|
||||
AppLocalPath = assetPath
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class DependencyContextExtensions
|
||||
{
|
||||
public static AssemblyLoadContextBuilder TryAddDependencyContext(this AssemblyLoadContextBuilder builder, string depsFilePath, out Exception error)
|
||||
{
|
||||
error = null;
|
||||
try
|
||||
{
|
||||
builder.AddDependencyContext(depsFilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex;
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static AssemblyLoadContextBuilder AddDependencyContext(this AssemblyLoadContextBuilder builder, string depsFilePath)
|
||||
{
|
||||
var reader = new DependencyContextJsonReader();
|
||||
using (var file = File.OpenRead(depsFilePath))
|
||||
{
|
||||
var deps = reader.Read(file);
|
||||
builder.SetBaseDirectory(Path.GetDirectoryName(depsFilePath));
|
||||
builder.AddDependencyContext(deps);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
static string GetFallbackRid()
|
||||
{
|
||||
string ridBase;
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
ridBase = "win10";
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
ridBase = "linux";
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
ridBase = "osx.10.12";
|
||||
else
|
||||
return "any";
|
||||
|
||||
switch (RuntimeInformation.OSArchitecture)
|
||||
{
|
||||
case Architecture.X86:
|
||||
return ridBase + "-x86";
|
||||
case Architecture.X64:
|
||||
return ridBase + "-x64";
|
||||
case Architecture.Arm:
|
||||
return ridBase + "-arm";
|
||||
case Architecture.Arm64:
|
||||
return ridBase + "-arm64";
|
||||
}
|
||||
|
||||
return ridBase;
|
||||
}
|
||||
|
||||
public static AssemblyLoadContextBuilder AddDependencyContext(this AssemblyLoadContextBuilder builder, DependencyContext dependencyContext)
|
||||
{
|
||||
var ridGraph = dependencyContext.RuntimeGraph.Any()
|
||||
? dependencyContext.RuntimeGraph
|
||||
: DependencyContext.Default.RuntimeGraph;
|
||||
|
||||
var rid = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.GetRuntimeIdentifier();
|
||||
var fallbackRid = GetFallbackRid();
|
||||
var fallbackGraph = ridGraph.FirstOrDefault(g => g.Runtime == rid)
|
||||
?? ridGraph.FirstOrDefault(g => g.Runtime == fallbackRid)
|
||||
?? new RuntimeFallbacks("any");
|
||||
|
||||
foreach (var managed in dependencyContext.ResolveRuntimeAssemblies(fallbackGraph))
|
||||
builder.AddManagedLibrary(managed);
|
||||
|
||||
foreach (var native in dependencyContext.ResolveNativeAssets(fallbackGraph))
|
||||
builder.AddNativeLibrary(native);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
static IEnumerable<ManagedLibrary> ResolveRuntimeAssemblies(this DependencyContext depContext, RuntimeFallbacks runtimeGraph)
|
||||
{
|
||||
var rids = GetRids(runtimeGraph);
|
||||
return from library in depContext.RuntimeLibraries
|
||||
from assetPath in SelectAssets(rids, library.RuntimeAssemblyGroups)
|
||||
select ManagedLibrary.CreateFromPackage(assetPath);
|
||||
}
|
||||
|
||||
static IEnumerable<NativeLibrary> ResolveNativeAssets(this DependencyContext depContext, RuntimeFallbacks runtimeGraph)
|
||||
{
|
||||
var rids = GetRids(runtimeGraph);
|
||||
return from library in depContext.RuntimeLibraries
|
||||
from assetPath in SelectAssets(rids, library.NativeLibraryGroups)
|
||||
where !assetPath.EndsWith(".a", StringComparison.Ordinal)
|
||||
select NativeLibrary.CreateFromPackage(assetPath);
|
||||
}
|
||||
|
||||
static IEnumerable<string> GetRids(RuntimeFallbacks runtimeGraph)
|
||||
{
|
||||
return Enumerable.Concat(new[] { runtimeGraph.Runtime }, runtimeGraph?.Fallbacks ?? Enumerable.Empty<string>());
|
||||
}
|
||||
|
||||
static IEnumerable<string> SelectAssets(IEnumerable<string> rids, IEnumerable<RuntimeAssetGroup> groups)
|
||||
{
|
||||
foreach (var rid in rids)
|
||||
{
|
||||
var group = groups.FirstOrDefault(g => g.Runtime == rid);
|
||||
if (group != null)
|
||||
return group.AssetPaths;
|
||||
}
|
||||
|
||||
return groups.GetDefaultAssets();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -17,11 +17,9 @@ namespace OpenRA.Traits
|
||||
[Desc("Required for shroud and fog visibility checks. Add this to the player actor.")]
|
||||
public class ShroudInfo : TraitInfo, ILobbyOptions
|
||||
{
|
||||
[Translate]
|
||||
[Desc("Descriptive label for the fog checkbox in the lobby.")]
|
||||
public readonly string FogCheckboxLabel = "Fog of War";
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the fog checkbox in the lobby.")]
|
||||
public readonly string FogCheckboxDescription = "Line of sight is required to view enemy forces";
|
||||
|
||||
@@ -37,11 +35,9 @@ namespace OpenRA.Traits
|
||||
[Desc("Display order for the fog checkbox in the lobby.")]
|
||||
public readonly int FogCheckboxDisplayOrder = 0;
|
||||
|
||||
[Translate]
|
||||
[Desc("Descriptive label for the explored map checkbox in the lobby.")]
|
||||
public readonly string ExploredMapCheckboxLabel = "Explored Map";
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the explored map checkbox in the lobby.")]
|
||||
public readonly string ExploredMapCheckboxDescription = "Initial map shroud is revealed";
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace OpenRA.Traits
|
||||
[Desc("The side that the faction belongs to. For example, England belongs to the 'Allies' side.")]
|
||||
public readonly string Side = null;
|
||||
|
||||
[Translate]
|
||||
public readonly string Description = null;
|
||||
|
||||
public readonly bool Selectable = true;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<TargetFramework Condition="'$(Mono)' == ''">net5.0</TargetFramework>
|
||||
<TargetFramework Condition="'$(Mono)' != ''">netstandard2.1</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Optimize>true</Optimize>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
@@ -17,6 +18,8 @@
|
||||
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
|
||||
<Configurations>Release;Debug</Configurations>
|
||||
<AssemblyName>OpenRA</AssemblyName>
|
||||
<IsPublishable Condition="'$(CopyGenericLauncher)' == 'False'">false</IsPublishable>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetPlatform Condition="$([MSBuild]::IsOsPlatform('Windows'))">win-x64</TargetPlatform>
|
||||
@@ -62,4 +65,9 @@
|
||||
<Analyzer Remove="@(Analyzer)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<TrimmerRootAssembly Include="mscorlib" />
|
||||
<TrimmerRootAssembly Include="netstandard" />
|
||||
<TrimmerRootAssembly Include="System.IO.Pipes" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -20,15 +20,11 @@ namespace OpenRA.Mods.Cnc.AudioLoaders
|
||||
bool IsAud(Stream s)
|
||||
{
|
||||
var start = s.Position;
|
||||
s.Position += 10;
|
||||
var readFlag = s.ReadByte();
|
||||
s.Position += 11;
|
||||
var readFormat = s.ReadByte();
|
||||
s.Position = start;
|
||||
|
||||
if (!Enum.IsDefined(typeof(SoundFlags), readFlag))
|
||||
return false;
|
||||
|
||||
return Enum.IsDefined(typeof(SoundFormat), readFormat);
|
||||
return readFormat == (int)SoundFormat.ImaAdpcm;
|
||||
}
|
||||
|
||||
bool ISoundLoader.TryParseSound(Stream stream, out ISoundFormat sound)
|
||||
@@ -53,8 +49,8 @@ namespace OpenRA.Mods.Cnc.AudioLoaders
|
||||
|
||||
public sealed class AudFormat : ISoundFormat
|
||||
{
|
||||
public int Channels { get { return 1; } }
|
||||
public int SampleBits { get { return 16; } }
|
||||
public int Channels { get { return channels; } }
|
||||
public int SampleBits { get { return sampleBits; } }
|
||||
public int SampleRate { get { return sampleRate; } }
|
||||
public float LengthInSeconds { get { return AudReader.SoundLength(sourceStream); } }
|
||||
public Stream GetPCMInputStream() { return audStreamFactory(); }
|
||||
@@ -62,13 +58,15 @@ namespace OpenRA.Mods.Cnc.AudioLoaders
|
||||
|
||||
readonly Stream sourceStream;
|
||||
readonly Func<Stream> audStreamFactory;
|
||||
readonly int channels;
|
||||
readonly int sampleBits;
|
||||
readonly int sampleRate;
|
||||
|
||||
public AudFormat(Stream stream)
|
||||
{
|
||||
sourceStream = stream;
|
||||
|
||||
if (!AudReader.LoadSound(stream, out audStreamFactory, out sampleRate))
|
||||
if (!AudReader.LoadSound(stream, out audStreamFactory, out sampleRate, out sampleBits, out channels))
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
||||
return (float)samples / sampleRate;
|
||||
}
|
||||
|
||||
public static bool LoadSound(Stream s, out Func<Stream> result, out int sampleRate)
|
||||
public static bool LoadSound(Stream s, out Func<Stream> result, out int sampleRate, out int sampleBits, out int channels)
|
||||
{
|
||||
result = null;
|
||||
var startPosition = s.Position;
|
||||
@@ -58,15 +58,17 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
||||
sampleRate = s.ReadUInt16();
|
||||
var dataSize = s.ReadInt32();
|
||||
var outputSize = s.ReadInt32();
|
||||
|
||||
var readFlag = s.ReadByte();
|
||||
if (!Enum.IsDefined(typeof(SoundFlags), readFlag))
|
||||
return false;
|
||||
sampleBits = (readFlag & (int)SoundFlags._16Bit) == 0 ? 8 : 16;
|
||||
channels = (readFlag & (int)SoundFlags.Stereo) == 0 ? 1 : 2;
|
||||
|
||||
var readFormat = s.ReadByte();
|
||||
if (!Enum.IsDefined(typeof(SoundFormat), readFormat))
|
||||
return false;
|
||||
|
||||
if (readFormat == (int)SoundFormat.WestwoodCompressed)
|
||||
throw new NotImplementedException();
|
||||
|
||||
var offsetPosition = s.Position;
|
||||
|
||||
result = () =>
|
||||
|
||||
@@ -35,10 +35,10 @@ namespace OpenRA.Mods.Cnc.Graphics
|
||||
uint IModel.Frames { get { return frames; } }
|
||||
uint IModel.Sections { get { return limbs; } }
|
||||
|
||||
public Voxel(VoxelLoader loader, VxlReader vxl, HvaReader hva)
|
||||
public Voxel(VoxelLoader loader, VxlReader vxl, HvaReader hva, (string Vxl, string Hva) files)
|
||||
{
|
||||
if (vxl.LimbCount != hva.LimbCount)
|
||||
throw new InvalidOperationException("Voxel and hva limb counts don't match");
|
||||
throw new InvalidOperationException("{0}.vxl and {1}.hva limb counts don't match.".F(files.Vxl, files.Hva));
|
||||
|
||||
transforms = hva.Transforms;
|
||||
frames = hva.FrameCount;
|
||||
|
||||
@@ -77,8 +77,8 @@ namespace OpenRA.Mods.Cnc.Graphics
|
||||
var size = new Size(su, sv);
|
||||
var s = sheetBuilder.Allocate(size);
|
||||
var t = sheetBuilder.Allocate(size);
|
||||
OpenRA.Graphics.Util.FastCopyIntoChannel(s, colors);
|
||||
OpenRA.Graphics.Util.FastCopyIntoChannel(t, normals);
|
||||
OpenRA.Graphics.Util.FastCopyIntoChannel(s, colors, SpriteFrameType.Indexed8);
|
||||
OpenRA.Graphics.Util.FastCopyIntoChannel(t, normals, SpriteFrameType.Indexed8);
|
||||
|
||||
// s and t are guaranteed to use the same sheet because
|
||||
// of the custom voxel sheet allocation implementation
|
||||
@@ -216,9 +216,10 @@ namespace OpenRA.Mods.Cnc.Graphics
|
||||
HvaReader hva;
|
||||
using (var s = fileSystem.Open(files.Vxl + ".vxl"))
|
||||
vxl = new VxlReader(s);
|
||||
|
||||
using (var s = fileSystem.Open(files.Hva + ".hva"))
|
||||
hva = new HvaReader(s, files.Hva + ".hva");
|
||||
return new Voxel(this, vxl, hva);
|
||||
return new Voxel(this, vxl, hva, files);
|
||||
}
|
||||
|
||||
public Voxel Load(string vxl, string hva)
|
||||
|
||||
@@ -86,6 +86,11 @@ namespace OpenRA.Mods.Cnc.Graphics
|
||||
return loader.Load(vxl, hva);
|
||||
}
|
||||
|
||||
public IModel GetModel(string model)
|
||||
{
|
||||
return loader.Load(model, model);
|
||||
}
|
||||
|
||||
public IModel GetModelSequence(string model, string sequence)
|
||||
{
|
||||
try { return models[model][sequence]; }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Optimize>true</Optimize>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
@@ -11,6 +11,7 @@
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
|
||||
<IsPublishable Condition="'$(CopyCncDll)' == 'False'">false</IsPublishable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- Work around an issue where Rider does not detect files in the project root using the default glob -->
|
||||
@@ -37,4 +38,4 @@
|
||||
<Analyzer Remove="@(Analyzer)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace OpenRA.Mods.Cnc.SpriteLoaders
|
||||
|
||||
class ShpD2Frame : ISpriteFrame
|
||||
{
|
||||
public SpriteFrameType Type { get { return SpriteFrameType.Indexed; } }
|
||||
public SpriteFrameType Type { get { return SpriteFrameType.Indexed8; } }
|
||||
public Size Size { get; private set; }
|
||||
public Size FrameSize { get { return Size; } }
|
||||
public float2 Offset { get { return float2.Zero; } }
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace OpenRA.Mods.Cnc.SpriteLoaders
|
||||
|
||||
class ImageHeader : ISpriteFrame
|
||||
{
|
||||
public SpriteFrameType Type { get { return SpriteFrameType.Indexed; } }
|
||||
public SpriteFrameType Type { get { return SpriteFrameType.Indexed8; } }
|
||||
public Size Size { get { return reader.Size; } }
|
||||
public Size FrameSize { get { return reader.Size; } }
|
||||
public float2 Offset { get { return float2.Zero; } }
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Cnc.SpriteLoaders
|
||||
{
|
||||
class TmpRAFrame : ISpriteFrame
|
||||
{
|
||||
public SpriteFrameType Type { get { return SpriteFrameType.Indexed; } }
|
||||
public SpriteFrameType Type { get { return SpriteFrameType.Indexed8; } }
|
||||
public Size Size { get; private set; }
|
||||
public Size FrameSize { get; private set; }
|
||||
public float2 Offset { get { return float2.Zero; } }
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Cnc.SpriteLoaders
|
||||
{
|
||||
class TmpTDFrame : ISpriteFrame
|
||||
{
|
||||
public SpriteFrameType Type { get { return SpriteFrameType.Indexed; } }
|
||||
public SpriteFrameType Type { get { return SpriteFrameType.Indexed8; } }
|
||||
public Size Size { get; private set; }
|
||||
public Size FrameSize { get; private set; }
|
||||
public float2 Offset { get { return float2.Zero; } }
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace OpenRA.Mods.Cnc.SpriteLoaders
|
||||
{
|
||||
readonly TmpTSFrame parent;
|
||||
|
||||
public SpriteFrameType Type { get { return SpriteFrameType.Indexed; } }
|
||||
public SpriteFrameType Type { get { return SpriteFrameType.Indexed8; } }
|
||||
public Size Size { get { return parent.Size; } }
|
||||
public Size FrameSize { get { return Size; } }
|
||||
public float2 Offset { get { return parent.Offset; } }
|
||||
@@ -36,7 +36,7 @@ namespace OpenRA.Mods.Cnc.SpriteLoaders
|
||||
|
||||
class TmpTSFrame : ISpriteFrame
|
||||
{
|
||||
public SpriteFrameType Type { get { return SpriteFrameType.Indexed; } }
|
||||
public SpriteFrameType Type { get { return SpriteFrameType.Indexed8; } }
|
||||
public Size Size { get; private set; }
|
||||
public Size FrameSize { get { return Size; } }
|
||||
public float2 Offset { get; private set; }
|
||||
|
||||
@@ -26,11 +26,9 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
[Desc("The prerequisite type that this provides.")]
|
||||
public readonly string Prerequisite = null;
|
||||
|
||||
[Translate]
|
||||
[Desc("Label to display over the support power icon and in its tooltip while the power is active.")]
|
||||
public readonly string ActiveText = "ACTIVE";
|
||||
|
||||
[Translate]
|
||||
[Desc("Label to display over the support power icon and in its tooltip while the power is available but not active.")]
|
||||
public readonly string AvailableText = "READY";
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Cnc.SpriteLoaders;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
@@ -35,7 +36,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
||||
var dest = inputFiles[0].Split('-').First() + ".shp";
|
||||
|
||||
var frames = inputFiles.Select(a => new Png(File.OpenRead(a))).ToList();
|
||||
if (frames.Any(f => f.Palette == null))
|
||||
if (frames.Any(f => f.Type != SpriteFrameType.Indexed8))
|
||||
throw new InvalidOperationException("All frames must be paletted");
|
||||
|
||||
var size = new Size(frames[0].Width, frames[0].Height);
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace OpenRA.Mods.Common.Lint
|
||||
if (!string.IsNullOrWhiteSpace(player.Faction) && !factions.Contains(player.Faction))
|
||||
emitError("Invalid faction {0} chosen for player {1}.".F(player.Faction, player.Name));
|
||||
|
||||
if (worldActor.HasTraitInfo<MPStartLocationsInfo>())
|
||||
if (worldActor.HasTraitInfo<MapStartingLocationsInfo>())
|
||||
{
|
||||
var playerCount = players.Count(p => p.Value.Playable);
|
||||
var spawns = new List<CPos>();
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace OpenRA.Mods.Common.LoadScreens
|
||||
public LaunchArguments Launch;
|
||||
protected ModData ModData { get; private set; }
|
||||
|
||||
bool initialized;
|
||||
|
||||
public virtual void Init(ModData modData, Dictionary<string, string> info)
|
||||
{
|
||||
ModData = modData;
|
||||
@@ -31,12 +33,15 @@ namespace OpenRA.Mods.Common.LoadScreens
|
||||
|
||||
public virtual void Display()
|
||||
{
|
||||
if (Game.Renderer == null)
|
||||
if (Game.Renderer == null || initialized)
|
||||
return;
|
||||
|
||||
// Draw a black screen
|
||||
Game.Renderer.BeginUI();
|
||||
Game.Renderer.EndFrame(new NullInputHandler());
|
||||
|
||||
// PERF: draw the screen only once
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
public virtual void StartGame(Arguments args)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Optimize>true</Optimize>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
@@ -11,6 +11,7 @@
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- Work around an issue where Rider does not detect files in the project root using the default glob -->
|
||||
@@ -24,6 +25,7 @@
|
||||
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj">
|
||||
<Private>False</Private>
|
||||
</ProjectReference>
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
|
||||
<PackageReference Include="OpenRA-FuzzyLogicLibrary" Version="1.0.1" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.0.150" />
|
||||
<PackageReference Include="rix0rrr.BeaconLib" Version="1.0.2" />
|
||||
|
||||
@@ -61,24 +61,22 @@ namespace OpenRA.Mods.Common.SpriteLoaders
|
||||
RegionsFromSlices(png, out frameRegions, out frameOffsets);
|
||||
|
||||
frames = new ISpriteFrame[frameRegions.Count];
|
||||
|
||||
var stride = png.PixelStride;
|
||||
for (var i = 0; i < frames.Length; i++)
|
||||
{
|
||||
var frameStart = frameRegions[i].X + frameRegions[i].Y * png.Width;
|
||||
var frameSize = new Size(frameRegions[i].Width, frameRegions[i].Height);
|
||||
var pixelLength = png.Palette == null ? 4 : 1;
|
||||
|
||||
frames[i] = new PngSheetFrame()
|
||||
{
|
||||
Size = frameSize,
|
||||
FrameSize = frameSize,
|
||||
Offset = frameOffsets[i],
|
||||
Data = new byte[frameRegions[i].Width * frameRegions[i].Height * pixelLength],
|
||||
Type = png.Palette == null ? SpriteFrameType.BGRA : SpriteFrameType.Indexed
|
||||
Data = new byte[frameRegions[i].Width * frameRegions[i].Height * stride],
|
||||
Type = png.Type
|
||||
};
|
||||
|
||||
for (var y = 0; y < frames[i].Size.Height; y++)
|
||||
Array.Copy(png.Data, (frameStart + y * png.Width) * pixelLength, frames[i].Data, y * frames[i].Size.Width * pixelLength, frames[i].Size.Width * pixelLength);
|
||||
Array.Copy(png.Data, (frameStart + y * png.Width) * stride, frames[i].Data, y * frames[i].Size.Width * stride, frames[i].Size.Width * stride);
|
||||
}
|
||||
|
||||
metadata = new TypeDictionary
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace OpenRA.Mods.Common.SpriteLoaders
|
||||
{
|
||||
class ShpTSFrame : ISpriteFrame
|
||||
{
|
||||
public SpriteFrameType Type { get { return SpriteFrameType.Indexed; } }
|
||||
public SpriteFrameType Type { get { return SpriteFrameType.Indexed8; } }
|
||||
public Size Size { get; private set; }
|
||||
public Size FrameSize { get; private set; }
|
||||
public float2 Offset { get; private set; }
|
||||
|
||||
@@ -54,7 +54,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Sort order for the production palette. Smaller numbers are presented earlier.")]
|
||||
public readonly int BuildPaletteOrder = 9999;
|
||||
|
||||
[Translate]
|
||||
[Desc("Text shown in the production tooltip.")]
|
||||
public readonly string Description = "";
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Delay for the end game notification in milliseconds.")]
|
||||
public readonly int NotificationDelay = 1500;
|
||||
|
||||
[Translate]
|
||||
[Desc("Description of the objective.")]
|
||||
public readonly string Objective = "Destroy all opposition!";
|
||||
|
||||
|
||||
@@ -19,11 +19,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Attach this to the player actor.")]
|
||||
public class DeveloperModeInfo : TraitInfo, ILobbyOptions
|
||||
{
|
||||
[Translate]
|
||||
[Desc("Descriptive label for the developer mode checkbox in the lobby.")]
|
||||
public readonly string CheckboxLabel = "Debug Menu";
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the developer mode checkbox in the lobby.")]
|
||||
public readonly string CheckboxDescription = "Enables cheats and developer commands";
|
||||
|
||||
|
||||
@@ -18,11 +18,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public class PlayerResourcesInfo : TraitInfo, ILobbyOptions
|
||||
{
|
||||
[Translate]
|
||||
[Desc("Descriptive label for the starting cash option in the lobby.")]
|
||||
public readonly string DefaultCashDropdownLabel = "Starting Cash";
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the starting cash option in the lobby.")]
|
||||
public readonly string DefaultCashDropdownDescription = "Change the amount of cash that players start with";
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Internal id for this tech level.")]
|
||||
public readonly string Id;
|
||||
|
||||
[Translate]
|
||||
[Desc("Name shown in the lobby options.")]
|
||||
public readonly string Name;
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Delay for the end game notification in milliseconds.")]
|
||||
public readonly int NotificationDelay = 1500;
|
||||
|
||||
[Translate]
|
||||
[Desc("Description of the objective")]
|
||||
public readonly string Objective = "Hold all the strategic positions!";
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ namespace OpenRA.Mods.Common.Traits.Render
|
||||
[Desc("Displays a text overlay relative to the selection box.")]
|
||||
public class WithTextDecorationInfo : WithDecorationBaseInfo
|
||||
{
|
||||
[Translate]
|
||||
[FieldLoader.Require]
|
||||
public readonly string Text = null;
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public abstract class TooltipInfoBase : ConditionalTraitInfo, Requires<IMouseBoundsInfo>
|
||||
{
|
||||
[Translate]
|
||||
public readonly string Name = "";
|
||||
}
|
||||
|
||||
@@ -28,7 +27,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Shown in the build palette widget.")]
|
||||
public class TooltipInfo : TooltipInfoBase, ITooltipInfo
|
||||
{
|
||||
[Translate]
|
||||
[Desc("An optional generic name (i.e. \"Soldier\" or \"Structure\")" +
|
||||
"to be shown to chosen players.")]
|
||||
public readonly string GenericName = null;
|
||||
@@ -36,15 +34,12 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Prefix generic tooltip name with 'Ally/Neutral/EnemyPrefix'.")]
|
||||
public readonly bool GenericStancePrefix = true;
|
||||
|
||||
[Translate]
|
||||
[Desc("Prefix to display in the tooltip for allied units.")]
|
||||
public readonly string AllyPrefix = "Allied";
|
||||
|
||||
[Translate]
|
||||
[Desc("Prefix to display in the tooltip for neutral units.")]
|
||||
public readonly string NeutralPrefix = null;
|
||||
|
||||
[Translate]
|
||||
[Desc("Prefix to display in the tooltip for enemy units.")]
|
||||
public readonly string EnemyPrefix = "Enemy";
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public class TooltipDescriptionInfo : ConditionalTraitInfo
|
||||
{
|
||||
[Desc("Text shown in tooltip.")]
|
||||
[Translate]
|
||||
public readonly string Description = "";
|
||||
|
||||
[Desc("Player relationships who can view the description.")]
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -25,8 +26,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Maximum number of actors.")]
|
||||
public readonly int Maximum = 4;
|
||||
|
||||
[Desc("Time (in ticks) between actor spawn.")]
|
||||
public readonly int SpawnInterval = 6000;
|
||||
[Desc("Time (in ticks) between actor spawn. Supports 1 or 2 values.\nIf 2 values are provided they are used as a range from which a value is randomly selected.")]
|
||||
public readonly int[] SpawnInterval = { 6000 };
|
||||
|
||||
[FieldLoader.Require]
|
||||
[ActorReference]
|
||||
@@ -38,6 +39,20 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Type of ActorSpawner with which it connects.")]
|
||||
public readonly HashSet<string> Types = new HashSet<string>() { };
|
||||
|
||||
public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
|
||||
{
|
||||
base.RulesetLoaded(rules, ai);
|
||||
|
||||
if (SpawnInterval.Length == 0 || SpawnInterval.Length > 2)
|
||||
throw new YamlException("{0}.{1} must be either 1 or 2 values".F(nameof(ActorSpawnManager), nameof(SpawnInterval)));
|
||||
|
||||
if (SpawnInterval.Length == 2 && SpawnInterval[0] >= SpawnInterval[1])
|
||||
throw new YamlException("{0}.{1}'s first value must be less than the second value".F(nameof(ActorSpawnManager), nameof(SpawnInterval)));
|
||||
|
||||
if (SpawnInterval.Any(it => it < 0))
|
||||
throw new YamlException("{0}.{1}'s value(s) must not be less than 0".F(nameof(ActorSpawnManager), nameof(SpawnInterval)));
|
||||
}
|
||||
|
||||
public override object Create(ActorInitializer init) { return new ActorSpawnManager(init.Self, this); }
|
||||
}
|
||||
|
||||
@@ -77,7 +92,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (spawnPoint == null)
|
||||
return;
|
||||
|
||||
spawnCountdown = info.SpawnInterval;
|
||||
spawnCountdown = Util.RandomDelay(self.World, info.SpawnInterval);
|
||||
|
||||
do
|
||||
{
|
||||
|
||||
@@ -20,11 +20,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public class CrateSpawnerInfo : TraitInfo, ILobbyOptions
|
||||
{
|
||||
[Translate]
|
||||
[Desc("Descriptive label for the crates checkbox in the lobby.")]
|
||||
public readonly string CheckboxLabel = "Crates";
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the crates checkbox in the lobby.")]
|
||||
public readonly string CheckboxDescription = "Collect crates with units to receive random bonuses or penalties";
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ using OpenRA.Traits;
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Attach this to the world actor.")]
|
||||
public class CreateMPPlayersInfo : TraitInfo<CreateMPPlayers>, ICreatePlayersInfo
|
||||
public class CreateMapPlayersInfo : TraitInfo<CreateMapPlayers>, ICreatePlayersInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a list of GameInformation.Players that matches the indexing of ICreatePlayers.CreatePlayers.
|
||||
@@ -80,7 +80,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
}
|
||||
|
||||
public class CreateMPPlayers : ICreatePlayers
|
||||
public class CreateMapPlayers : ICreatePlayers
|
||||
{
|
||||
void ICreatePlayers.CreatePlayers(World w, MersenneTwister playerRandom)
|
||||
{
|
||||
@@ -17,11 +17,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Controls the build radius checkboxes in the lobby options.")]
|
||||
public class MapBuildRadiusInfo : TraitInfo, ILobbyOptions
|
||||
{
|
||||
[Translate]
|
||||
[Desc("Descriptive label for the ally build radius checkbox in the lobby.")]
|
||||
public readonly string AllyBuildRadiusCheckboxLabel = "Build off Allies";
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the ally build radius checkbox in the lobby.")]
|
||||
public readonly string AllyBuildRadiusCheckboxDescription = "Allow allies to place structures inside your build area";
|
||||
|
||||
@@ -37,11 +35,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Display order for the ally build radius checkbox in the lobby.")]
|
||||
public readonly int AllyBuildRadiusCheckboxDisplayOrder = 0;
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the build radius checkbox in the lobby.")]
|
||||
public readonly string BuildRadiusCheckboxLabel = "Limit Build Area";
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the build radius checkbox in the lobby.")]
|
||||
public readonly string BuildRadiusCheckboxDescription = "Limits structure placement to areas around Construction Yards";
|
||||
|
||||
|
||||
@@ -17,11 +17,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Controls the 'Creeps' checkbox in the lobby options.")]
|
||||
public class MapCreepsInfo : TraitInfo, ILobbyOptions
|
||||
{
|
||||
[Translate]
|
||||
[Desc("Descriptive label for the creeps checkbox in the lobby.")]
|
||||
public readonly string CheckboxLabel = "Creep Actors";
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the creeps checkbox in the lobby.")]
|
||||
public readonly string CheckboxDescription = "Hostile forces spawn on the battlefield";
|
||||
|
||||
|
||||
@@ -18,11 +18,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Controls the game speed, tech level, and short game lobby options.")]
|
||||
public class MapOptionsInfo : TraitInfo, ILobbyOptions, IRulesetLoaded
|
||||
{
|
||||
[Translate]
|
||||
[Desc("Descriptive label for the short game checkbox in the lobby.")]
|
||||
public readonly string ShortGameCheckboxLabel = "Short Game";
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the short game checkbox in the lobby.")]
|
||||
public readonly string ShortGameCheckboxDescription = "Players are defeated when their bases are destroyed";
|
||||
|
||||
@@ -38,11 +36,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Display order for the short game checkbox in the lobby.")]
|
||||
public readonly int ShortGameCheckboxDisplayOrder = 0;
|
||||
|
||||
[Translate]
|
||||
[Desc("Descriptive label for the tech level option in the lobby.")]
|
||||
public readonly string TechLevelDropdownLabel = "Tech Level";
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the tech level option in the lobby.")]
|
||||
public readonly string TechLevelDropdownDescription = "Change the units and abilities at your disposal";
|
||||
|
||||
@@ -58,11 +54,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Display order for the tech level option in the lobby.")]
|
||||
public readonly int TechLevelDropdownDisplayOrder = 0;
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the game speed option in the lobby.")]
|
||||
public readonly string GameSpeedDropdownLabel = "Game Speed";
|
||||
|
||||
[Translate]
|
||||
[Desc("Description of the game speed option in the lobby.")]
|
||||
public readonly string GameSpeedDropdownDescription = "Change the rate at which time passes";
|
||||
|
||||
|
||||
@@ -20,15 +20,13 @@ using OpenRA.Traits;
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Allows the map to have working spawnpoints. Also controls the 'Separate Team Spawns' checkbox in the lobby options.")]
|
||||
public class MPStartLocationsInfo : TraitInfo, ILobbyOptions, IAssignSpawnPointsInfo
|
||||
public class MapStartingLocationsInfo : TraitInfo, ILobbyOptions, IAssignSpawnPointsInfo
|
||||
{
|
||||
public readonly WDist InitialExploreRange = WDist.FromCells(5);
|
||||
|
||||
[Translate]
|
||||
[Desc("Descriptive label for the spawn positions checkbox in the lobby.")]
|
||||
public readonly string SeparateTeamSpawnsCheckboxLabel = "Separate Team Spawns";
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the spawn positions checkbox in the lobby.")]
|
||||
public readonly string SeparateTeamSpawnsCheckboxDescription = "Players without assigned spawn points will start as far as possible from enemy players";
|
||||
|
||||
@@ -44,7 +42,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Display order for the spawn positions checkbox in the lobby.")]
|
||||
public readonly int SeparateTeamSpawnsCheckboxDisplayOrder = 0;
|
||||
|
||||
public override object Create(ActorInitializer init) { return new MPStartLocations(this); }
|
||||
public override object Create(ActorInitializer init) { return new MapStartingLocations(this); }
|
||||
|
||||
IEnumerable<LobbyOption> ILobbyOptions.LobbyOptions(Ruleset rules)
|
||||
{
|
||||
@@ -105,15 +103,15 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
}
|
||||
|
||||
public class MPStartLocations : IWorldLoaded, INotifyCreated, IAssignSpawnPoints
|
||||
public class MapStartingLocations : IWorldLoaded, INotifyCreated, IAssignSpawnPoints
|
||||
{
|
||||
readonly MPStartLocationsInfo info;
|
||||
readonly MapStartingLocationsInfo info;
|
||||
readonly Dictionary<int, Session.Client> occupiedSpawnPoints = new Dictionary<int, Session.Client>();
|
||||
bool separateTeamSpawns;
|
||||
CPos[] spawnLocations;
|
||||
List<int> availableSpawnPoints;
|
||||
|
||||
public MPStartLocations(MPStartLocationsInfo info)
|
||||
public MapStartingLocations(MapStartingLocationsInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
}
|
||||
@@ -14,8 +14,8 @@ using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Used by SpawnMPUnits. Attach these to the world actor. You can have multiple variants by adding @suffixes.")]
|
||||
public class MPStartUnitsInfo : TraitInfo<MPStartUnits>
|
||||
[Desc("Used by SpawnStartingUnits. Attach these to the world actor. You can have multiple variants by adding @suffixes.")]
|
||||
public class StartingUnitsInfo : TraitInfo<StartingUnits>
|
||||
{
|
||||
[Desc("Internal class ID.")]
|
||||
public readonly string Class = "none";
|
||||
@@ -50,5 +50,5 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public readonly WAngle? SupportActorsFacing = null;
|
||||
}
|
||||
|
||||
public class MPStartUnits { }
|
||||
public class StartingUnits { }
|
||||
}
|
||||
@@ -21,12 +21,10 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Internal id for this option.")]
|
||||
public readonly string ID = null;
|
||||
|
||||
[Translate]
|
||||
[FieldLoader.Require]
|
||||
[Desc("Descriptive label for this option.")]
|
||||
public readonly string Label = null;
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for this option.")]
|
||||
public readonly string Description = null;
|
||||
|
||||
|
||||
@@ -19,15 +19,13 @@ using OpenRA.Traits;
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Spawn base actor at the spawnpoint and support units in an annulus around the base actor. Both are defined at MPStartUnits. Attach this to the world actor.")]
|
||||
public class SpawnMPUnitsInfo : TraitInfo, Requires<MPStartUnitsInfo>, ILobbyOptions
|
||||
public class SpawnStartingUnitsInfo : TraitInfo, Requires<StartingUnitsInfo>, ILobbyOptions
|
||||
{
|
||||
public readonly string StartingUnitsClass = "none";
|
||||
|
||||
[Translate]
|
||||
[Desc("Descriptive label for the starting units option in the lobby.")]
|
||||
public readonly string DropdownLabel = "Starting Units";
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the starting units option in the lobby.")]
|
||||
public readonly string DropdownDescription = "Change the units that you start the game with";
|
||||
|
||||
@@ -45,7 +43,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var startingUnits = new Dictionary<string, string>();
|
||||
|
||||
// Duplicate classes are defined for different race variants
|
||||
foreach (var t in rules.Actors["world"].TraitInfos<MPStartUnitsInfo>())
|
||||
foreach (var t in rules.Actors["world"].TraitInfos<StartingUnitsInfo>())
|
||||
startingUnits[t.Class] = t.ClassName;
|
||||
|
||||
if (startingUnits.Any())
|
||||
@@ -53,14 +51,14 @@ namespace OpenRA.Mods.Common.Traits
|
||||
new ReadOnlyDictionary<string, string>(startingUnits), StartingUnitsClass, DropdownLocked);
|
||||
}
|
||||
|
||||
public override object Create(ActorInitializer init) { return new SpawnMPUnits(this); }
|
||||
public override object Create(ActorInitializer init) { return new SpawnStartingUnits(this); }
|
||||
}
|
||||
|
||||
public class SpawnMPUnits : IWorldLoaded
|
||||
public class SpawnStartingUnits : IWorldLoaded
|
||||
{
|
||||
readonly SpawnMPUnitsInfo info;
|
||||
readonly SpawnStartingUnitsInfo info;
|
||||
|
||||
public SpawnMPUnits(SpawnMPUnitsInfo info)
|
||||
public SpawnStartingUnits(SpawnStartingUnitsInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
}
|
||||
@@ -77,7 +75,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var spawnClass = p.PlayerReference.StartingUnitsClass ?? w.LobbyInfo.GlobalSettings
|
||||
.OptionOrDefault("startingunits", info.StartingUnitsClass);
|
||||
|
||||
var unitGroup = w.Map.Rules.Actors["world"].TraitInfos<MPStartUnitsInfo>()
|
||||
var unitGroup = w.Map.Rules.Actors["world"].TraitInfos<StartingUnitsInfo>()
|
||||
.Where(g => g.Class == spawnClass && g.Factions != null && g.Factions.Contains(p.Faction.InternalName))
|
||||
.RandomOrDefault(w.SharedRandom);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenRA.Mods.Common.UpdateRules.Rules
|
||||
{
|
||||
public class RenameMPTraits : UpdateRule
|
||||
{
|
||||
public override string Name { get { return "Several traits spawning map actors and players have been renamed."; } }
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
return "'SpawnMPUnits' was renamed to 'SpawnStartingUnits', 'MPStartUnits' to 'StartingUnits', 'MPStartLocations' to " +
|
||||
"'MapStartingLocations', and 'CreateMPPlayers' to 'CreateMapPlayers'.";
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
|
||||
{
|
||||
actorNode.RenameChildrenMatching("SpawnMPUnits", "SpawnStartingUnits");
|
||||
actorNode.RenameChildrenMatching("MPStartUnits", "StartingUnits");
|
||||
actorNode.RenameChildrenMatching("MPStartLocations", "MapStartingLocations");
|
||||
actorNode.RenameChildrenMatching("CreateMPPlayers", "CreateMapPlayers");
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,9 +53,9 @@ namespace OpenRA.Mods.Common.UpdateRules
|
||||
new RenameRallyPointPath(),
|
||||
}),
|
||||
|
||||
new UpdatePath("release-20200503", new UpdateRule[]
|
||||
new UpdatePath("release-20200503", "playtest-20201213", new UpdateRule[]
|
||||
{
|
||||
// Bleed only changes here
|
||||
// Prep only changes here
|
||||
new AddPipDecorationTraits(),
|
||||
new ModernizeDecorationTraits(),
|
||||
new RenameHealCrateAction(),
|
||||
@@ -79,6 +79,12 @@ namespace OpenRA.Mods.Common.UpdateRules
|
||||
new RenameCircleContrast(),
|
||||
new SplitDamagedByTerrain(),
|
||||
new RemoveLaysTerrain(),
|
||||
}),
|
||||
|
||||
new UpdatePath("playtest-20201213", new UpdateRule[]
|
||||
{
|
||||
// Bleed only changes here
|
||||
new RenameMPTraits(),
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace OpenRA.Mods.Common.UtilityCommands
|
||||
{
|
||||
public class CheckRuntimeAssembliesCommand : IUtilityCommand
|
||||
{
|
||||
string IUtilityCommand.Name { get { return "--check-runtime-assemblies"; } }
|
||||
|
||||
bool IUtilityCommand.ValidateArguments(string[] args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
[Desc("ASSEMBLY [ASSEMBLY ...]", "Check the runtime dependencies of the mod against a given whitelist of " +
|
||||
"assembly (dll and exe) names and generate an error if any unlisted files are required.")]
|
||||
void IUtilityCommand.Run(Utility utility, string[] args)
|
||||
{
|
||||
var whitelist = args
|
||||
.Skip(1)
|
||||
.Select(a => Path.GetFileName(a))
|
||||
.ToArray();
|
||||
|
||||
// Load the renderer assembly so we can check its dependencies
|
||||
Assembly.LoadFile(Path.Combine(Platform.BinDir, "OpenRA.Platforms.Default.dll"));
|
||||
|
||||
var missing = new List<string>();
|
||||
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
var assemblyName = Path.GetFileName(a.Location);
|
||||
if (!whitelist.Contains(assemblyName))
|
||||
missing.Add(assemblyName);
|
||||
}
|
||||
|
||||
if (missing.Any())
|
||||
{
|
||||
Console.WriteLine("error: The following assemblies are referenced but not whitelisted:");
|
||||
foreach (var m in missing)
|
||||
Console.WriteLine(" " + m);
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,16 +78,8 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
frame.Size.Width);
|
||||
}
|
||||
|
||||
if (frame.Type == SpriteFrameType.BGRA)
|
||||
{
|
||||
var png = new Png(pngData, frameSize.Width, frameSize.Height);
|
||||
png.Save("{0}-{1:D4}.png".F(prefix, count++));
|
||||
}
|
||||
else
|
||||
{
|
||||
var png = new Png(pngData, frameSize.Width, frameSize.Height, palColors);
|
||||
png.Save("{0}-{1:D4}.png".F(prefix, count++));
|
||||
}
|
||||
var png = new Png(pngData, SpriteFrameType.Indexed8, frameSize.Width, frameSize.Height, palColors);
|
||||
png.Save("{0}-{1:D4}.png".F(prefix, count++));
|
||||
}
|
||||
|
||||
Console.WriteLine("Saved {0}-[0..{1}].png", prefix, count - 1);
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
|
||||
var count = 0;
|
||||
|
||||
var sb = sequences.SpriteCache.SheetBuilders[SpriteFrameType.Indexed];
|
||||
var sb = sequences.SpriteCache.SheetBuilders[SheetType.Indexed];
|
||||
foreach (var s in sb.AllSheets)
|
||||
{
|
||||
var max = s == sb.Current ? (int)sb.CurrentChannel + 1 : 4;
|
||||
@@ -53,7 +53,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
s.AsPng((TextureChannel)ChannelMasks[i], palette).Save("{0}.png".F(count++));
|
||||
}
|
||||
|
||||
sb = sequences.SpriteCache.SheetBuilders[SpriteFrameType.BGRA];
|
||||
sb = sequences.SpriteCache.SheetBuilders[SheetType.BGRA];
|
||||
foreach (var s in sb.AllSheets)
|
||||
s.AsPng().Save("{0}.png".F(count++));
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.Mods.Common.UtilityCommands
|
||||
{
|
||||
class ExtractLanguageStringsCommand : IUtilityCommand
|
||||
{
|
||||
string IUtilityCommand.Name { get { return "--extract-language-strings"; } }
|
||||
|
||||
bool IUtilityCommand.ValidateArguments(string[] args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
[Desc("Extract translatable strings that are not yet localized and update chrome layout.")]
|
||||
void IUtilityCommand.Run(Utility utility, string[] args)
|
||||
{
|
||||
// HACK: The engine code assumes that Game.modData is set.
|
||||
var modData = Game.ModData = utility.ModData;
|
||||
|
||||
var types = modData.ObjectCreator.GetTypes();
|
||||
var translatableFields = types.SelectMany(t => t.GetFields())
|
||||
.Where(f => f.HasAttribute<TranslateAttribute>()).Distinct();
|
||||
|
||||
foreach (var filename in modData.Manifest.ChromeLayout)
|
||||
{
|
||||
modData.ModFiles.TryGetPackageContaining(filename, out var package, out var name);
|
||||
name = package.Name + "/" + name;
|
||||
Console.WriteLine("# {0}:", filename);
|
||||
|
||||
var yaml = MiniYaml.FromFile(name, false);
|
||||
FromChromeLayout(ref yaml, null,
|
||||
translatableFields.Select(t => t.Name).Distinct(), null);
|
||||
using (var file = new StreamWriter(name))
|
||||
file.WriteLine(yaml.WriteToString());
|
||||
}
|
||||
|
||||
// TODO: Properties can also be translated.
|
||||
}
|
||||
|
||||
internal static void FromChromeLayout(ref List<MiniYamlNode> nodes, MiniYamlNode parent, IEnumerable<string> translatables, string container)
|
||||
{
|
||||
var parentNode = parent != null && parent.Key != null ? parent.Key.Split('@') : null;
|
||||
var parentType = parent != null && parent.Key != null ? parentNode.First() : null;
|
||||
var parentLabel = parent != null && parent.Key != null ? parentNode.Last() : null;
|
||||
|
||||
if ((parentType == "Background" || parentType == "Container") && parentLabel.IsUppercase())
|
||||
container = parentLabel;
|
||||
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
var alreadyTranslated = node.Value.Value != null && node.Value.Value.Contains('@');
|
||||
if (translatables.Contains(node.Key) && !alreadyTranslated && parentLabel != null)
|
||||
{
|
||||
var translationKey = "{0}-{1}".F(parentLabel.Replace('_', '-'), node.Key.ToUpper());
|
||||
if (container != null)
|
||||
translationKey = "{0}-".F(container.Replace('_', '-')) + translationKey;
|
||||
Console.WriteLine("\t{0}: {1}", translationKey, node.Value.Value);
|
||||
node.Value.Value = "@{0}@".F(translationKey);
|
||||
}
|
||||
|
||||
FromChromeLayout(ref node.Value.Nodes, node, translatables, container);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
public bool DisableKeyRepeat = false;
|
||||
public bool DisableKeySound = false;
|
||||
|
||||
[Translate]
|
||||
public string Text = "";
|
||||
public TextAlign Align = TextAlign.Center;
|
||||
public int LeftMargin = 5;
|
||||
@@ -58,11 +57,9 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
protected Lazy<TooltipContainerWidget> tooltipContainer;
|
||||
|
||||
[Translate]
|
||||
public string TooltipText;
|
||||
public Func<string> GetTooltipText;
|
||||
|
||||
[Translate]
|
||||
public string TooltipDesc;
|
||||
public Func<string> GetTooltipDesc;
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
public Func<string> GetImageName;
|
||||
public Func<string> GetImageCollection;
|
||||
|
||||
[Translate]
|
||||
public string TooltipText;
|
||||
|
||||
Lazy<TooltipContainerWidget> tooltipContainer;
|
||||
|
||||
@@ -21,7 +21,6 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
public class LabelWidget : Widget
|
||||
{
|
||||
[Translate]
|
||||
public string Text = null;
|
||||
public TextAlign Align = TextAlign.Left;
|
||||
public TextVAlign VAlign = TextVAlign.Middle;
|
||||
|
||||
@@ -43,10 +43,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
string currentFilename;
|
||||
IReadOnlyPackage currentPackage;
|
||||
Sprite[] currentSprites;
|
||||
IModel currentVoxel;
|
||||
VqaPlayerWidget player = null;
|
||||
bool isVideoLoaded = false;
|
||||
bool isLoadError = false;
|
||||
int currentFrame;
|
||||
WRot modelOrientation;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public AssetBrowserLogic(Widget widget, Action onExit, ModData modData, World world, Dictionary<string, MiniYaml> logicArgs)
|
||||
@@ -79,13 +81,24 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
spriteWidget.GetSprite = () => currentSprites != null ? currentSprites[currentFrame] : null;
|
||||
currentPalette = spriteWidget.Palette;
|
||||
spriteWidget.GetPalette = () => currentPalette;
|
||||
spriteWidget.IsVisible = () => !isVideoLoaded && !isLoadError;
|
||||
spriteWidget.IsVisible = () => !isVideoLoaded && !isLoadError && currentSprites != null;
|
||||
}
|
||||
|
||||
var playerWidget = panel.GetOrNull<VqaPlayerWidget>("PLAYER");
|
||||
if (playerWidget != null)
|
||||
playerWidget.IsVisible = () => isVideoLoaded && !isLoadError;
|
||||
|
||||
var modelWidget = panel.GetOrNull<ModelWidget>("VOXEL");
|
||||
if (modelWidget != null)
|
||||
{
|
||||
modelWidget.GetVoxel = () => currentVoxel;
|
||||
currentPalette = modelWidget.Palette;
|
||||
modelWidget.GetPalette = () => currentPalette;
|
||||
modelWidget.GetPlayerPalette = () => currentPalette;
|
||||
modelWidget.GetRotation = () => modelOrientation;
|
||||
modelWidget.IsVisible = () => !isVideoLoaded && !isLoadError && currentVoxel != null;
|
||||
}
|
||||
|
||||
var errorLabelWidget = panel.GetOrNull("ERROR");
|
||||
if (errorLabelWidget != null)
|
||||
errorLabelWidget.IsVisible = () => isLoadError;
|
||||
@@ -210,6 +223,46 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
prevButton.IsVisible = () => !isVideoLoaded;
|
||||
}
|
||||
|
||||
var voxelContainer = panel.GetOrNull("VOXEL_SELECTOR");
|
||||
if (voxelContainer != null)
|
||||
voxelContainer.IsVisible = () => currentVoxel != null;
|
||||
|
||||
var rollSlider = panel.GetOrNull<SliderWidget>("ROLL_SLIDER");
|
||||
if (rollSlider != null)
|
||||
{
|
||||
rollSlider.OnChange += x =>
|
||||
{
|
||||
var roll = (int)x;
|
||||
modelOrientation = modelOrientation.WithRoll(new WAngle(roll));
|
||||
};
|
||||
|
||||
rollSlider.GetValue = () => modelOrientation.Roll.Angle;
|
||||
}
|
||||
|
||||
var pitchSlider = panel.GetOrNull<SliderWidget>("PITCH_SLIDER");
|
||||
if (pitchSlider != null)
|
||||
{
|
||||
pitchSlider.OnChange += x =>
|
||||
{
|
||||
var pitch = (int)x;
|
||||
modelOrientation = modelOrientation.WithPitch(new WAngle(pitch));
|
||||
};
|
||||
|
||||
pitchSlider.GetValue = () => modelOrientation.Pitch.Angle;
|
||||
}
|
||||
|
||||
var yawSlider = panel.GetOrNull<SliderWidget>("YAW_SLIDER");
|
||||
if (yawSlider != null)
|
||||
{
|
||||
yawSlider.OnChange += x =>
|
||||
{
|
||||
var yaw = (int)x;
|
||||
modelOrientation = modelOrientation.WithYaw(new WAngle(yaw));
|
||||
};
|
||||
|
||||
yawSlider.GetValue = () => modelOrientation.Yaw.Angle;
|
||||
}
|
||||
|
||||
var assetBrowserModData = modData.Manifest.Get<AssetBrowser>();
|
||||
allowedExtensions = assetBrowserModData.SupportedExtensions;
|
||||
|
||||
@@ -342,12 +395,24 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
return true;
|
||||
}
|
||||
|
||||
currentSprites = world.Map.Rules.Sequences.SpriteCache[prefix + filename];
|
||||
currentFrame = 0;
|
||||
if (frameSlider != null)
|
||||
if (Path.GetExtension(filename.ToLowerInvariant()) == ".vxl")
|
||||
{
|
||||
frameSlider.MaximumValue = (float)currentSprites.Length - 1;
|
||||
frameSlider.Ticks = currentSprites.Length;
|
||||
var voxelName = Path.GetFileNameWithoutExtension(filename);
|
||||
currentVoxel = world.ModelCache.GetModel(voxelName);
|
||||
currentSprites = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentSprites = world.Map.Rules.Sequences.SpriteCache[prefix + filename];
|
||||
currentFrame = 0;
|
||||
|
||||
if (frameSlider != null)
|
||||
{
|
||||
frameSlider.MaximumValue = (float)currentSprites.Length - 1;
|
||||
frameSlider.Ticks = currentSprites.Length;
|
||||
}
|
||||
|
||||
currentVoxel = null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -101,7 +101,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
if (player == null || player.RelationshipWith(pp) == PlayerRelationship.Ally || player.WinState != WinState.Undefined)
|
||||
{
|
||||
flag.GetImageName = () => pp.Faction.InternalName;
|
||||
item.Get<LabelWidget>("FACTION").GetText = () => pp.Faction.Name;
|
||||
var factionName = pp.Faction.Name != pp.DisplayFaction.Name ? "{0} ({1})".F(pp.DisplayFaction.Name, pp.Faction.Name) : pp.Faction.Name;
|
||||
item.Get<LabelWidget>("FACTION").GetText = () => factionName;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -247,13 +247,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
BindIntSliderPref(panel, "FRAME_LIMIT_SLIDER", ds, "MaxFramerate");
|
||||
BindCheckboxPref(panel, "PLAYER_STANCE_COLORS_CHECKBOX", gs, "UsePlayerStanceColors");
|
||||
|
||||
var languageDropDownButton = panel.GetOrNull<DropDownButtonWidget>("LANGUAGE_DROPDOWNBUTTON");
|
||||
if (languageDropDownButton != null)
|
||||
{
|
||||
languageDropDownButton.OnMouseDown = _ => ShowLanguageDropdown(languageDropDownButton, modData.Languages);
|
||||
languageDropDownButton.GetText = () => FieldLoader.Translate(ds.Language);
|
||||
}
|
||||
|
||||
var windowModeDropdown = panel.Get<DropDownButtonWidget>("MODE_DROPDOWN");
|
||||
windowModeDropdown.OnMouseDown = _ => ShowWindowModeDropdown(windowModeDropdown, ds);
|
||||
windowModeDropdown.GetText = () => ds.Mode == WindowMode.Windowed ?
|
||||
@@ -388,7 +381,6 @@ 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;
|
||||
@@ -828,21 +820,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options.Keys, setupItem);
|
||||
}
|
||||
|
||||
static void ShowLanguageDropdown(DropDownButtonWidget dropdown, IEnumerable<string> languages)
|
||||
{
|
||||
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (o, itemTemplate) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(itemTemplate,
|
||||
() => Game.Settings.Graphics.Language == o,
|
||||
() => Game.Settings.Graphics.Language = o);
|
||||
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => FieldLoader.Translate(o);
|
||||
return item;
|
||||
};
|
||||
|
||||
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, languages, setupItem);
|
||||
}
|
||||
|
||||
static void ShowStatusBarsDropdown(DropDownButtonWidget dropdown, GameSettings s)
|
||||
{
|
||||
var options = new Dictionary<string, StatusBarsType>()
|
||||
|
||||
217
OpenRA.Mods.Common/Widgets/ModelWidget.cs
Normal file
217
OpenRA.Mods.Common/Widgets/ModelWidget.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Graphics;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class ModelWidget : Widget
|
||||
{
|
||||
public string Palette = "terrain";
|
||||
public string PlayerPalette = "player";
|
||||
public string NormalsPalette = "normals";
|
||||
public string ShadowPalette = "shadow";
|
||||
public float Scale = 12f;
|
||||
public int LightPitch = 142;
|
||||
public int LightYaw = 682;
|
||||
public float[] LightAmbientColor = new float[] { 0.6f, 0.6f, 0.6f };
|
||||
public float[] LightDiffuseColor = new float[] { 0.4f, 0.4f, 0.4f };
|
||||
public WRot Rotation = WRot.None;
|
||||
public WAngle CameraAngle = WAngle.FromDegrees(40);
|
||||
|
||||
public Func<string> GetPalette;
|
||||
public Func<string> GetPlayerPalette;
|
||||
public Func<string> GetNormalsPalette;
|
||||
public Func<string> GetShadowPalette;
|
||||
public Func<float[]> GetLightAmbientColor;
|
||||
public Func<float[]> GetLightDiffuseColor;
|
||||
public Func<float> GetScale;
|
||||
public Func<int> GetLightPitch;
|
||||
public Func<int> GetLightYaw;
|
||||
public Func<IModel> GetVoxel;
|
||||
public Func<WRot> GetRotation;
|
||||
public Func<WAngle> GetCameraAngle;
|
||||
public int2 IdealPreviewSize { get; private set; }
|
||||
|
||||
protected readonly WorldRenderer WorldRenderer;
|
||||
|
||||
IFinalizedRenderable renderable;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public ModelWidget(WorldRenderer worldRenderer)
|
||||
{
|
||||
GetPalette = () => Palette;
|
||||
GetPlayerPalette = () => PlayerPalette;
|
||||
GetNormalsPalette = () => NormalsPalette;
|
||||
GetShadowPalette = () => ShadowPalette;
|
||||
GetLightAmbientColor = () => LightAmbientColor;
|
||||
GetLightDiffuseColor = () => LightDiffuseColor;
|
||||
GetScale = () => Scale;
|
||||
GetRotation = () => Rotation;
|
||||
GetLightPitch = () => LightPitch;
|
||||
GetLightYaw = () => LightYaw;
|
||||
GetCameraAngle = () => CameraAngle;
|
||||
WorldRenderer = worldRenderer;
|
||||
}
|
||||
|
||||
protected ModelWidget(ModelWidget other)
|
||||
: base(other)
|
||||
{
|
||||
Palette = other.Palette;
|
||||
GetPalette = other.GetPalette;
|
||||
GetVoxel = other.GetVoxel;
|
||||
|
||||
WorldRenderer = other.WorldRenderer;
|
||||
}
|
||||
|
||||
public override Widget Clone()
|
||||
{
|
||||
return new ModelWidget(this);
|
||||
}
|
||||
|
||||
IModel cachedVoxel;
|
||||
string cachedPalette;
|
||||
string cachedPlayerPalette;
|
||||
string cachedNormalsPalette;
|
||||
string cachedShadowPalette;
|
||||
float cachedScale;
|
||||
WRot cachedRotation;
|
||||
float[] cachedLightAmbientColor = new float[] { 0, 0, 0 };
|
||||
float[] cachedLightDiffuseColor = new float[] { 0, 0, 0 };
|
||||
int cachedLightPitch;
|
||||
int cachedLightYaw;
|
||||
WAngle cachedCameraAngle;
|
||||
PaletteReference paletteReference;
|
||||
PaletteReference paletteReferencePlayer;
|
||||
PaletteReference paletteReferenceNormals;
|
||||
PaletteReference paletteReferenceShadow;
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
if (renderable == null)
|
||||
return;
|
||||
|
||||
renderable.Render(WorldRenderer);
|
||||
}
|
||||
|
||||
public override void PrepareRenderables()
|
||||
{
|
||||
var voxel = GetVoxel();
|
||||
var palette = GetPalette();
|
||||
var playerPalette = GetPlayerPalette();
|
||||
var normalsPalette = GetNormalsPalette();
|
||||
var shadowPalette = GetShadowPalette();
|
||||
var scale = GetScale();
|
||||
var rotation = GetRotation();
|
||||
var lightAmbientColor = GetLightAmbientColor();
|
||||
var lightDiffuseColor = GetLightDiffuseColor();
|
||||
var lightPitch = GetLightPitch();
|
||||
var lightYaw = GetLightYaw();
|
||||
var cameraAngle = GetCameraAngle();
|
||||
|
||||
if (voxel == null || palette == null)
|
||||
return;
|
||||
|
||||
if (voxel != cachedVoxel)
|
||||
cachedVoxel = voxel;
|
||||
|
||||
if (palette != cachedPalette)
|
||||
{
|
||||
if (string.IsNullOrEmpty(palette) && string.IsNullOrEmpty(playerPalette))
|
||||
return;
|
||||
|
||||
var paletteName = string.IsNullOrEmpty(palette) ? playerPalette : palette;
|
||||
paletteReference = WorldRenderer.Palette(paletteName);
|
||||
cachedPalette = paletteName;
|
||||
}
|
||||
|
||||
if (playerPalette != cachedPlayerPalette)
|
||||
{
|
||||
paletteReferencePlayer = WorldRenderer.Palette(playerPalette);
|
||||
cachedPlayerPalette = playerPalette;
|
||||
}
|
||||
|
||||
if (normalsPalette != cachedNormalsPalette)
|
||||
{
|
||||
paletteReferenceNormals = WorldRenderer.Palette(normalsPalette);
|
||||
cachedNormalsPalette = normalsPalette;
|
||||
}
|
||||
|
||||
if (shadowPalette != cachedShadowPalette)
|
||||
{
|
||||
paletteReferenceShadow = WorldRenderer.Palette(shadowPalette);
|
||||
cachedShadowPalette = shadowPalette;
|
||||
}
|
||||
|
||||
if (scale != cachedScale)
|
||||
cachedScale = scale;
|
||||
|
||||
if (rotation != cachedRotation)
|
||||
cachedRotation = rotation;
|
||||
|
||||
if (lightPitch != cachedLightPitch)
|
||||
cachedLightPitch = lightPitch;
|
||||
|
||||
if (lightYaw != cachedLightYaw)
|
||||
cachedLightYaw = lightYaw;
|
||||
|
||||
if (cachedLightAmbientColor[0] != lightAmbientColor[0] || cachedLightAmbientColor[1] != lightAmbientColor[1] || cachedLightAmbientColor[2] != lightAmbientColor[2])
|
||||
cachedLightAmbientColor = lightAmbientColor;
|
||||
|
||||
if (cachedLightDiffuseColor[0] != lightDiffuseColor[0] || cachedLightDiffuseColor[1] != lightDiffuseColor[1] || cachedLightDiffuseColor[2] != lightDiffuseColor[2])
|
||||
cachedLightDiffuseColor = lightDiffuseColor;
|
||||
|
||||
if (cameraAngle != cachedCameraAngle)
|
||||
cachedCameraAngle = cameraAngle;
|
||||
|
||||
if (cachedVoxel == null)
|
||||
return;
|
||||
|
||||
var animation = new ModelAnimation(
|
||||
cachedVoxel,
|
||||
() => WVec.Zero,
|
||||
() => cachedRotation,
|
||||
() => false,
|
||||
() => 0,
|
||||
true);
|
||||
|
||||
var animations = new ModelAnimation[] { animation };
|
||||
|
||||
ModelPreview preview = new ModelPreview(
|
||||
new ModelAnimation[] { animation }, WVec.Zero, 0,
|
||||
cachedScale,
|
||||
new WAngle(cachedLightPitch),
|
||||
new WAngle(cachedLightYaw),
|
||||
cachedLightAmbientColor,
|
||||
cachedLightDiffuseColor,
|
||||
cachedCameraAngle,
|
||||
paletteReference,
|
||||
paletteReferenceNormals,
|
||||
paletteReferenceShadow);
|
||||
|
||||
var screenBounds = animation.ScreenBounds(WPos.Zero, WorldRenderer, scale);
|
||||
IdealPreviewSize = new int2(screenBounds.Width, screenBounds.Height);
|
||||
var origin = RenderOrigin + new int2(RenderBounds.Size.Width / 2, RenderBounds.Size.Height / 2);
|
||||
|
||||
var camera = new WRot(WAngle.Zero, cachedCameraAngle - new WAngle(256), new WAngle(256));
|
||||
var modelRenderable = new UIModelRenderable(
|
||||
animations, WPos.Zero, origin, 0, camera, scale,
|
||||
WRot.None, cachedLightAmbientColor, cachedLightDiffuseColor,
|
||||
paletteReferencePlayer, paletteReferenceNormals, paletteReferenceShadow);
|
||||
|
||||
renderable = modelRenderable.PrepareRender(WorldRenderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,13 +70,10 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
public readonly bool DrawTime = true;
|
||||
|
||||
[Translate]
|
||||
public readonly string ReadyText = "";
|
||||
|
||||
[Translate]
|
||||
public readonly string HoldText = "";
|
||||
|
||||
[Translate]
|
||||
public readonly string InfiniteSymbol = "\u221E";
|
||||
|
||||
public int DisplayedIconCount { get; private set; }
|
||||
|
||||
@@ -22,10 +22,8 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class SupportPowersWidget : Widget
|
||||
{
|
||||
[Translate]
|
||||
public readonly string ReadyText = "";
|
||||
|
||||
[Translate]
|
||||
public readonly string HoldText = "";
|
||||
|
||||
public readonly string OverlayFont = "TinyBold";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Optimize>true</Optimize>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
@@ -11,6 +11,7 @@
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
|
||||
<IsPublishable Condition="'$(CopyD2kDll)' == 'False'">false</IsPublishable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- Work around an issue where Rider does not detect files in the project root using the default glob -->
|
||||
@@ -37,4 +38,4 @@
|
||||
<Analyzer Remove="@(Analyzer)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace OpenRA.Mods.D2k.SpriteLoaders
|
||||
{
|
||||
class R8Frame : ISpriteFrame
|
||||
{
|
||||
public SpriteFrameType Type { get; set; }
|
||||
public SpriteFrameType Type { get { return SpriteFrameType.Indexed8; } }
|
||||
public Size Size { get; private set; }
|
||||
public Size FrameSize { get; private set; }
|
||||
public float2 Offset { get; private set; }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Optimize>true</Optimize>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
@@ -11,6 +11,7 @@
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetPlatform Condition="$([MSBuild]::IsOsPlatform('Windows'))">win-x64</TargetPlatform>
|
||||
|
||||
@@ -59,7 +59,6 @@ namespace OpenRA.Platforms.Default
|
||||
Name = "ThreadedGraphicsContext RenderThread",
|
||||
IsBackground = true
|
||||
};
|
||||
renderThread.SetApartmentState(ApartmentState.STA);
|
||||
lock (syncObject)
|
||||
{
|
||||
// Start and wait for the rendering thread to have initialized before returning.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<TargetFramework Condition="'$(Mono)' == ''">net5.0</TargetFramework>
|
||||
<TargetFramework Condition="'$(Mono)' != ''">netstandard2.1</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Optimize>true</Optimize>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
@@ -39,4 +40,9 @@
|
||||
<Analyzer Remove="@(Analyzer)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<TrimmerRootAssembly Include="mscorlib" />
|
||||
<TrimmerRootAssembly Include="netstandard" />
|
||||
<TrimmerRootAssembly Include="System.IO.Pipes" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@@ -10,6 +10,7 @@
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
|
||||
<IsPublishable>false</IsPublishable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- Work around an issue where Rider does not detect files in the project root using the default glob -->
|
||||
@@ -22,14 +23,12 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj" />
|
||||
<ProjectReference Include="..\OpenRA.Mods.Common\OpenRA.Mods.Common.csproj">
|
||||
<Private>False</Private>
|
||||
<Private>True</Private>
|
||||
</ProjectReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="NUnit.Console" Version="3.11.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0-beta.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
|
||||
<AdditionalFiles Include="../stylecop.json" />
|
||||
@@ -40,4 +39,4 @@
|
||||
<Analyzer Remove="@(Analyzer)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<TargetFramework Condition="'$(Mono)' == ''">net5.0</TargetFramework>
|
||||
<TargetFramework Condition="'$(Mono)' != ''">netstandard2.1</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Optimize>true</Optimize>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
@@ -39,4 +40,9 @@
|
||||
<Analyzer Remove="@(Analyzer)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<TrimmerRootAssembly Include="mscorlib" />
|
||||
<TrimmerRootAssembly Include="netstandard" />
|
||||
<TrimmerRootAssembly Include="System.IO.Pipes" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>winexe</OutputType>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<ApplicationIcon>$(LauncherIcon)</ApplicationIcon>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Optimize>true</Optimize>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
@@ -10,14 +12,12 @@
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
|
||||
<OutputPath>../bin</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ExternalConsole>false</ExternalConsole>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
|
||||
<Configurations>Release;Debug</Configurations>
|
||||
<AssemblyName>$(LauncherName)</AssemblyName>
|
||||
<ApplicationIcon>$(LauncherIcon)</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- Work around an issue where Rider does not detect files in the project root using the default glob -->
|
||||
@@ -58,4 +58,9 @@
|
||||
<Analyzer Remove="@(Analyzer)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<TrimmerRootAssembly Include="mscorlib" />
|
||||
<TrimmerRootAssembly Include="netstandard" />
|
||||
<TrimmerRootAssembly Include="System.IO.Pipes" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -58,9 +58,7 @@ Global
|
||||
{FE6C8CC0-2F07-442A-B29F-17617B3B7FC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FE6C8CC0-2F07-442A-B29F-17617B3B7FC6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6CB8E1B7-6B36-4D93-8633-7C573E194AC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6CB8E1B7-6B36-4D93-8633-7C573E194AC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6CB8E1B7-6B36-4D93-8633-7C573E194AC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6CB8E1B7-6B36-4D93-8633-7C573E194AC4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{54DAE0E0-3125-49D3-992E-A0E931EB5FC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{54DAE0E0-3125-49D3-992E-A0E931EB5FC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{54DAE0E0-3125-49D3-992E-A0E931EB5FC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
||||
@@ -25,7 +25,7 @@ ShareAnonymizedIPs="${ShareAnonymizedIPs:-"True"}"
|
||||
SupportDir="${SupportDir:-""}"
|
||||
|
||||
while true; do
|
||||
mono --debug bin/OpenRA.Server.exe Engine.EngineDir=".." Game.Mod="$Mod" \
|
||||
mono --debug bin/OpenRA.Server.dll Engine.EngineDir=".." Game.Mod="$Mod" \
|
||||
Server.Name="$Name" \
|
||||
Server.ListenPort="$ListenPort" \
|
||||
Server.AdvertiseOnline="$AdvertiseOnline" \
|
||||
|
||||
@@ -25,7 +25,7 @@ then
|
||||
fi
|
||||
|
||||
# Launch the engine with the appropriate arguments
|
||||
mono --debug bin/OpenRA.exe Engine.EngineDir=".." Engine.LaunchPath="$MODLAUNCHER" $MODARG "$@"
|
||||
mono --debug bin/OpenRA.dll Engine.EngineDir=".." Engine.LaunchPath="$MODLAUNCHER" $MODARG "$@"
|
||||
|
||||
# Show a crash dialog if something went wrong
|
||||
if [ $? != 0 ] && [ $? != 1 ]; then
|
||||
|
||||
30
make.ps1
30
make.ps1
@@ -10,7 +10,7 @@ function All-Command
|
||||
return
|
||||
}
|
||||
|
||||
dotnet build /p:Configuration=Release /nologo
|
||||
dotnet build -c Release --nologo -p:TargetPlatform=win-x64
|
||||
if ($lastexitcode -ne 0)
|
||||
{
|
||||
Write-Host "Build failed. If just the development tools failed to build, try installing Visual Studio. You may also still be able to run the game." -ForegroundColor Red
|
||||
@@ -99,19 +99,19 @@ function Test-Command
|
||||
|
||||
Write-Host "Testing mods..." -ForegroundColor Cyan
|
||||
Write-Host "Testing Tiberian Sun mod MiniYAML..." -ForegroundColor Cyan
|
||||
Invoke-Expression "$utilityPath ts --check-yaml"
|
||||
InvokeCommand "$utilityPath ts --check-yaml"
|
||||
Write-Host "Testing Dune 2000 mod MiniYAML..." -ForegroundColor Cyan
|
||||
Invoke-Expression "$utilityPath d2k --check-yaml"
|
||||
InvokeCommand "$utilityPath d2k --check-yaml"
|
||||
Write-Host "Testing Tiberian Dawn mod MiniYAML..." -ForegroundColor Cyan
|
||||
Invoke-Expression "$utilityPath cnc --check-yaml"
|
||||
InvokeCommand "$utilityPath cnc --check-yaml"
|
||||
Write-Host "Testing Red Alert mod MiniYAML..." -ForegroundColor Cyan
|
||||
Invoke-Expression "$utilityPath ra --check-yaml"
|
||||
InvokeCommand "$utilityPath ra --check-yaml"
|
||||
}
|
||||
|
||||
function Check-Command
|
||||
{
|
||||
Write-Host "Compiling in debug configuration..." -ForegroundColor Cyan
|
||||
dotnet build /p:Configuration=Debug /nologo
|
||||
dotnet build -c Debug --nologo -p:TargetPlatform=win-x64
|
||||
if ($lastexitcode -ne 0)
|
||||
{
|
||||
Write-Host "Build failed." -ForegroundColor Red
|
||||
@@ -120,10 +120,10 @@ function Check-Command
|
||||
if ((CheckForUtility) -eq 0)
|
||||
{
|
||||
Write-Host "Checking for explicit interface violations..." -ForegroundColor Cyan
|
||||
Invoke-Expression "$utilityPath all --check-explicit-interfaces"
|
||||
InvokeCommand "$utilityPath all --check-explicit-interfaces"
|
||||
|
||||
Write-Host "Checking for incorrect conditional trait interface overrides..." -ForegroundColor Cyan
|
||||
Invoke-Expression "$utilityPath all --check-conditional-trait-interface-overrides"
|
||||
InvokeCommand "$utilityPath all --check-conditional-trait-interface-overrides"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +201,20 @@ function WaitForInput
|
||||
}
|
||||
}
|
||||
|
||||
function InvokeCommand
|
||||
{
|
||||
param($expression)
|
||||
# $? is the return value of the called expression
|
||||
# Invoke-Expression itself will always succeed, even if the invoked expression fails
|
||||
# So temporarily store the return value in $success
|
||||
$expression += '; $success = $?'
|
||||
Invoke-Expression $expression
|
||||
if ($success -eq $False)
|
||||
{
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
###############################################################
|
||||
############################ Main #############################
|
||||
###############################################################
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
english:
|
||||
english: English
|
||||
|
||||
@@ -27,8 +27,8 @@ Actors:
|
||||
|
||||
Rules:
|
||||
World:
|
||||
-SpawnMPUnits:
|
||||
-MPStartLocations:
|
||||
-SpawnStartingUnits:
|
||||
-MapStartingLocations:
|
||||
-CrateSpawner:
|
||||
MusicPlaylist:
|
||||
BackgroundMusic: map1
|
||||
|
||||
@@ -7,8 +7,8 @@ World:
|
||||
SpawnInterval: 125
|
||||
CrateActors: unitcrate
|
||||
InitialSpawnDelay: 0
|
||||
-SpawnMPUnits:
|
||||
-MPStartLocations:
|
||||
-SpawnStartingUnits:
|
||||
-MapStartingLocations:
|
||||
MapBuildRadius:
|
||||
AllyBuildRadiusCheckboxLocked: True
|
||||
AllyBuildRadiusCheckboxEnabled: False
|
||||
|
||||
@@ -140,9 +140,6 @@ Notifications:
|
||||
Music:
|
||||
cnc|audio/music.yaml
|
||||
|
||||
Translations:
|
||||
cnc|languages/english.yaml
|
||||
|
||||
Hotkeys:
|
||||
common|hotkeys/game.yaml
|
||||
common|hotkeys/observer.yaml
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
World:
|
||||
-SpawnMPUnits:
|
||||
-MPStartLocations:
|
||||
-SpawnStartingUnits:
|
||||
-MapStartingLocations:
|
||||
-CrateSpawner:
|
||||
ObjectivesPanel:
|
||||
PanelName: MISSION_OBJECTIVES
|
||||
|
||||
@@ -668,8 +668,8 @@
|
||||
ActorPreviewPlaceBuildingPreview:
|
||||
OverridePalette: placebuilding
|
||||
SoundOnDamageTransition:
|
||||
DamagedSounds: xplos.aud
|
||||
DestroyedSounds: crumble.aud
|
||||
DamagedSounds: xplobig4.aud
|
||||
DestroyedSounds: crumble.aud, xplobig4.aud
|
||||
WithSpriteBody:
|
||||
Explodes:
|
||||
Type: Footprint
|
||||
|
||||
@@ -193,51 +193,51 @@ World:
|
||||
ShortGameCheckboxDisplayOrder: 2
|
||||
TechLevelDropdownDisplayOrder: 2
|
||||
GameSpeedDropdownDisplayOrder: 3
|
||||
MPStartLocations:
|
||||
MapStartingLocations:
|
||||
SeparateTeamSpawnsCheckboxDisplayOrder: 6
|
||||
CreateMPPlayers:
|
||||
MPStartUnits@mcvonly:
|
||||
CreateMapPlayers:
|
||||
StartingUnits@mcvonly:
|
||||
Class: none
|
||||
ClassName: MCV Only
|
||||
Factions: gdi, nod
|
||||
BaseActor: mcv
|
||||
MPStartUnits@defaultgdia:
|
||||
StartingUnits@defaultgdia:
|
||||
Class: light
|
||||
ClassName: Light Support
|
||||
Factions: gdi
|
||||
BaseActor: mcv
|
||||
SupportActors: e1,e1,e1,e1,e1,e3,e3,jeep
|
||||
MPStartUnits@defaultnoda:
|
||||
StartingUnits@defaultnoda:
|
||||
Class: light
|
||||
ClassName: Light Support
|
||||
Factions: nod
|
||||
BaseActor: mcv
|
||||
SupportActors: e1,e1,e1,e1,e1,e1,e3,e3,bggy
|
||||
MPStartUnits@heavynoda:
|
||||
StartingUnits@heavynoda:
|
||||
Class: heavy
|
||||
ClassName: Heavy Support
|
||||
Factions: nod
|
||||
BaseActor: mcv
|
||||
SupportActors: e1,e1,e1,e1,e3,e3,ltnk,ltnk,ftnk
|
||||
MPStartUnits@heavynodb:
|
||||
StartingUnits@heavynodb:
|
||||
Class: heavy
|
||||
ClassName: Heavy Support
|
||||
Factions: nod
|
||||
BaseActor: mcv
|
||||
SupportActors: e1,e1,e1,e1,e1,e3,e3,e3,ftnk,ftnk
|
||||
MPStartUnits@heavygdia:
|
||||
StartingUnits@heavygdia:
|
||||
Class: heavy
|
||||
ClassName: Heavy Support
|
||||
Factions: gdi
|
||||
BaseActor: mcv
|
||||
SupportActors: e1,e1,e1,e1,e3,e3,jeep,mtnk,mtnk
|
||||
MPStartUnits@heavygdib:
|
||||
StartingUnits@heavygdib:
|
||||
Class: heavy
|
||||
ClassName: Heavy Support
|
||||
Factions: gdi
|
||||
BaseActor: mcv
|
||||
SupportActors: e1,e1,e1,e1,e1,e2,e2,e2,e3,e3,apc,mtnk
|
||||
SpawnMPUnits:
|
||||
SpawnStartingUnits:
|
||||
DropdownDisplayOrder: 0
|
||||
CrateSpawner:
|
||||
Minimum: 1
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user