Compare commits
77 Commits
prep-2307
...
devtest-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
253e02ae08 | ||
|
|
f707b9c975 | ||
|
|
8629e578e3 | ||
|
|
c65cfd3528 | ||
|
|
7ad6cfda40 | ||
|
|
393fd585b1 | ||
|
|
ea221f9bfb | ||
|
|
b4ca81d1bd | ||
|
|
49590bb5f7 | ||
|
|
2d059c0d95 | ||
|
|
d5dd6a9086 | ||
|
|
a7ca0f0699 | ||
|
|
4b02d9b700 | ||
|
|
ebf55c8d86 | ||
|
|
f928922f3a | ||
|
|
bc3743d9ff | ||
|
|
8aa6600655 | ||
|
|
215b3d8ab3 | ||
|
|
a397670dce | ||
|
|
43d7f9c6df | ||
|
|
6904ea8c6b | ||
|
|
d702d58d1a | ||
|
|
e0b0c5624f | ||
|
|
33773f895c | ||
|
|
9f759f3cc2 | ||
|
|
9fb4302b2f | ||
|
|
4685c1a6b1 | ||
|
|
70bc5b097d | ||
|
|
7691507baf | ||
|
|
8d6cebe654 | ||
|
|
daf10c34a6 | ||
|
|
d96ec21b95 | ||
|
|
aea1182bb5 | ||
|
|
b413b97a52 | ||
|
|
7daa27f123 | ||
|
|
f3f98d8750 | ||
|
|
69eeb2dc84 | ||
|
|
7cee29ff70 | ||
|
|
5e80fee913 | ||
|
|
c1474204e2 | ||
|
|
223f9408fd | ||
|
|
674ca8555b | ||
|
|
275325b0cf | ||
|
|
06e399d3ce | ||
|
|
d6d77eab7c | ||
|
|
2392566c1d | ||
|
|
028467f150 | ||
|
|
eee4b04248 | ||
|
|
f551b542f3 | ||
|
|
5d5419702b | ||
|
|
47e89b7d76 | ||
|
|
9bb0409f19 | ||
|
|
80ac494948 | ||
|
|
eb82f2b975 | ||
|
|
a5c8db82da | ||
|
|
2a26ff6818 | ||
|
|
5a11d54956 | ||
|
|
26c5a8b76c | ||
|
|
efcfab78dc | ||
|
|
ac351f6e65 | ||
|
|
42bdc9e53e | ||
|
|
3097efc5b6 | ||
|
|
853422409f | ||
|
|
f097250394 | ||
|
|
96e0f96c12 | ||
|
|
0a4c4162be | ||
|
|
10ff24f24e | ||
|
|
aeb96d98f3 | ||
|
|
cf4bfdcdfb | ||
|
|
d55af8d75d | ||
|
|
dd9e75d017 | ||
|
|
1b6220962e | ||
|
|
f70f2acb39 | ||
|
|
32e2507bff | ||
|
|
22a42b7dc9 | ||
|
|
c01e4043e8 | ||
|
|
c8665c98a6 |
@@ -126,6 +126,9 @@ dotnet_style_predefined_type_for_locals_parameters_members = true
|
||||
# Show an IDE warning when default access modifiers are explicitly specified.
|
||||
dotnet_style_require_accessibility_modifiers = omit_if_default:warning
|
||||
|
||||
# use 'var' instead of explicit type
|
||||
dotnet_diagnostic.IDE0007.severity = warning
|
||||
|
||||
# Don't prefer braces (for one liners).
|
||||
dotnet_diagnostic.IDE0011.severity = silent
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ name: Continuous Integration
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
branches: [ bleed ]
|
||||
branches: [ bleed, 'prep-*' ]
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
19
.github/workflows/documentation.yml
vendored
19
.github/workflows/documentation.yml
vendored
@@ -79,19 +79,29 @@ jobs:
|
||||
run: |
|
||||
make all
|
||||
|
||||
- name: Clone docs.openra.net
|
||||
- name: Clone docs.openra.net (Playtest)
|
||||
if: startsWith(github.event.inputs.tag, 'playtest-')
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: openra/docs
|
||||
token: ${{ secrets.DOCS_TOKEN }}
|
||||
path: docs
|
||||
ref: playtest
|
||||
|
||||
- name: Clone docs.openra.net (Release)
|
||||
if: startsWith(github.event.inputs.tag, 'release-')
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: openra/docs
|
||||
token: ${{ secrets.DOCS_TOKEN }}
|
||||
path: docs
|
||||
ref: release
|
||||
|
||||
- name: Update docs.openra.net (Playtest)
|
||||
if: startsWith(github.event.inputs.tag, 'playtest-')
|
||||
env:
|
||||
GIT_TAG: ${{ github.event.inputs.tag }}
|
||||
run: |
|
||||
git checkout playtest
|
||||
./utility.sh all --docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/traits.md"
|
||||
./utility.sh all --weapon-docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/weapons.md"
|
||||
./utility.sh all --sprite-sequence-docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/sprite-sequences.md"
|
||||
@@ -102,7 +112,6 @@ jobs:
|
||||
env:
|
||||
GIT_TAG: ${{ github.event.inputs.tag }}
|
||||
run: |
|
||||
git checkout release
|
||||
./utility.sh all --docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/traits.md"
|
||||
./utility.sh all --weapon-docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/weapons.md"
|
||||
./utility.sh all --sprite-sequence-docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/sprite-sequences.md"
|
||||
@@ -115,15 +124,17 @@ jobs:
|
||||
cd docs
|
||||
git config --local user.email "actions@github.com"
|
||||
git config --local user.name "GitHub Actions"
|
||||
git add *.md
|
||||
git add api/*.md
|
||||
git commit -m "Update auto-generated documentation for ${GIT_TAG}"
|
||||
|
||||
- name: Push docs.openra.net (Release)
|
||||
if: startsWith(github.event.inputs.tag, 'release-')
|
||||
run: |
|
||||
cd docs
|
||||
git push origin release
|
||||
|
||||
- name: Push docs.openra.net (Playtest)
|
||||
if: startsWith(github.event.inputs.tag, 'playtest-')
|
||||
run: |
|
||||
cd docs
|
||||
git push origin playtest
|
||||
|
||||
6
.github/workflows/packaging.yml
vendored
6
.github/workflows/packaging.yml
vendored
@@ -62,7 +62,7 @@ jobs:
|
||||
file: build/linux/*
|
||||
|
||||
macos:
|
||||
name: macOS Disk Images
|
||||
name: macOS Disk Image
|
||||
runs-on: macos-11
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
- name: Prepare Environment
|
||||
run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
|
||||
|
||||
- name: Package Disk Images
|
||||
- name: Package Disk Image
|
||||
env:
|
||||
MACOS_DEVELOPER_IDENTITY: ${{ secrets.MACOS_DEVELOPER_IDENTITY }}
|
||||
MACOS_DEVELOPER_CERTIFICATE_BASE64: ${{ secrets.MACOS_DEVELOPER_CERTIFICATE_BASE64 }}
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
mkdir -p build/macos
|
||||
./packaging/macos/buildpackage.sh "${GIT_TAG}" "${PWD}/build/macos"
|
||||
|
||||
- name: Upload Packages
|
||||
- name: Upload Package
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
10
Makefile
10
Makefile
@@ -58,6 +58,8 @@ RM_RF = $(RM) -rf
|
||||
|
||||
RUNTIME ?= net6
|
||||
CONFIGURATION ?= Release
|
||||
DOTNET_RID = $(shell ${DOTNET} --info | grep RID: | cut -w -f3)
|
||||
ARCH_X64 = $(shell echo ${DOTNET_RID} | grep x64)
|
||||
|
||||
# Only for use in target version:
|
||||
VERSION := $(shell git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || (c=$$(git rev-parse --short HEAD 2>/dev/null) && echo git-$$c))
|
||||
@@ -67,15 +69,23 @@ ifndef TARGETPLATFORM
|
||||
UNAME_S := $(shell uname -s)
|
||||
UNAME_M := $(shell uname -m)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
ifeq ($(ARCH_X64),)
|
||||
TARGETPLATFORM = osx-arm64
|
||||
else
|
||||
TARGETPLATFORM = osx-x64
|
||||
endif
|
||||
else
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
TARGETPLATFORM = linux-x64
|
||||
else
|
||||
ifeq ($(UNAME_M),aarch64)
|
||||
TARGETPLATFORM = linux-arm64
|
||||
else
|
||||
TARGETPLATFORM = unix-generic
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
##################### DEVELOPMENT BUILDS AND TESTS #####################
|
||||
#
|
||||
|
||||
@@ -55,7 +55,8 @@ namespace OpenRA.FileSystem
|
||||
get
|
||||
{
|
||||
foreach (ZipEntry entry in pkg)
|
||||
yield return entry.Name;
|
||||
if (entry.IsFile)
|
||||
yield return entry.Name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -318,7 +318,7 @@ namespace OpenRA
|
||||
if (!string.IsNullOrEmpty(supportDirArg))
|
||||
Platform.OverrideSupportDir(supportDirArg);
|
||||
|
||||
Console.WriteLine($"Platform is {Platform.CurrentPlatform}");
|
||||
Console.WriteLine($"Platform is {Platform.CurrentPlatform} ({Platform.CurrentArchitecture})");
|
||||
|
||||
// Load the engine version as early as possible so it can be written to exception logs
|
||||
try
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public ImmutablePalette(IPalette p)
|
||||
{
|
||||
for (int i = 0; i < Palette.Size; i++)
|
||||
for (var i = 0; i < Palette.Size; i++)
|
||||
colors[i] = p[i];
|
||||
}
|
||||
|
||||
|
||||
@@ -243,6 +243,8 @@ namespace OpenRA
|
||||
public CellRegion AllCells { get; private set; }
|
||||
public List<CPos> AllEdgeCells { get; private set; }
|
||||
|
||||
public event Action<CPos> CellProjectionChanged;
|
||||
|
||||
// Internal data
|
||||
readonly ModData modData;
|
||||
CellLayer<short> cachedTerrainIndexes;
|
||||
@@ -336,11 +338,12 @@ namespace OpenRA
|
||||
Height = new CellLayer<byte>(Grid.Type, size);
|
||||
Ramp = new CellLayer<byte>(Grid.Type, size);
|
||||
Tiles.Clear(terrainInfo.DefaultTerrainTile);
|
||||
|
||||
if (Grid.MaximumTerrainHeight > 0)
|
||||
{
|
||||
Height.CellEntryChanged += UpdateProjection;
|
||||
Tiles.CellEntryChanged += UpdateProjection;
|
||||
Tiles.CellEntryChanged += UpdateRamp;
|
||||
Tiles.CellEntryChanged += UpdateProjection;
|
||||
Height.CellEntryChanged += UpdateProjection;
|
||||
}
|
||||
|
||||
PostInit();
|
||||
@@ -530,6 +533,7 @@ namespace OpenRA
|
||||
var inverse = inverseCellProjection[uv];
|
||||
inverse.Clear();
|
||||
inverse.Add(uv);
|
||||
CellProjectionChanged?.Invoke(cell);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -567,6 +571,8 @@ namespace OpenRA
|
||||
projectedHeight[temp] = height;
|
||||
}
|
||||
}
|
||||
|
||||
CellProjectionChanged?.Invoke(cell);
|
||||
}
|
||||
|
||||
byte ProjectedCellHeightInner(PPos puv)
|
||||
|
||||
@@ -344,7 +344,7 @@ namespace OpenRA
|
||||
if (yaml.TryGetValue("Visibility", out temp))
|
||||
newData.Visibility = FieldLoader.GetValue<MapVisibility>("Visibility", temp.Value);
|
||||
|
||||
string requiresMod = string.Empty;
|
||||
var requiresMod = string.Empty;
|
||||
if (yaml.TryGetValue("RequiresMod", out temp))
|
||||
requiresMod = temp.Value;
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace OpenRA
|
||||
|
||||
public MiniYamlNode Clone()
|
||||
{
|
||||
return new MiniYamlNode(Key, Value.Clone());
|
||||
return new MiniYamlNode(Key, Value.Clone(), Comment, Location);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,12 +57,12 @@ namespace OpenRA.Network
|
||||
|
||||
void IConnection.Send(int frame, IEnumerable<Order> o)
|
||||
{
|
||||
orders.Enqueue((frame, new OrderPacket(o.ToArray())));
|
||||
orders.Enqueue((frame, new OrderPacket(o)));
|
||||
}
|
||||
|
||||
void IConnection.SendImmediate(IEnumerable<Order> o)
|
||||
{
|
||||
immediateOrders.Enqueue(new OrderPacket(o.ToArray()));
|
||||
immediateOrders.Enqueue(new OrderPacket(o));
|
||||
}
|
||||
|
||||
void IConnection.SendSync(int frame, int syncHash, ulong defeatState)
|
||||
@@ -230,14 +230,14 @@ namespace OpenRA.Network
|
||||
|
||||
void IConnection.Send(int frame, IEnumerable<Order> orders)
|
||||
{
|
||||
var o = new OrderPacket(orders.ToArray());
|
||||
var o = new OrderPacket(orders);
|
||||
sentOrders.Enqueue((frame, o));
|
||||
Send(o.Serialize(frame));
|
||||
}
|
||||
|
||||
void IConnection.SendImmediate(IEnumerable<Order> orders)
|
||||
{
|
||||
var o = new OrderPacket(orders.ToArray());
|
||||
var o = new OrderPacket(orders);
|
||||
sentImmediateOrders.Enqueue(o);
|
||||
Send(o.Serialize(0));
|
||||
}
|
||||
|
||||
@@ -17,32 +17,32 @@ namespace OpenRA.Network
|
||||
{
|
||||
public class OrderPacket
|
||||
{
|
||||
readonly Order[] orders;
|
||||
readonly MemoryStream data;
|
||||
public OrderPacket(Order[] orders)
|
||||
public OrderPacket(IEnumerable<Order> orders)
|
||||
{
|
||||
this.orders = orders;
|
||||
data = null;
|
||||
// Orders may refer to actors that no longer exist by the time
|
||||
// that the order is resolved. In order to ensure consistent
|
||||
// behaviour between local and remote clients, it is simplest
|
||||
// to always serialize / deserialize orders, instead of storing
|
||||
// the Order objects directly on the local client.
|
||||
data = new MemoryStream();
|
||||
foreach (var o in orders)
|
||||
data.WriteArray(o.Serialize());
|
||||
}
|
||||
|
||||
public OrderPacket(MemoryStream data)
|
||||
{
|
||||
orders = null;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public IEnumerable<Order> GetOrders(World world)
|
||||
{
|
||||
return orders ?? ParseData(world);
|
||||
}
|
||||
|
||||
IEnumerable<Order> ParseData(World world)
|
||||
{
|
||||
if (data == null)
|
||||
if (data.Length == 0)
|
||||
yield break;
|
||||
|
||||
// Order deserialization depends on the current world state,
|
||||
// so must be deferred until we are ready to consume them.
|
||||
data.Position = 0;
|
||||
var reader = new BinaryReader(data);
|
||||
while (data.Position < data.Length)
|
||||
{
|
||||
@@ -54,28 +54,25 @@ namespace OpenRA.Network
|
||||
|
||||
public byte[] Serialize(int frame)
|
||||
{
|
||||
if (data != null)
|
||||
return data.ToArray();
|
||||
|
||||
var ms = new MemoryStream();
|
||||
var ms = new MemoryStream((int)data.Length + 4);
|
||||
ms.WriteArray(BitConverter.GetBytes(frame));
|
||||
foreach (var o in orders)
|
||||
ms.WriteArray(o.Serialize());
|
||||
return ms.ToArray();
|
||||
|
||||
data.Position = 0;
|
||||
data.CopyTo(ms);
|
||||
|
||||
return ms.GetBuffer();
|
||||
}
|
||||
|
||||
public static OrderPacket Combine(IEnumerable<OrderPacket> packets)
|
||||
{
|
||||
var orders = new List<Order>();
|
||||
var ms = new MemoryStream();
|
||||
foreach (var packet in packets)
|
||||
{
|
||||
if (packet.orders == null)
|
||||
throw new InvalidOperationException("OrderPacket.Combine can only be used with locally generated OrderPackets.");
|
||||
|
||||
orders.AddRange(packet.orders);
|
||||
packet.data.Position = 0;
|
||||
packet.data.CopyTo(ms);
|
||||
}
|
||||
|
||||
return new OrderPacket(orders.ToArray());
|
||||
return new OrderPacket(ms);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace OpenRA.Network
|
||||
readonly Queue<Chunk> chunks = new Queue<Chunk>();
|
||||
readonly Queue<(int Frame, int SyncHash, ulong DefeatState)> sync = new Queue<(int, int, ulong)>();
|
||||
readonly int orderLatency;
|
||||
int ordersFrame;
|
||||
|
||||
public readonly int TickCount;
|
||||
public readonly int FinalGameTick;
|
||||
@@ -91,7 +90,6 @@ namespace OpenRA.Network
|
||||
var gameSpeeds = Game.ModData.Manifest.Get<GameSpeeds>();
|
||||
var gameSpeedName = LobbyInfo.GlobalSettings.OptionOrDefault("gamespeed", gameSpeeds.DefaultSpeed);
|
||||
orderLatency = gameSpeeds.Speeds[gameSpeedName].OrderLatency;
|
||||
ordersFrame = orderLatency;
|
||||
}
|
||||
|
||||
void IConnection.StartGame() { }
|
||||
@@ -103,9 +101,6 @@ namespace OpenRA.Network
|
||||
void IConnection.SendSync(int frame, int syncHash, ulong defeatState)
|
||||
{
|
||||
sync.Enqueue((frame, syncHash, defeatState));
|
||||
|
||||
// Store the current frame so Receive() can return the next chunk of orders.
|
||||
ordersFrame = frame + orderLatency;
|
||||
}
|
||||
|
||||
void IConnection.Receive(OrderManager orderManager)
|
||||
@@ -113,7 +108,7 @@ namespace OpenRA.Network
|
||||
while (sync.Count != 0)
|
||||
orderManager.ReceiveSync(sync.Dequeue());
|
||||
|
||||
while (chunks.Count != 0 && chunks.Peek().Frame <= ordersFrame)
|
||||
while (chunks.Count != 0 && chunks.Peek().Frame <= orderManager.NetFrameNumber + orderLatency)
|
||||
{
|
||||
foreach (var o in chunks.Dequeue().Packets)
|
||||
{
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace OpenRA.Network
|
||||
Log.Write("sync", $"\t {a.ActorID} {a.Type} {a.Owner} {a.Trait} ({a.Hash})");
|
||||
|
||||
var nvp = a.NamesValues;
|
||||
for (int i = 0; i < nvp.Names.Length; i++)
|
||||
for (var i = 0; i < nvp.Names.Length; i++)
|
||||
if (nvp.Values[i] != null)
|
||||
Log.Write("sync", $"\t\t {nvp.Names[i]}: {nvp.Values[i]}");
|
||||
}
|
||||
@@ -133,7 +133,7 @@ namespace OpenRA.Network
|
||||
Log.Write("sync", "\t {0} ({1})", e.Name, e.Hash);
|
||||
|
||||
var nvp = e.NamesValues;
|
||||
for (int i = 0; i < nvp.Names.Length; i++)
|
||||
for (var i = 0; i < nvp.Names.Length; i++)
|
||||
if (nvp.Values[i] != null)
|
||||
Log.Write("sync", $"\t\t {nvp.Names[i]}: {nvp.Values[i]}");
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Linguini.Bundle" Version="0.3.1" />
|
||||
<PackageReference Include="OpenRA-Eluant" Version="1.0.18" />
|
||||
<PackageReference Include="OpenRA-Eluant" Version="1.0.20" />
|
||||
<PackageReference Include="Mono.NAT" Version="3.0.3" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||
|
||||
@@ -13,6 +13,7 @@ using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
@@ -23,6 +24,7 @@ namespace OpenRA
|
||||
public static class Platform
|
||||
{
|
||||
public static PlatformType CurrentPlatform => LazyCurrentPlatform.Value;
|
||||
public static Architecture CurrentArchitecture => RuntimeInformation.ProcessArchitecture;
|
||||
public static readonly Guid SessionGUID = Guid.NewGuid();
|
||||
|
||||
static readonly Lazy<PlatformType> LazyCurrentPlatform = Exts.Lazy(GetCurrentPlatform);
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace OpenRA.Scripting
|
||||
|
||||
public static bool TryGetClrValue<T>(this LuaValue value, out T clrObject)
|
||||
{
|
||||
var ret = value.TryGetClrValue(typeof(T), out object temp);
|
||||
var ret = value.TryGetClrValue(typeof(T), out var temp);
|
||||
clrObject = ret ? (T)temp : default;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace OpenRA
|
||||
if (Game.ModData != null)
|
||||
{
|
||||
var mod = Game.ModData.Manifest.Metadata;
|
||||
Log.Write("exception", $"{mod.Title} mod version ${mod.Version}");
|
||||
Log.Write("exception", $"{mod.Title} mod version {mod.Version}");
|
||||
}
|
||||
|
||||
if (Game.OrderManager != null && Game.OrderManager.World != null && Game.OrderManager.World.Map != null)
|
||||
@@ -42,7 +42,7 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
Log.Write("exception", $"Date: {DateTime.UtcNow:u}");
|
||||
Log.Write("exception", $"Operating System: {Platform.CurrentPlatform} ({Environment.OSVersion})");
|
||||
Log.Write("exception", $"Operating System: {Platform.CurrentPlatform} ({Platform.CurrentArchitecture}, {Environment.OSVersion})");
|
||||
Log.Write("exception", $"Runtime Version: {Platform.RuntimeVersion}", Platform.RuntimeVersion);
|
||||
Log.Write("exception", $"Installed Language: {CultureInfo.InstalledUICulture.TwoLetterISOLanguageName} (Installed) {CultureInfo.CurrentCulture.TwoLetterISOLanguageName} (Current) {CultureInfo.CurrentUICulture.TwoLetterISOLanguageName} (Current UI)");
|
||||
|
||||
@@ -56,9 +56,9 @@ namespace OpenRA
|
||||
return BuildExceptionReport(ex, new StringBuilder(), 0);
|
||||
}
|
||||
|
||||
static StringBuilder AppendIndentedFormatLine(this StringBuilder sb, int indent, string format, params object[] args)
|
||||
static StringBuilder AppendIndentedLine(this StringBuilder sb, int indent, string message)
|
||||
{
|
||||
return sb.Append(new string(' ', indent * 2)).AppendFormat(format, args).AppendLine();
|
||||
return sb.Append(new string(' ', indent * 2)).Append(message).AppendLine();
|
||||
}
|
||||
|
||||
static StringBuilder BuildExceptionReport(Exception ex, StringBuilder sb, int indent)
|
||||
@@ -66,11 +66,11 @@ namespace OpenRA
|
||||
if (ex == null)
|
||||
return sb;
|
||||
|
||||
sb.AppendIndentedFormatLine(indent, $"Exception of type `{ex.GetType().FullName}`: {ex.Message}");
|
||||
sb.AppendIndentedLine(indent, $"Exception of type `{ex.GetType().FullName}`: {ex.Message}");
|
||||
|
||||
if (ex is TypeLoadException tle)
|
||||
{
|
||||
sb.AppendIndentedFormatLine(indent, $"TypeName=`{tle.TypeName}`");
|
||||
sb.AppendIndentedLine(indent, $"TypeName=`{tle.TypeName}`");
|
||||
}
|
||||
else if (ex is OutOfMemoryException)
|
||||
{
|
||||
@@ -78,14 +78,14 @@ namespace OpenRA
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
sb.AppendIndentedFormatLine(indent, $"GC Memory (post-collect)={GC.GetTotalMemory(false):N0}");
|
||||
sb.AppendIndentedFormatLine(indent, $"GC Memory (pre-collect)={gcMemoryBeforeCollect:N0}");
|
||||
sb.AppendIndentedLine(indent, $"GC Memory (post-collect)={GC.GetTotalMemory(false):N0}");
|
||||
sb.AppendIndentedLine(indent, $"GC Memory (pre-collect)={gcMemoryBeforeCollect:N0}");
|
||||
|
||||
using (var p = Process.GetCurrentProcess())
|
||||
{
|
||||
sb.AppendIndentedFormatLine(indent, $"Working Set={p.WorkingSet64:N0}");
|
||||
sb.AppendIndentedFormatLine(indent, $"Private Memory={p.PrivateMemorySize64:N0}");
|
||||
sb.AppendIndentedFormatLine(indent, $"Virtual Memory={p.VirtualMemorySize64:N0}");
|
||||
sb.AppendIndentedLine(indent, $"Working Set={p.WorkingSet64:N0}");
|
||||
sb.AppendIndentedLine(indent, $"Private Memory={p.PrivateMemorySize64:N0}");
|
||||
sb.AppendIndentedLine(indent, $"Virtual Memory={p.VirtualMemorySize64:N0}");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -95,11 +95,11 @@ namespace OpenRA
|
||||
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
sb.AppendIndentedFormatLine(indent, "Inner");
|
||||
sb.AppendIndentedLine(indent, "Inner");
|
||||
BuildExceptionReport(ex.InnerException, sb, indent + 1);
|
||||
}
|
||||
|
||||
sb.AppendIndentedFormatLine(indent, ex.StackTrace);
|
||||
sb.AppendIndentedLine(indent, ex.StackTrace);
|
||||
|
||||
return sb;
|
||||
}
|
||||
|
||||
@@ -244,6 +244,12 @@ namespace OpenRA.Traits
|
||||
IEnumerable<Rectangle> ModifyScreenBounds(Actor self, WorldRenderer wr, IEnumerable<Rectangle> r);
|
||||
}
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface ITilesetSpecificPaletteInfo : ITraitInfoInterface
|
||||
{
|
||||
string Tileset { get; }
|
||||
}
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface IProvidesCursorPaletteInfo : ITraitInfoInterface
|
||||
{
|
||||
|
||||
@@ -88,6 +88,9 @@ namespace OpenRA
|
||||
|
||||
public bool TryGetString(string key, out string value, IDictionary<string, object> arguments = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key))
|
||||
throw new ArgumentException("A translation key must not be null or empty.", nameof(key));
|
||||
|
||||
if (!HasMessage(key))
|
||||
{
|
||||
value = null;
|
||||
|
||||
@@ -77,12 +77,12 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
||||
byte* ip;
|
||||
uint t;
|
||||
byte* mPos;
|
||||
byte* ipEnd = @in + inLen;
|
||||
var ipEnd = @in + inLen;
|
||||
outLen = 0;
|
||||
op = @out;
|
||||
ip = @in;
|
||||
bool gtFirstLiteralRun = false;
|
||||
bool gtMatchDone = false;
|
||||
var gtFirstLiteralRun = false;
|
||||
var gtMatchDone = false;
|
||||
if (*ip > 17)
|
||||
{
|
||||
t = (uint)(*ip++ - 17);
|
||||
|
||||
@@ -338,8 +338,8 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
||||
var mapSection = file.GetSection("IsoMapPack5");
|
||||
|
||||
var data = Convert.FromBase64String(string.Concat(mapSection.Select(kvp => kvp.Value)));
|
||||
int cells = (fullSize.X * 2 - 1) * fullSize.Y;
|
||||
int lzoPackSize = cells * 11 + 4; // last 4 bytes contains a lzo pack header saying no more data is left
|
||||
var cells = (fullSize.X * 2 - 1) * fullSize.Y;
|
||||
var lzoPackSize = cells * 11 + 4; // last 4 bytes contains a lzo pack header saying no more data is left
|
||||
var isoMapPack = new byte[lzoPackSize];
|
||||
UnpackLZO(data, isoMapPack);
|
||||
|
||||
@@ -354,8 +354,8 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
|
||||
var z = mf.ReadUInt8();
|
||||
/*var zero2 = */mf.ReadUInt8();
|
||||
|
||||
int dx = rx - ry + fullSize.X - 1;
|
||||
int dy = rx + ry - fullSize.X - 1;
|
||||
var dx = rx - ry + fullSize.X - 1;
|
||||
var dy = rx + ry - fullSize.X - 1;
|
||||
var mapCell = new MPos(dx / 2, dy);
|
||||
var cell = mapCell.ToCPos(map);
|
||||
|
||||
|
||||
@@ -65,15 +65,12 @@ namespace OpenRA.Mods.Common.Activities
|
||||
|
||||
public override bool Tick(Actor self)
|
||||
{
|
||||
if (cargo != carryall.Carryable)
|
||||
return true;
|
||||
|
||||
if (IsCanceling)
|
||||
if (IsCanceling || cargo != carryall.Carryable)
|
||||
{
|
||||
if (carryall.State == Carryall.CarryallState.Reserved)
|
||||
carryall.UnreserveCarryable(self);
|
||||
|
||||
// Make sure we run the TakeOff activity if we are / have landed
|
||||
// Make sure we run the TakeOff activity if we are/have landed
|
||||
if (self.Trait<Aircraft>().HasInfluence())
|
||||
{
|
||||
ChildHasPriority = true;
|
||||
@@ -88,7 +85,8 @@ namespace OpenRA.Mods.Common.Activities
|
||||
if (cargo.IsDead || carryable.IsTraitDisabled || !cargo.AppearsFriendlyTo(self))
|
||||
{
|
||||
carryall.UnreserveCarryable(self);
|
||||
return true;
|
||||
Cancel(self, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait until we are near the target before we try to lock it
|
||||
@@ -100,7 +98,10 @@ namespace OpenRA.Mods.Common.Activities
|
||||
{
|
||||
var lockResponse = carryable.LockForPickup(cargo, self);
|
||||
if (lockResponse == LockResponse.Failed)
|
||||
Cancel(self);
|
||||
{
|
||||
Cancel(self, true);
|
||||
return false;
|
||||
}
|
||||
else if (lockResponse == LockResponse.Success)
|
||||
{
|
||||
// Pickup position and facing are now known - swap the fly/wait activity with Land
|
||||
|
||||
@@ -115,6 +115,7 @@ namespace OpenRA.Mods.Common
|
||||
DateTime? timestamp = null;
|
||||
Party party = null;
|
||||
Secrets secrets = null;
|
||||
Button[] buttons = null;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
@@ -161,6 +162,18 @@ namespace OpenRA.Mods.Common
|
||||
throw new ArgumentOutOfRangeException(nameof(state), state, null);
|
||||
}
|
||||
|
||||
if (party == null)
|
||||
{
|
||||
buttons = new[]
|
||||
{
|
||||
new Button
|
||||
{
|
||||
Label = "Visit Website",
|
||||
Url = Game.ModData.Manifest.Metadata.Website
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var richPresence = new RichPresence
|
||||
{
|
||||
Details = details,
|
||||
@@ -173,14 +186,7 @@ namespace OpenRA.Mods.Common
|
||||
Timestamps = timestamp.HasValue ? new Timestamps(timestamp.Value) : null,
|
||||
Party = party,
|
||||
Secrets = secrets,
|
||||
Buttons = new[]
|
||||
{
|
||||
new Button
|
||||
{
|
||||
Label = "Visit Website",
|
||||
Url = Game.ModData.Manifest.Metadata.Website
|
||||
}
|
||||
}
|
||||
Buttons = buttons
|
||||
};
|
||||
|
||||
client.SetPresence(richPresence);
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace OpenRA.Mods.Common.Graphics
|
||||
for (var i = 1; i < renderLength; i++)
|
||||
{
|
||||
var j = next - skip - 1 - i;
|
||||
var nextColor = Exts.ColorLerp(i * 1f / (renderLength - 1), startcolor, endcolor);
|
||||
var nextColor = Exts.ColorLerp(i / (renderLength - 1f), startcolor, endcolor);
|
||||
|
||||
var nextX = 0L;
|
||||
var nextY = 0L;
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Server;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -146,11 +145,11 @@ namespace OpenRA.Mods.Common.Lint
|
||||
}
|
||||
else
|
||||
{
|
||||
// PaletteFromFile might only be active for a single tileset
|
||||
// So ignore any duplicate palette names as long as they are on different tilesets
|
||||
if (traitInfo is PaletteFromFileInfo paletteFromFileInfo && paletteFromFileInfo.Tileset != null)
|
||||
// Tileset-specific palettes can share a name, so check combinations.
|
||||
// NOTE: This does not check PaletteFromGimpOrJascFile!
|
||||
if (traitInfo is ITilesetSpecificPaletteInfo tilesetSpecificPaletteInfo && tilesetSpecificPaletteInfo.Tileset != null)
|
||||
{
|
||||
var tilesetPalette = (paletteFromFileInfo.Tileset, value);
|
||||
var tilesetPalette = (tilesetSpecificPaletteInfo.Tileset, value);
|
||||
if (tilesetPalettes.Contains(tilesetPalette))
|
||||
emitError($"Duplicate palette definition for palette name {value}");
|
||||
else
|
||||
|
||||
@@ -32,6 +32,15 @@ namespace OpenRA.Mods.Common.Lint
|
||||
var language = "en";
|
||||
var translation = new Translation(language, modData.Manifest.Translations, modData.DefaultFileSystem);
|
||||
|
||||
var gameSpeeds = modData.Manifest.Get<GameSpeeds>();
|
||||
foreach (var speed in gameSpeeds.Speeds.Values)
|
||||
{
|
||||
if (!translation.HasMessage(speed.Name))
|
||||
emitError($"{speed.Name} not present in {language} translation.");
|
||||
|
||||
referencedKeys.Add(speed.Name);
|
||||
}
|
||||
|
||||
foreach (var modType in modData.ObjectCreator.GetTypes())
|
||||
{
|
||||
foreach (var fieldInfo in modType.GetFields(Binding).Where(m => m.HasAttribute<TranslationReferenceAttribute>()))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</ProjectReference>
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageReference Include="OpenRA-FuzzyLogicLibrary" Version="1.0.1" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.0.175" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||
<PackageReference Include="MP3Sharp" Version="1.0.5" />
|
||||
<PackageReference Include="NVorbis" Version="0.10.4" />
|
||||
<PackageReference Include="TagLibSharp" Version="2.2.0" />
|
||||
|
||||
@@ -281,6 +281,10 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
// When we build the cost table, it depends on the movement costs of the cells at that time.
|
||||
// When this changes, we must update the cost table.
|
||||
locomotor.CellCostChanged += RequireCostRefreshInCell;
|
||||
|
||||
// If the map projection changes, the result of Map.Contains(CPos) may change.
|
||||
// We need to rebuild grids to account for this possibility.
|
||||
this.world.Map.CellProjectionChanged += RequireProjectionRefreshInCell;
|
||||
}
|
||||
|
||||
public (
|
||||
@@ -619,6 +623,14 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When map projection changes for a cell, marks the grid it belongs to as out of date.
|
||||
/// </summary>
|
||||
void RequireProjectionRefreshInCell(CPos cell)
|
||||
{
|
||||
dirtyGridIndexes.Add(GridIndex(cell));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="BlockedByActor.Immovable"/> defines immovability based on the mobile trait. The blocking rules
|
||||
/// in <see cref="Locomotor.CanMoveFreelyInto(Actor, CPos, SubCell, BlockedByActor, Actor)"/> allow units to
|
||||
@@ -780,6 +792,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
fullGraph.GetConnections, locomotor, target, target, estimatedSearchSize, pathFinderOverlay?.RecordAbstractEdges(self)))
|
||||
{
|
||||
var sourcesWithPathableNodes = new HashSet<CPos>(sources.Count);
|
||||
List<CPos> unpathableNodes = null;
|
||||
foreach (var (source, adjacentSource) in sourcesWithReachableNodes)
|
||||
{
|
||||
// Check if we have already found a route to this node before we attempt to expand the search.
|
||||
@@ -788,12 +801,24 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
{
|
||||
if (sourceStatus.CostSoFar != PathGraph.PathCostForInvalidPath)
|
||||
sourcesWithPathableNodes.Add(source);
|
||||
else
|
||||
{
|
||||
if (unpathableNodes == null)
|
||||
unpathableNodes = new List<CPos>();
|
||||
unpathableNodes.Add(adjacentSource);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reverseAbstractSearch.TargetPredicate = cell => cell == adjacentSource;
|
||||
if (reverseAbstractSearch.ExpandToTarget())
|
||||
sourcesWithPathableNodes.Add(source);
|
||||
else
|
||||
{
|
||||
if (unpathableNodes == null)
|
||||
unpathableNodes = new List<CPos>();
|
||||
unpathableNodes.Add(adjacentSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -802,7 +827,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
|
||||
using (var fromSrc = GetLocalPathSearch(
|
||||
self, sourcesWithPathableNodes, target, customCost, ignoreActor, check, laneBias, null, heuristicWeightPercentage,
|
||||
heuristic: Heuristic(reverseAbstractSearch, estimatedSearchSize, sourcesWithPathableNodes),
|
||||
heuristic: Heuristic(reverseAbstractSearch, estimatedSearchSize, sourcesWithPathableNodes, unpathableNodes),
|
||||
recorder: pathFinderOverlay?.RecordLocalEdges(self)))
|
||||
return fromSrc.FindPath();
|
||||
}
|
||||
@@ -852,12 +877,12 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
|
||||
RebuildDirtyGrids();
|
||||
|
||||
// If the target cell in unreachable, there is no path.
|
||||
// If the target cell is unreachable, there is no path.
|
||||
var targetAbstractCell = AbstractCellForLocalCell(target);
|
||||
if (targetAbstractCell == null)
|
||||
return PathFinder.NoPath;
|
||||
|
||||
// If the source cell in unreachable, there may still be a path.
|
||||
// If the source cell is unreachable, there may still be a path.
|
||||
// As long as one of the cells adjacent to the source is reachable, the path can be made.
|
||||
// Call the other overload which can handle this scenario.
|
||||
var sourceAbstractCell = AbstractCellForLocalCell(source);
|
||||
@@ -886,11 +911,11 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
|
||||
using (var fromSrc = GetLocalPathSearch(
|
||||
self, new[] { source }, target, customCost, ignoreActor, check, laneBias, null, heuristicWeightPercentage,
|
||||
heuristic: Heuristic(reverseAbstractSearch, estimatedSearchSize, null),
|
||||
heuristic: Heuristic(reverseAbstractSearch, estimatedSearchSize, null, null),
|
||||
recorder: pathFinderOverlay?.RecordLocalEdges(self)))
|
||||
using (var fromDest = GetLocalPathSearch(
|
||||
self, new[] { target }, source, customCost, ignoreActor, check, laneBias, null, heuristicWeightPercentage,
|
||||
heuristic: Heuristic(forwardAbstractSearch, estimatedSearchSize, null),
|
||||
heuristic: Heuristic(forwardAbstractSearch, estimatedSearchSize, null, null),
|
||||
recorder: pathFinderOverlay?.RecordLocalEdges(self),
|
||||
inReverse: true))
|
||||
return PathSearch.FindBidiPath(fromDest, fromSrc);
|
||||
@@ -1096,13 +1121,20 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
/// (the heuristic) for a local path search. The abstract search must run in the opposite direction to the
|
||||
/// local search. So when searching from source to target, the abstract search must be from target to source.
|
||||
/// </summary>
|
||||
Func<CPos, int> Heuristic(PathSearch abstractSearch, int estimatedSearchSize, HashSet<CPos> sources)
|
||||
Func<CPos, int> Heuristic(PathSearch abstractSearch, int estimatedSearchSize,
|
||||
HashSet<CPos> sources, List<CPos> unpathableNodes)
|
||||
{
|
||||
var nodeForCostLookup = new Dictionary<CPos, CPos>(estimatedSearchSize);
|
||||
var graph = (SparsePathGraph)abstractSearch.Graph;
|
||||
return cell =>
|
||||
{
|
||||
// All cells searched by the heuristic are guaranteed to be reachable.
|
||||
// When dealing with an unreachable source cell, the path search will check adjacent locations.
|
||||
// These cells may be reachable, but may represent jumping into an area cut off from the target.
|
||||
// Searching on the abstract graph would fail to provide a route in this scenario, so bail early.
|
||||
if (unpathableNodes != null && unpathableNodes.Contains(cell))
|
||||
return PathGraph.PathCostForInvalidPath;
|
||||
|
||||
// All other cells searched by the heuristic are guaranteed to be reachable.
|
||||
// So we don't need to handle an abstract cell lookup failing, or the search failing to expand.
|
||||
// Cells added as initial starting points for the search are filtered out if they aren't reachable.
|
||||
// The search only explores accessible cells from then on.
|
||||
@@ -1112,13 +1144,14 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
var maybeAbstractCell = AbstractCellForLocalCellNoAccessibleCheck(cell);
|
||||
if (maybeAbstractCell == null)
|
||||
{
|
||||
// If the source cell in unreachable, use one of the adjacent reachable cells instead.
|
||||
// If the source cell is unreachable, use one of the adjacent reachable cells instead.
|
||||
if (sources != null && sources.Contains(cell))
|
||||
{
|
||||
foreach (var dir in CVec.Directions)
|
||||
{
|
||||
var adjacentSource = cell + dir;
|
||||
if (!world.Map.Contains(adjacentSource))
|
||||
if (!world.Map.Contains(adjacentSource) ||
|
||||
(unpathableNodes != null && unpathableNodes.Contains(adjacentSource)))
|
||||
continue;
|
||||
|
||||
// Ideally we'd choose the cheapest cell rather than just any one of them,
|
||||
@@ -1132,7 +1165,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
if (maybeAbstractCell == null)
|
||||
throw new Exception(
|
||||
"The abstract path should never be searched for an unreachable point. " +
|
||||
"This is a bug. Failed lookup for an abstract cell.");
|
||||
$"Cell {cell} failed lookup for an abstract cell.");
|
||||
}
|
||||
|
||||
var abstractCell = maybeAbstractCell.Value;
|
||||
@@ -1145,7 +1178,7 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
if (!abstractSearch.ExpandToTarget())
|
||||
throw new Exception(
|
||||
"The abstract path should never be searched for an unreachable point. " +
|
||||
"This is a bug. Failed to route to abstract cell.");
|
||||
$"Abstract cell {abstractCell} failed to route to abstract cell.");
|
||||
info = graph[abstractCell];
|
||||
}
|
||||
|
||||
|
||||
@@ -182,7 +182,11 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
return;
|
||||
}
|
||||
|
||||
var estimatedCost = heuristic(location) * heuristicWeightPercentage / 100;
|
||||
var heuristicCost = heuristic(location);
|
||||
if (heuristicCost == PathGraph.PathCostForInvalidPath)
|
||||
return;
|
||||
|
||||
var estimatedCost = heuristicCost * heuristicWeightPercentage / 100;
|
||||
Graph[location] = new CellInfo(CellStatus.Open, initialCost, initialCost + estimatedCost, location);
|
||||
var connection = new GraphConnection(location, estimatedCost);
|
||||
openQueue.Add(connection);
|
||||
@@ -237,14 +241,22 @@ namespace OpenRA.Mods.Common.Pathfinder
|
||||
(neighborInfo.Status == CellStatus.Open && costSoFarToNeighbor >= neighborInfo.CostSoFar))
|
||||
continue;
|
||||
|
||||
// Now we may seriously consider this direction using heuristics. If the cell has
|
||||
// already been processed, we can reuse the result (just the difference between the
|
||||
// estimated total and the cost so far)
|
||||
// Now we may seriously consider this direction using heuristics.
|
||||
int estimatedRemainingCostToTarget;
|
||||
if (neighborInfo.Status == CellStatus.Open)
|
||||
{
|
||||
// If the cell has already been processed, we can reuse the result
|
||||
// (just the difference between the estimated total and the cost so far)
|
||||
estimatedRemainingCostToTarget = neighborInfo.EstimatedTotalCost - neighborInfo.CostSoFar;
|
||||
}
|
||||
else
|
||||
estimatedRemainingCostToTarget = heuristic(neighbor) * heuristicWeightPercentage / 100;
|
||||
{
|
||||
// If the heuristic reports the cell is unreachable, we won't consider it.
|
||||
var heuristicCost = heuristic(neighbor);
|
||||
if (heuristicCost == PathGraph.PathCostForInvalidPath)
|
||||
continue;
|
||||
estimatedRemainingCostToTarget = heuristicCost * heuristicWeightPercentage / 100;
|
||||
}
|
||||
|
||||
recorder?.Add(currentMinNode, neighbor, costSoFarToNeighbor, estimatedRemainingCostToTarget);
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@ namespace OpenRA.Mods.Common.Projectiles
|
||||
[Desc("How far beyond the target the projectile keeps on travelling.")]
|
||||
public readonly WDist BeyondTargetRange = new WDist(0);
|
||||
|
||||
[Desc("The minimum distance the beam travels.")]
|
||||
public readonly WDist MinDistance = WDist.Zero;
|
||||
|
||||
[Desc("Damage modifier applied at each range step.")]
|
||||
public readonly int[] Falloff = { 100, 100 };
|
||||
|
||||
@@ -136,7 +139,19 @@ namespace OpenRA.Mods.Common.Projectiles
|
||||
// Update the target position with the range we shoot beyond the target by
|
||||
// I.e. we can deliberately overshoot, so aim for that position
|
||||
var dir = new WVec(0, -1024, 0).Rotate(WRot.FromYaw(towardsTargetFacing));
|
||||
target += dir * info.BeyondTargetRange.Length / 1024;
|
||||
var dist = (args.SourceActor.CenterPosition - target).Length;
|
||||
int extraDist;
|
||||
if (info.MinDistance.Length > dist)
|
||||
{
|
||||
if (info.MinDistance.Length - dist < info.BeyondTargetRange.Length)
|
||||
extraDist = info.BeyondTargetRange.Length;
|
||||
else
|
||||
extraDist = info.MinDistance.Length - dist;
|
||||
}
|
||||
else
|
||||
extraDist = info.BeyondTargetRange.Length;
|
||||
|
||||
target += dir * extraDist / 1024;
|
||||
|
||||
length = Math.Max((target - headPos).Length / speed.Length, 1);
|
||||
weaponRange = new WDist(Util.ApplyPercentageModifiers(args.Weapon.Range.Length, args.RangeModifiers));
|
||||
|
||||
@@ -197,7 +197,7 @@ namespace OpenRA.Mods.Common.Projectiles
|
||||
if (info.ContrailLength > 0)
|
||||
{
|
||||
var startcolor = info.ContrailStartColorUsePlayerColor ? Color.FromArgb(info.ContrailStartColorAlpha, args.SourceActor.Owner.Color) : Color.FromArgb(info.ContrailStartColorAlpha, info.ContrailStartColor);
|
||||
var endcolor = info.ContrailEndColorUsePlayerColor ? Color.FromArgb(info.ContrailEndColorAlpha, args.SourceActor.Owner.Color) : Color.FromArgb(info.ContrailEndColorAlpha, info.ContrailEndColor ?? info.ContrailStartColor);
|
||||
var endcolor = info.ContrailEndColorUsePlayerColor ? Color.FromArgb(info.ContrailEndColorAlpha, args.SourceActor.Owner.Color) : Color.FromArgb(info.ContrailEndColorAlpha, info.ContrailEndColor ?? startcolor);
|
||||
contrail = new ContrailRenderable(world, startcolor, endcolor, info.ContrailWidth, info.ContrailLength, info.ContrailDelay, info.ContrailZOffset);
|
||||
}
|
||||
|
||||
|
||||
@@ -283,7 +283,7 @@ namespace OpenRA.Mods.Common.Projectiles
|
||||
if (info.ContrailLength > 0)
|
||||
{
|
||||
var startcolor = info.ContrailStartColorUsePlayerColor ? Color.FromArgb(info.ContrailStartColorAlpha, args.SourceActor.Owner.Color) : Color.FromArgb(info.ContrailStartColorAlpha, info.ContrailStartColor);
|
||||
var endcolor = info.ContrailEndColorUsePlayerColor ? Color.FromArgb(info.ContrailEndColorAlpha, args.SourceActor.Owner.Color) : Color.FromArgb(info.ContrailEndColorAlpha, info.ContrailEndColor ?? info.ContrailStartColor);
|
||||
var endcolor = info.ContrailEndColorUsePlayerColor ? Color.FromArgb(info.ContrailEndColorAlpha, args.SourceActor.Owner.Color) : Color.FromArgb(info.ContrailEndColorAlpha, info.ContrailEndColor ?? startcolor);
|
||||
contrail = new ContrailRenderable(world, startcolor, endcolor, info.ContrailWidth, info.ContrailLength, info.ContrailDelay, info.ContrailZOffset);
|
||||
}
|
||||
|
||||
@@ -760,10 +760,10 @@ namespace OpenRA.Mods.Common.Projectiles
|
||||
|
||||
WVec HomingTick(World world, in WVec tarDistVec, int relTarHorDist)
|
||||
{
|
||||
int predClfHgt = 0;
|
||||
int predClfDist = 0;
|
||||
int lastHtChg = 0;
|
||||
int lastHt = 0;
|
||||
var predClfHgt = 0;
|
||||
var predClfDist = 0;
|
||||
var lastHtChg = 0;
|
||||
var lastHt = 0;
|
||||
|
||||
if (info.TerrainHeightAware)
|
||||
InclineLookahead(world, relTarHorDist, out predClfHgt, out predClfDist, out lastHtChg, out lastHt);
|
||||
|
||||
@@ -175,8 +175,8 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
return false;
|
||||
}
|
||||
|
||||
AsyncLoader l = new AsyncLoader(Media.LoadVideo);
|
||||
IAsyncResult ar = l.BeginInvoke(s, null, null);
|
||||
var l = new AsyncLoader(Media.LoadVideo);
|
||||
var ar = l.BeginInvoke(s, null, null);
|
||||
Action onLoadComplete = () =>
|
||||
{
|
||||
Media.StopFMVInRadar();
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using Eluant;
|
||||
using OpenRA.Mods.Common.Activities;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Scripting;
|
||||
@@ -31,12 +30,18 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
[Desc("Captures the target actor.")]
|
||||
public void Capture(Actor target)
|
||||
{
|
||||
var targetManager = target.TraitOrDefault<CaptureManager>();
|
||||
if (targetManager == null || !targetManager.CanBeTargetedBy(target, Self, captureManager))
|
||||
throw new LuaException($"Actor '{Self}' cannot capture actor '{target}'!");
|
||||
if (!CanCapture(target))
|
||||
return;
|
||||
|
||||
// NB: Scripted actions get no visible targetlines.
|
||||
Self.QueueActivity(new CaptureActor(Self, Target.FromActor(target), null));
|
||||
}
|
||||
|
||||
[Desc("Checks if the target actor can be catured.")]
|
||||
public bool CanCapture(Actor target)
|
||||
{
|
||||
var targetManager = target.TraitOrDefault<CaptureManager>();
|
||||
return targetManager != null && targetManager.CanBeTargetedBy(target, Self, captureManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common
|
||||
/// /// </summary>
|
||||
public static Target RecalculateInvalidatingHiddenTargets(this Target t, Player viewer)
|
||||
{
|
||||
var updated = t.Recalculate(viewer, out bool targetIsHiddenActor);
|
||||
var updated = t.Recalculate(viewer, out var targetIsHiddenActor);
|
||||
return targetIsHiddenActor ? Target.Invalid : updated;
|
||||
}
|
||||
|
||||
|
||||
@@ -868,8 +868,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public void AddInfluence((CPos, SubCell)[] landingCells)
|
||||
{
|
||||
if (HasInfluence())
|
||||
throw new InvalidOperationException(
|
||||
$"Cannot {nameof(AddInfluence)} until previous influence is removed with {nameof(RemoveInfluence)}");
|
||||
self.World.ActorMap.RemoveInfluence(self, this);
|
||||
|
||||
this.landingCells = landingCells;
|
||||
if (self.IsInWorld)
|
||||
|
||||
@@ -106,30 +106,33 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
readonly Actor cargo;
|
||||
readonly Carryable carryable;
|
||||
readonly CarryallInfo carryallInfo;
|
||||
readonly Carryall carryall;
|
||||
|
||||
public FerryUnit(Actor self, Actor cargo)
|
||||
{
|
||||
this.cargo = cargo;
|
||||
carryable = cargo.Trait<Carryable>();
|
||||
carryallInfo = self.Trait<Carryall>().Info;
|
||||
carryall = self.Trait<Carryall>();
|
||||
}
|
||||
|
||||
protected override void OnFirstRun(Actor self)
|
||||
{
|
||||
if (!cargo.IsDead)
|
||||
QueueChild(new PickupUnit(self, cargo, 0, carryallInfo.TargetLineColor));
|
||||
QueueChild(new PickupUnit(self, cargo, 0, carryall.Info.TargetLineColor));
|
||||
}
|
||||
|
||||
public override bool Tick(Actor self)
|
||||
{
|
||||
if (cargo.IsDead)
|
||||
{
|
||||
carryall.UnreserveCarryable(self);
|
||||
return true;
|
||||
}
|
||||
|
||||
var dropRange = carryallInfo.DropRange;
|
||||
var dropRange = carryall.Info.DropRange;
|
||||
var destination = carryable.Destination;
|
||||
if (destination != null)
|
||||
self.QueueActivity(true, new DeliverUnit(self, Target.FromCell(self.World, destination.Value), dropRange, carryallInfo.TargetLineColor));
|
||||
self.QueueActivity(true, new DeliverUnit(self, Target.FromCell(self.World, destination.Value), dropRange, carryall.Info.TargetLineColor));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -342,7 +342,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
var chosenTarget = Target.Invalid;
|
||||
var chosenTargetPriority = int.MinValue;
|
||||
int chosenTargetRange = 0;
|
||||
var chosenTargetRange = 0;
|
||||
|
||||
var activePriorities = activeTargetPriorities.ToList();
|
||||
if (activePriorities.Count == 0)
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var type = BuildingType.Building;
|
||||
CPos? location = null;
|
||||
var actorVariant = 0;
|
||||
string orderString = "PlaceBuilding";
|
||||
var orderString = "PlaceBuilding";
|
||||
|
||||
// Check if Building is a plug for other Building
|
||||
var actorInfo = world.Map.Rules.Actors[currentBuilding.Item];
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
// Find new harvesters
|
||||
// TODO: Look for a more performance-friendly way to update this list
|
||||
var newHarvesters = world.ActorsHavingTrait<Harvester>().Where(a => a.Owner == player && !harvesters.ContainsKey(a));
|
||||
var newHarvesters = world.ActorsHavingTrait<Harvester>().Where(a => !unitCannotBeOrdered(a) && !harvesters.ContainsKey(a));
|
||||
foreach (var a in newHarvesters)
|
||||
harvesters[a] = new HarvesterTraitWrapper(a);
|
||||
|
||||
|
||||
@@ -256,7 +256,11 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public virtual void UnreserveCarryable(Actor self)
|
||||
{
|
||||
if (Carryable != null && Carryable.IsInWorld && !Carryable.IsDead)
|
||||
Carryable.Trait<Carryable>().UnReserve(Carryable);
|
||||
{
|
||||
var carryable = Carryable.Trait<Carryable>();
|
||||
if (carryable.Carrier == self)
|
||||
carryable.UnReserve(Carryable);
|
||||
}
|
||||
|
||||
Carryable = null;
|
||||
State = CarryallState.Idle;
|
||||
|
||||
@@ -376,7 +376,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
var availCells = new List<CPos>();
|
||||
var notStupidCells = new List<CPos>();
|
||||
foreach (CVec direction in CVec.Directions)
|
||||
foreach (var direction in CVec.Directions)
|
||||
{
|
||||
var p = ToCell + direction;
|
||||
if (CanEnterCell(p) && CanStayInCell(p) && (preferToAvoid == null || !preferToAvoid(p)))
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[TraitLocation(SystemActors.World | SystemActors.EditorWorld)]
|
||||
[Desc("Load VGA palette (.pal) registers.")]
|
||||
class PaletteFromFileInfo : TraitInfo, IProvidesCursorPaletteInfo
|
||||
class PaletteFromFileInfo : TraitInfo, ITilesetSpecificPaletteInfo, IProvidesCursorPaletteInfo
|
||||
{
|
||||
[PaletteDefinition]
|
||||
[FieldLoader.Require]
|
||||
@@ -46,6 +46,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public override object Create(ActorInitializer init) { return new PaletteFromFile(init.World, this); }
|
||||
|
||||
string ITilesetSpecificPaletteInfo.Tileset => Tileset;
|
||||
|
||||
string IProvidesCursorPaletteInfo.Palette => CursorPalette ? Name : null;
|
||||
|
||||
ImmutablePalette IProvidesCursorPaletteInfo.ReadPalette(IReadOnlyFileSystem fileSystem)
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[TraitLocation(SystemActors.World | SystemActors.EditorWorld)]
|
||||
[Desc("Creates a greyscale palette without any base palette file.")]
|
||||
class PaletteFromGrayscaleInfo : TraitInfo
|
||||
class PaletteFromGrayscaleInfo : TraitInfo, ITilesetSpecificPaletteInfo
|
||||
{
|
||||
[PaletteDefinition]
|
||||
[FieldLoader.Require]
|
||||
@@ -34,6 +34,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Index set to be fully transparent/invisible.")]
|
||||
public readonly int TransparentIndex = 0;
|
||||
|
||||
string ITilesetSpecificPaletteInfo.Tileset => Tileset;
|
||||
|
||||
public override object Create(ActorInitializer init) { return new PaletteFromGrayscale(init.World, this); }
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[TraitLocation(SystemActors.World | SystemActors.EditorWorld)]
|
||||
[Desc("Load a PNG and use its embedded palette.")]
|
||||
class PaletteFromPngInfo : TraitInfo, IProvidesCursorPaletteInfo
|
||||
class PaletteFromPngInfo : TraitInfo, ITilesetSpecificPaletteInfo, IProvidesCursorPaletteInfo
|
||||
{
|
||||
[PaletteDefinition]
|
||||
[FieldLoader.Require]
|
||||
@@ -44,6 +44,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public override object Create(ActorInitializer init) { return new PaletteFromPng(init.World, this); }
|
||||
|
||||
string ITilesetSpecificPaletteInfo.Tileset => Tileset;
|
||||
|
||||
string IProvidesCursorPaletteInfo.Palette => CursorPalette ? Name : null;
|
||||
|
||||
ImmutablePalette IProvidesCursorPaletteInfo.ReadPalette(IReadOnlyFileSystem fileSystem)
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[TraitLocation(SystemActors.World | SystemActors.EditorWorld)]
|
||||
[Desc("Creates a single color palette without any base palette file.")]
|
||||
class PaletteFromRGBAInfo : TraitInfo
|
||||
class PaletteFromRGBAInfo : TraitInfo, ITilesetSpecificPaletteInfo
|
||||
{
|
||||
[PaletteDefinition]
|
||||
[FieldLoader.Require]
|
||||
@@ -46,6 +46,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Index set to be fully transparent/invisible.")]
|
||||
public readonly int TransparentIndex = 0;
|
||||
|
||||
string ITilesetSpecificPaletteInfo.Tileset => Tileset;
|
||||
|
||||
public override object Create(ActorInitializer init) { return new PaletteFromRGBA(init.World, this); }
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
this.info = info;
|
||||
|
||||
startcolor = info.StartColorUsePlayerColor ? Color.FromArgb(info.StartColorAlpha, self.Owner.Color) : Color.FromArgb(info.StartColorAlpha, info.StartColor);
|
||||
endcolor = info.EndColorUsePlayerColor ? Color.FromArgb(info.EndColorAlpha, self.Owner.Color) : Color.FromArgb(info.EndColorAlpha, info.EndColor ?? info.StartColor);
|
||||
endcolor = info.EndColorUsePlayerColor ? Color.FromArgb(info.EndColorAlpha, self.Owner.Color) : Color.FromArgb(info.EndColorAlpha, info.EndColor ?? startcolor);
|
||||
trail = new ContrailRenderable(self.World, startcolor, endcolor, info.TrailWidth, info.TrailLength, info.TrailDelay, info.ZOffset);
|
||||
|
||||
body = self.Trait<BodyOrientation>();
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
WorldRenderer worldRenderer;
|
||||
|
||||
public MapPlayers Players { get; private set; }
|
||||
PlayerReference worldOwner;
|
||||
|
||||
public EditorActorLayer(EditorActorLayerInfo info)
|
||||
{
|
||||
@@ -59,7 +60,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
Players = new MapPlayers(w.Map.PlayerDefinitions);
|
||||
|
||||
var worldOwner = Players.Players.Select(kvp => kvp.Value).First(p => !p.Playable && p.OwnsWorld);
|
||||
worldOwner = Players.Players.Select(kvp => kvp.Value).First(p => !p.Playable && p.OwnsWorld);
|
||||
w.SetWorldOwner(new Player(w, null, worldOwner, playerRandom));
|
||||
}
|
||||
|
||||
@@ -126,11 +127,17 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public EditorActorPreview Add(string id, ActorReference reference, bool initialSetup = false)
|
||||
{
|
||||
var owner = Players.Players[reference.Get<OwnerInit>().InternalName];
|
||||
// If an actor's doesn't have a valid owner transfer ownership to neutral
|
||||
var ownerInit = reference.Get<OwnerInit>();
|
||||
if (!Players.Players.TryGetValue(ownerInit.InternalName, out var owner))
|
||||
{
|
||||
owner = worldOwner;
|
||||
reference.Remove(ownerInit);
|
||||
reference.Add(new OwnerInit(worldOwner.Name));
|
||||
}
|
||||
|
||||
var preview = new EditorActorPreview(worldRenderer, id, reference, owner);
|
||||
|
||||
Add(preview, initialSetup);
|
||||
|
||||
return preview;
|
||||
}
|
||||
|
||||
|
||||
@@ -325,17 +325,17 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (otherActor == ignoreActor)
|
||||
return false;
|
||||
|
||||
// If the check allows: We are not blocked by units that we can force to move out of the way.
|
||||
if (check <= BlockedByActor.Immovable && cellFlag.HasCellFlag(CellFlag.HasMovableActor) &&
|
||||
var otherMobile = otherActor.OccupiesSpace as Mobile;
|
||||
var otherIsMovable = otherMobile != null && !otherMobile.IsTraitDisabled && !otherMobile.IsTraitPaused && !otherMobile.IsImmovable;
|
||||
var otherIsMoving = otherIsMovable && otherMobile.CurrentMovementTypes.HasMovementType(MovementType.Horizontal);
|
||||
|
||||
// If the check allows: We are not blocked by allied units that we can force to move out of the way.
|
||||
if (check <= BlockedByActor.Immovable && cellFlag.HasCellFlag(CellFlag.HasMovableActor) && otherIsMovable &&
|
||||
actor.Owner.RelationshipWith(otherActor.Owner) == PlayerRelationship.Ally)
|
||||
{
|
||||
if (otherActor.OccupiesSpace is Mobile mobile && !mobile.IsTraitDisabled && !mobile.IsTraitPaused && !mobile.IsImmovable)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
// If the check allows: we are not blocked by moving units.
|
||||
if (check <= BlockedByActor.Stationary && cellFlag.HasCellFlag(CellFlag.HasMovingActor) &&
|
||||
otherActor.OccupiesSpace is Mobile otherMobile && otherMobile.CurrentMovementTypes.HasMovementType(MovementType.Horizontal))
|
||||
if (check <= BlockedByActor.Stationary && cellFlag.HasCellFlag(CellFlag.HasMovingActor) && otherIsMoving)
|
||||
return false;
|
||||
|
||||
if (cellFlag.HasCellFlag(CellFlag.HasTemporaryBlocker))
|
||||
|
||||
@@ -35,10 +35,13 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
var actorReference = new ActorReference(kv.Value.Value, kv.Value.ToDictionary());
|
||||
|
||||
// If there is no real player associated, don't spawn it.
|
||||
var ownerName = actorReference.Get<OwnerInit>().InternalName;
|
||||
if (!world.Players.Any(p => p.InternalName == ownerName))
|
||||
continue;
|
||||
// If an actor's doesn't have a valid owner transfer ownership to neutral
|
||||
var ownerInit = actorReference.Get<OwnerInit>();
|
||||
if (!world.Players.Any(p => p.InternalName == ownerInit.InternalName))
|
||||
{
|
||||
actorReference.Remove(ownerInit);
|
||||
actorReference.Add(new OwnerInit(world.WorldActor.Owner));
|
||||
}
|
||||
|
||||
actorReference.Add(new SkipMakeAnimsInit());
|
||||
actorReference.Add(new SpawnedByMapInit(kv.Key));
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.Mods.Common.UpdateRules.Rules
|
||||
{
|
||||
public class AddControlGroups : UpdateRule
|
||||
{
|
||||
public override string Name => "Add new ControlGroups trait.";
|
||||
|
||||
public override string Description => "A new trait ControlGroups was added, splitting logic away from Selection.";
|
||||
|
||||
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
|
||||
{
|
||||
if (actorNode.ChildrenMatching("Selection").Any(x => !x.IsRemoval())
|
||||
&& !actorNode.ChildrenMatching("ControlGroups").Any())
|
||||
actorNode.AddNode(new MiniYamlNode("ControlGroups", ""));
|
||||
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2022 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 RemoveDomainIndex : UpdateRule
|
||||
{
|
||||
public override string Name => "Remove DomainIndex from World and add path finder overlays.";
|
||||
|
||||
public override string Description => "The DomainIndex trait was removed from World. Two overlay traits were added at the same time.";
|
||||
|
||||
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
|
||||
{
|
||||
if (actorNode.RemoveNodes("DomainIndex") > 0)
|
||||
{
|
||||
actorNode.AddNode(new MiniYamlNode("PathFinderOverlay", ""));
|
||||
actorNode.AddNode(new MiniYamlNode("HierarchicalPathFinderOverlay", ""));
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,27 +11,88 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.UpdateRules.Rules
|
||||
{
|
||||
public class UnhardcodeBaseBuilderBotModule : UpdateRule
|
||||
{
|
||||
MiniYamlNode defences;
|
||||
|
||||
// Excludes AttackBomber and AttackTDGunboatTurreted as actors with these AttackBase traits aren't supposed to be controlled.
|
||||
readonly string[] attackBase = { "AttackLeap", "AttackPopupTurreted", "AttackAircraft", "AttackTesla", "AttackCharges", "AttackFollow", "AttackTurreted", "AttackFrontal", "AttackGarrisoned", "AttackOmni", "AttackSwallow" };
|
||||
readonly string[] buildings = { "Building", "EnergyWall", "D2kBuilding" };
|
||||
|
||||
bool anyAdded;
|
||||
|
||||
public override string Name => "BaseBuilderBotModule got new fields to configure buildings that are defenses.";
|
||||
|
||||
public override string Description => "DefenseTypes were added.";
|
||||
|
||||
public override IEnumerable<string> BeforeUpdateActors(ModData modData, List<MiniYamlNode> resolvedActors)
|
||||
{
|
||||
var defences = new List<string>();
|
||||
|
||||
foreach (var actor in resolvedActors)
|
||||
{
|
||||
if (actor.Key.StartsWith('^'))
|
||||
continue;
|
||||
|
||||
var isBuildable = false;
|
||||
var isBuilding = false;
|
||||
var canAttack = false;
|
||||
|
||||
foreach (var trait in actor.Value.Nodes)
|
||||
{
|
||||
if (trait.IsRemoval())
|
||||
continue;
|
||||
|
||||
if (trait.KeyMatches("Buildable", includeRemovals: false))
|
||||
{
|
||||
isBuildable = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buildings.Any(v => trait.KeyMatches(v, includeRemovals: false)))
|
||||
{
|
||||
isBuilding = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (attackBase.Any(ab => trait.KeyMatches(ab, includeRemovals: false)))
|
||||
canAttack = true;
|
||||
}
|
||||
|
||||
if (isBuildable && isBuilding && canAttack)
|
||||
{
|
||||
var name = actor.Key.ToLower();
|
||||
if (!defences.Contains(name))
|
||||
defences.Add(name);
|
||||
}
|
||||
}
|
||||
|
||||
this.defences = new MiniYamlNode("DefenseTypes", FieldSaver.FormatValue(defences));
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> AfterUpdate(ModData modData)
|
||||
{
|
||||
if (anyAdded)
|
||||
yield return "`BaseBuilderBotModule` was unhardcoded and a new field added: `DefenseTypes`. Please verify the automated changes.";
|
||||
|
||||
anyAdded = false;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
|
||||
{
|
||||
var addNodes = new List<MiniYamlNode>();
|
||||
|
||||
var defense = modData.DefaultRules.Actors.Values.Where(a => a.HasTraitInfo<BuildingInfo>() && a.HasTraitInfo<AttackBaseInfo>()).Select(a => a.Name);
|
||||
var defensetypes = new MiniYamlNode("DefenseTypes", FieldSaver.FormatValue(defense.ToList()));
|
||||
addNodes.Add(defensetypes);
|
||||
|
||||
foreach (var baseBuilderManager in actorNode.ChildrenMatching("BaseBuilderBotModule"))
|
||||
foreach (var addNode in addNodes)
|
||||
baseBuilderManager.AddNode(addNode);
|
||||
foreach (var squadManager in actorNode.ChildrenMatching("BaseBuilderBotModule", includeRemovals: false))
|
||||
{
|
||||
if (!squadManager.ChildrenMatching(defences.Key, includeRemovals: false).Any())
|
||||
{
|
||||
squadManager.AddNode(defences);
|
||||
anyAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
@@ -11,33 +11,132 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.UpdateRules.Rules
|
||||
{
|
||||
public class UnhardcodeSquadManager : UpdateRule
|
||||
{
|
||||
readonly List<MiniYamlNode> addNodes = new List<MiniYamlNode>();
|
||||
|
||||
// Excludes AttackBomber and AttackTDGunboatTurreted as actors with these AttackBase traits aren't supposed to be controlled.
|
||||
readonly string[] attackBase = { "AttackLeap", "AttackPopupTurreted", "AttackAircraft", "AttackTesla", "AttackCharges", "AttackFollow", "AttackTurreted", "AttackFrontal", "AttackGarrisoned", "AttackOmni", "AttackSwallow" };
|
||||
readonly string[] vipsNames = { "Harvester", "BaseBuilding" };
|
||||
readonly string[] buildings = { "Building", "EnergyWall", "D2kBuilding" };
|
||||
readonly string[] excludedBuildings = { "LineBuild", "Plug" };
|
||||
|
||||
public override string Name => "SquadManagerBotModule got new fields to configure ground attacks and defensive actions.";
|
||||
|
||||
public override string Description => "AirUnitsTypes and ProtectionTypes were added.";
|
||||
|
||||
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
|
||||
public override IEnumerable<string> BeforeUpdateActors(ModData modData, List<MiniYamlNode> resolvedActors)
|
||||
{
|
||||
var addNodes = new List<MiniYamlNode>();
|
||||
var aircraft = new List<string>();
|
||||
var vips = new List<string>();
|
||||
|
||||
var aircraft = modData.DefaultRules.Actors.Values.Where(a => a.HasTraitInfo<AircraftInfo>() && a.HasTraitInfo<AttackBaseInfo>()).Select(a => a.Name);
|
||||
var airUnits = new MiniYamlNode("AirUnitsTypes", FieldSaver.FormatValue(aircraft.ToList()));
|
||||
addNodes.Add(airUnits);
|
||||
foreach (var actor in resolvedActors)
|
||||
{
|
||||
if (actor.Key.StartsWith('^'))
|
||||
continue;
|
||||
|
||||
var vips = modData.DefaultRules.Actors.Values.Where(a => a.HasTraitInfo<HarvesterInfo>() || a.HasTraitInfo<BaseBuildingInfo>() || (a.HasTraitInfo<BuildingInfo>() && a.HasTraitInfo<BuildableInfo>() && !a.HasTraitInfo<LineBuildInfo>() && !a.HasTraitInfo<PlugInfo>())).Select(a => a.Name);
|
||||
var protection = new MiniYamlNode("ProtectionTypes", FieldSaver.FormatValue(vips.ToList()));
|
||||
addNodes.Add(protection);
|
||||
var isVip = false;
|
||||
var isBuildable = false;
|
||||
var isBuilding = false;
|
||||
var isAircraft = false;
|
||||
var isExcluded = false;
|
||||
var canAttack = false;
|
||||
var isKillable = false;
|
||||
|
||||
foreach (var squadManager in actorNode.ChildrenMatching("SquadManagerBotModule"))
|
||||
foreach (var addNode in addNodes)
|
||||
squadManager.AddNode(addNode);
|
||||
foreach (var trait in actor.Value.Nodes)
|
||||
{
|
||||
if (trait.IsRemoval())
|
||||
continue;
|
||||
|
||||
if (trait.KeyMatches("Buildable", includeRemovals: false))
|
||||
{
|
||||
isBuildable = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trait.KeyMatches("Aircraft", includeRemovals: false))
|
||||
{
|
||||
isAircraft = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trait.KeyMatches("Health", includeRemovals: false))
|
||||
{
|
||||
isKillable = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vipsNames.Any(v => trait.KeyMatches(v, includeRemovals: false)))
|
||||
{
|
||||
isVip = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buildings.Any(b => trait.KeyMatches(b, includeRemovals: false)))
|
||||
{
|
||||
isBuilding = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (excludedBuildings.Any(eb => trait.KeyMatches(eb, includeRemovals: false)))
|
||||
{
|
||||
isExcluded = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (attackBase.Any(ab => trait.KeyMatches(ab, includeRemovals: false)))
|
||||
canAttack = true;
|
||||
}
|
||||
|
||||
if (isAircraft && isBuildable && canAttack && isKillable)
|
||||
{
|
||||
var name = actor.Key.ToLower();
|
||||
if (!aircraft.Contains(name))
|
||||
aircraft.Add(name);
|
||||
}
|
||||
|
||||
if (isBuildable && isKillable && (isVip || (isBuilding && !isExcluded)))
|
||||
{
|
||||
var name = actor.Key.ToLower();
|
||||
if (!vips.Contains(name))
|
||||
vips.Add(name);
|
||||
}
|
||||
}
|
||||
|
||||
addNodes.Add(new MiniYamlNode("AirUnitsTypes", FieldSaver.FormatValue(aircraft)));
|
||||
addNodes.Add(new MiniYamlNode("ProtectionTypes", FieldSaver.FormatValue(vips)));
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
bool anyAdded = false;
|
||||
|
||||
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
|
||||
{
|
||||
foreach (var squadManager in actorNode.ChildrenMatching("SquadManagerBotModule", includeRemovals: false))
|
||||
{
|
||||
foreach (var addNode in addNodes)
|
||||
{
|
||||
if (!squadManager.ChildrenMatching(addNode.Key, includeRemovals: false).Any())
|
||||
{
|
||||
squadManager.AddNode(addNode);
|
||||
anyAdded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> AfterUpdate(ModData modData)
|
||||
{
|
||||
if (anyAdded)
|
||||
yield return "`SquadManagerBotModule` was unhardcoded and new fields added: `AirUnitsTypes` and `ProtectionTypes`. Please verify the automated changes.";
|
||||
|
||||
anyAdded = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,8 @@ namespace OpenRA.Mods.Common.UpdateRules
|
||||
new UnhardcodeBaseBuilderBotModule(),
|
||||
new UnhardcodeVeteranProductionIconOverlay(),
|
||||
new RenameContrailProperties(),
|
||||
new RemoveDomainIndex(),
|
||||
new AddControlGroups(),
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
@@ -36,5 +36,9 @@ namespace OpenRA.Mods.Common.UpdateRules
|
||||
|
||||
public virtual IEnumerable<string> BeforeUpdate(ModData modData) { yield break; }
|
||||
public virtual IEnumerable<string> AfterUpdate(ModData modData) { yield break; }
|
||||
|
||||
public virtual IEnumerable<string> BeforeUpdateActors(ModData modData, List<MiniYamlNode> resolvedActors) { yield break; }
|
||||
public virtual IEnumerable<string> BeforeUpdateWeapons(ModData modData, List<MiniYamlNode> resolvedWeapons) { yield break; }
|
||||
public virtual IEnumerable<string> BeforeUpdateSequences(ModData modData, List<MiniYamlNode> resolvedImages) { yield break; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +114,9 @@ namespace OpenRA.Mods.Common.UpdateRules
|
||||
var mapRulesNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Rules");
|
||||
if (mapRulesNode != null)
|
||||
{
|
||||
var resolvedActors = LoadMapYaml(modData.DefaultFileSystem, mapPackage, modData.Manifest.Rules, mapRulesNode.Value);
|
||||
manualSteps.AddRange(rule.BeforeUpdateActors(modData, resolvedActors));
|
||||
|
||||
var mapRules = LoadInternalMapYaml(modData, mapPackage, mapRulesNode.Value, externalFilenames);
|
||||
manualSteps.AddRange(ApplyTopLevelTransform(modData, mapRules, rule.UpdateActorNode));
|
||||
files.AddRange(mapRules);
|
||||
@@ -122,6 +125,9 @@ namespace OpenRA.Mods.Common.UpdateRules
|
||||
var mapWeaponsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Weapons");
|
||||
if (mapWeaponsNode != null)
|
||||
{
|
||||
var resolvedWeapons = LoadMapYaml(modData.DefaultFileSystem, mapPackage, modData.Manifest.Weapons, mapWeaponsNode.Value);
|
||||
manualSteps.AddRange(rule.BeforeUpdateWeapons(modData, resolvedWeapons));
|
||||
|
||||
var mapWeapons = LoadInternalMapYaml(modData, mapPackage, mapWeaponsNode.Value, externalFilenames);
|
||||
manualSteps.AddRange(ApplyTopLevelTransform(modData, mapWeapons, rule.UpdateWeaponNode));
|
||||
files.AddRange(mapWeapons);
|
||||
@@ -130,8 +136,11 @@ namespace OpenRA.Mods.Common.UpdateRules
|
||||
var mapSequencesNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Sequences");
|
||||
if (mapSequencesNode != null)
|
||||
{
|
||||
var resolvedImages = LoadMapYaml(modData.DefaultFileSystem, mapPackage, modData.Manifest.Sequences, mapSequencesNode.Value);
|
||||
manualSteps.AddRange(rule.BeforeUpdateSequences(modData, resolvedImages));
|
||||
|
||||
var mapSequences = LoadInternalMapYaml(modData, mapPackage, mapSequencesNode.Value, externalFilenames);
|
||||
manualSteps.AddRange(ApplyTopLevelTransform(modData, mapSequences, rule.UpdateWeaponNode));
|
||||
manualSteps.AddRange(ApplyTopLevelTransform(modData, mapSequences, rule.UpdateSequenceNode));
|
||||
files.AddRange(mapSequences);
|
||||
}
|
||||
|
||||
@@ -141,6 +150,29 @@ namespace OpenRA.Mods.Common.UpdateRules
|
||||
return manualSteps;
|
||||
}
|
||||
|
||||
public static List<MiniYamlNode> LoadMapYaml(IReadOnlyFileSystem fileSystem, IReadOnlyPackage mapPackage, IEnumerable<string> files, MiniYaml mapNode)
|
||||
{
|
||||
var yaml = files.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s)).ToList();
|
||||
|
||||
if (mapNode != null && mapNode.Value != null)
|
||||
{
|
||||
var mapFiles = FieldLoader.GetValue<string[]>("value", mapNode.Value);
|
||||
yaml.AddRange(mapFiles.Select(filename =>
|
||||
{
|
||||
// Explicit package paths never refer to a map
|
||||
if (!filename.Contains('|') && mapPackage.Contains(filename))
|
||||
return MiniYaml.FromStream(mapPackage.GetStream(filename));
|
||||
|
||||
return MiniYaml.FromStream(fileSystem.Open(filename));
|
||||
}));
|
||||
}
|
||||
|
||||
if (mapNode != null && mapNode.Nodes.Count > 0)
|
||||
yaml.Add(mapNode.Nodes);
|
||||
|
||||
return MiniYaml.Merge(yaml);
|
||||
}
|
||||
|
||||
static IEnumerable<string> FilterExternalModFiles(ModData modData, IEnumerable<string> files, HashSet<string> externalFilenames)
|
||||
{
|
||||
foreach (var f in files)
|
||||
@@ -196,9 +228,19 @@ namespace OpenRA.Mods.Common.UpdateRules
|
||||
}
|
||||
|
||||
manualSteps.AddRange(rule.BeforeUpdate(modData));
|
||||
|
||||
var resolvedActors = MiniYaml.Load(modData.DefaultFileSystem, modData.Manifest.Rules, null);
|
||||
manualSteps.AddRange(rule.BeforeUpdateActors(modData, resolvedActors));
|
||||
manualSteps.AddRange(ApplyTopLevelTransform(modData, modRules, rule.UpdateActorNode));
|
||||
|
||||
var resolvedWeapons = MiniYaml.Load(modData.DefaultFileSystem, modData.Manifest.Weapons, null);
|
||||
manualSteps.AddRange(rule.BeforeUpdateWeapons(modData, resolvedWeapons));
|
||||
manualSteps.AddRange(ApplyTopLevelTransform(modData, modWeapons, rule.UpdateWeaponNode));
|
||||
|
||||
var resolvedSequences = MiniYaml.Load(modData.DefaultFileSystem, modData.Manifest.Sequences, null);
|
||||
manualSteps.AddRange(rule.BeforeUpdateSequences(modData, resolvedSequences));
|
||||
manualSteps.AddRange(ApplyTopLevelTransform(modData, modSequences, rule.UpdateSequenceNode));
|
||||
|
||||
manualSteps.AddRange(ApplyTopLevelTransform(modData, modTilesets, rule.UpdateTilesetNode));
|
||||
manualSteps.AddRange(ApplyChromeTransform(modData, modChromeLayout, rule.UpdateChromeNode));
|
||||
manualSteps.AddRange(ApplyTopLevelTransform(modData, modChromeProvider, rule.UpdateChromeProviderNode));
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
|
||||
{
|
||||
var knownKeys = new HashSet<TKey>();
|
||||
foreach (TSource element in source)
|
||||
foreach (var element in source)
|
||||
{
|
||||
if (knownKeys.Add(keySelector(element)))
|
||||
yield return element;
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
return PrintUsage();
|
||||
|
||||
var invalid = false;
|
||||
for (int i = 1; i < args.Length; i++)
|
||||
for (var i = 1; i < args.Length; i++)
|
||||
{
|
||||
var parts = args[i].Split(Comma, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length != 3 && parts.Length != 4)
|
||||
@@ -75,7 +75,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
[Desc("Convert r,g,b[,a] triples/quads into hex colors")]
|
||||
void IUtilityCommand.Run(Utility utility, string[] args)
|
||||
{
|
||||
for (int i = 1; i < args.Length;)
|
||||
for (var i = 1; i < args.Length;)
|
||||
{
|
||||
var parts = args[i].Split(Comma, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length == 3)
|
||||
@@ -113,7 +113,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
return PrintUsage();
|
||||
|
||||
var invalid = false;
|
||||
for (int i = 1; i < args.Length; i++)
|
||||
for (var i = 1; i < args.Length; i++)
|
||||
{
|
||||
var parts = args[i].Split(Comma, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length != 3 && parts.Length != 4)
|
||||
@@ -180,7 +180,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
[Desc("Convert a,r,g,b legacy colors into hex colors")]
|
||||
void IUtilityCommand.Run(Utility utility, string[] args)
|
||||
{
|
||||
for (int i = 1; i < args.Length;)
|
||||
for (var i = 1; i < args.Length;)
|
||||
{
|
||||
var parts = args[i].Split(Comma, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length == 3)
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
// Equivalent to OnMouseUp, but without an input arg
|
||||
public Action OnClick = () => { };
|
||||
public Action OnDoubleClick = () => { };
|
||||
public Action OnDoubleClick = null;
|
||||
public Action<KeyInput> OnKeyPress = _ => { };
|
||||
|
||||
public string Cursor = ChromeMetrics.Get<string>("ButtonCursor");
|
||||
@@ -163,7 +163,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
return false;
|
||||
|
||||
var disabled = IsDisabled();
|
||||
if (HasMouseFocus && mi.Event == MouseInputEvent.Up && mi.MultiTapCount == 2)
|
||||
if (HasMouseFocus && mi.Event == MouseInputEvent.Up && mi.MultiTapCount == 2 && OnDoubleClick != null)
|
||||
{
|
||||
if (!disabled)
|
||||
{
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
widget.Get<LabelWidget>("CONNECTING_DESC").GetText = () => connectingDescText;
|
||||
|
||||
var connectionError = widget.Get<LabelWidget>("CONNECTION_ERROR");
|
||||
var connectionErrorText = modData.Translation.GetString(orderManager.ServerError) ?? connection.ErrorMessage ?? modData.Translation.GetString(UnknownError);
|
||||
var connectionErrorText = orderManager.ServerError != null ? modData.Translation.GetString(orderManager.ServerError) : connection.ErrorMessage ?? modData.Translation.GetString(UnknownError);
|
||||
connectionError.GetText = () => connectionErrorText;
|
||||
|
||||
var panelTitle = widget.Get<LabelWidget>("TITLE");
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
readonly BackgroundWidget actorEditPanel;
|
||||
readonly LabelWidget typeLabel;
|
||||
readonly TextFieldWidget actorIDField;
|
||||
readonly HashSet<TextFieldWidget> typableFields = new HashSet<TextFieldWidget>();
|
||||
readonly LabelWidget actorIDErrorLabel;
|
||||
|
||||
readonly Widget initContainer;
|
||||
@@ -328,6 +329,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
if (float.TryParse(valueField.Text, out var result))
|
||||
slider.UpdateValue(result);
|
||||
};
|
||||
|
||||
valueField.OnEscKey = _ => { valueField.YieldKeyboardFocus(); return true; };
|
||||
valueField.OnEnterKey = _ => { valueField.YieldKeyboardFocus(); return true; };
|
||||
typableFields.Add(valueField);
|
||||
}
|
||||
|
||||
initContainer.AddChild(sliderContainer);
|
||||
@@ -372,11 +377,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
actorEditPanel.Bounds.X = origin.X + editPanelPadding;
|
||||
actorEditPanel.Bounds.Y = origin.Y;
|
||||
}
|
||||
else
|
||||
else if (CurrentActor != null)
|
||||
{
|
||||
// Selected actor is null, hide the border and edit panel.
|
||||
actorIDField.YieldKeyboardFocus();
|
||||
CurrentActor = null;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,6 +406,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
void Close()
|
||||
{
|
||||
actorIDField.YieldKeyboardFocus();
|
||||
foreach (var f in typableFields)
|
||||
f.YieldKeyboardFocus();
|
||||
|
||||
editor.DefaultBrush.SelectedActor = null;
|
||||
CurrentActor = null;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Widgets;
|
||||
@@ -25,41 +26,27 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
|
||||
var visibilityCheckbox = widget.GetOrNull<CheckboxWidget>("DISABLE_VISIBILITY_CHECKS");
|
||||
if (visibilityCheckbox != null)
|
||||
{
|
||||
visibilityCheckbox.IsChecked = () => devTrait.DisableShroud;
|
||||
visibilityCheckbox.OnClick = () => Order(world, "DevVisibility");
|
||||
}
|
||||
BindOrderCheckbox(visibilityCheckbox, world, "DevVisibility", () => devTrait.DisableShroud);
|
||||
|
||||
var pathCheckbox = widget.GetOrNull<CheckboxWidget>("SHOW_UNIT_PATHS");
|
||||
if (pathCheckbox != null)
|
||||
{
|
||||
pathCheckbox.IsChecked = () => devTrait.PathDebug;
|
||||
pathCheckbox.OnClick = () => Order(world, "DevPathDebug");
|
||||
}
|
||||
BindOrderCheckbox(pathCheckbox, world, "DevPathDebug", () => devTrait.PathDebug);
|
||||
|
||||
var cashButton = widget.GetOrNull<ButtonWidget>("GIVE_CASH");
|
||||
if (cashButton != null)
|
||||
cashButton.OnClick = () =>
|
||||
world.IssueOrder(new Order("DevGiveCash", world.LocalPlayer.PlayerActor, false));
|
||||
cashButton.OnClick = () => IssueOrder(world, "DevGiveCash");
|
||||
|
||||
var growResourcesButton = widget.GetOrNull<ButtonWidget>("GROW_RESOURCES");
|
||||
if (growResourcesButton != null)
|
||||
growResourcesButton.OnClick = () =>
|
||||
world.IssueOrder(new Order("DevGrowResources", world.LocalPlayer.PlayerActor, false));
|
||||
growResourcesButton.OnClick = () => IssueOrder(world, "DevGrowResources");
|
||||
|
||||
var fastBuildCheckbox = widget.GetOrNull<CheckboxWidget>("INSTANT_BUILD");
|
||||
if (fastBuildCheckbox != null)
|
||||
{
|
||||
fastBuildCheckbox.IsChecked = () => devTrait.FastBuild;
|
||||
fastBuildCheckbox.OnClick = () => Order(world, "DevFastBuild");
|
||||
}
|
||||
BindOrderCheckbox(fastBuildCheckbox, world, "DevFastBuild", () => devTrait.FastBuild);
|
||||
|
||||
var fastChargeCheckbox = widget.GetOrNull<CheckboxWidget>("INSTANT_CHARGE");
|
||||
if (fastChargeCheckbox != null)
|
||||
{
|
||||
fastChargeCheckbox.IsChecked = () => devTrait.FastCharge;
|
||||
fastChargeCheckbox.OnClick = () => Order(world, "DevFastCharge");
|
||||
}
|
||||
BindOrderCheckbox(fastChargeCheckbox, world, "DevFastCharge", () => devTrait.FastCharge);
|
||||
|
||||
var showCombatCheckbox = widget.GetOrNull<CheckboxWidget>("SHOW_COMBATOVERLAY");
|
||||
if (showCombatCheckbox != null)
|
||||
@@ -103,34 +90,23 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
|
||||
var allTechCheckbox = widget.GetOrNull<CheckboxWidget>("ENABLE_TECH");
|
||||
if (allTechCheckbox != null)
|
||||
{
|
||||
allTechCheckbox.IsChecked = () => devTrait.AllTech;
|
||||
allTechCheckbox.OnClick = () => Order(world, "DevEnableTech");
|
||||
}
|
||||
BindOrderCheckbox(allTechCheckbox, world, "DevEnableTech", () => devTrait.AllTech);
|
||||
|
||||
var powerCheckbox = widget.GetOrNull<CheckboxWidget>("UNLIMITED_POWER");
|
||||
if (powerCheckbox != null)
|
||||
{
|
||||
powerCheckbox.IsChecked = () => devTrait.UnlimitedPower;
|
||||
powerCheckbox.OnClick = () => Order(world, "DevUnlimitedPower");
|
||||
}
|
||||
BindOrderCheckbox(powerCheckbox, world, "DevUnlimitedPower", () => devTrait.UnlimitedPower);
|
||||
|
||||
var buildAnywhereCheckbox = widget.GetOrNull<CheckboxWidget>("BUILD_ANYWHERE");
|
||||
if (buildAnywhereCheckbox != null)
|
||||
{
|
||||
buildAnywhereCheckbox.IsChecked = () => devTrait.BuildAnywhere;
|
||||
buildAnywhereCheckbox.OnClick = () => Order(world, "DevBuildAnywhere");
|
||||
}
|
||||
BindOrderCheckbox(buildAnywhereCheckbox, world, "DevBuildAnywhere", () => devTrait.BuildAnywhere);
|
||||
|
||||
var explorationButton = widget.GetOrNull<ButtonWidget>("GIVE_EXPLORATION");
|
||||
if (explorationButton != null)
|
||||
explorationButton.OnClick = () =>
|
||||
world.IssueOrder(new Order("DevGiveExploration", world.LocalPlayer.PlayerActor, false));
|
||||
explorationButton.OnClick = () => IssueOrder(world, "DevGiveExploration");
|
||||
|
||||
var noexplorationButton = widget.GetOrNull<ButtonWidget>("RESET_EXPLORATION");
|
||||
if (noexplorationButton != null)
|
||||
noexplorationButton.OnClick = () =>
|
||||
world.IssueOrder(new Order("DevResetExploration", world.LocalPlayer.PlayerActor, false));
|
||||
noexplorationButton.OnClick = () => IssueOrder(world, "DevResetExploration");
|
||||
|
||||
var showActorTagsCheckbox = widget.GetOrNull<CheckboxWidget>("SHOW_ACTOR_TAGS");
|
||||
if (showActorTagsCheckbox != null)
|
||||
@@ -153,7 +129,18 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
}
|
||||
}
|
||||
|
||||
public static void Order(World world, string order)
|
||||
static void BindOrderCheckbox(CheckboxWidget checkbox, World world, string order, Func<bool> getValue)
|
||||
{
|
||||
var isChecked = new PredictedCachedTransform<bool, bool>(state => state);
|
||||
checkbox.IsChecked = () => isChecked.Update(getValue());
|
||||
checkbox.OnClick = () =>
|
||||
{
|
||||
isChecked.Predict(!getValue());
|
||||
IssueOrder(world, order);
|
||||
};
|
||||
}
|
||||
|
||||
public static void IssueOrder(World world, string order)
|
||||
{
|
||||
world.IssueOrder(new Order(order, world.LocalPlayer.PlayerActor, false));
|
||||
}
|
||||
|
||||
@@ -448,6 +448,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
var harvesters = template.Get<LabelWidget>("HARVESTERS");
|
||||
harvesters.GetText = () => world.ActorsWithTrait<Harvester>().Count(a => a.Actor.Owner == player && !a.Actor.IsDead && !a.Trait.IsTraitDisabled).ToString();
|
||||
|
||||
var carryalls = template.GetOrNull<LabelWidget>("CARRYALLS");
|
||||
if (carryalls != null)
|
||||
carryalls.GetText = () => world.ActorsWithTrait<AutoCarryall>().Count(a => a.Actor.Owner == player && !a.Actor.IsDead).ToString();
|
||||
|
||||
var derricks = template.GetOrNull<LabelWidget>("DERRICKS");
|
||||
if (derricks != null)
|
||||
derricks.GetText = () => world.ActorsHavingTrait<UpdatesDerrickCount>().Count(a => a.Owner == player && !a.IsDead).ToString();
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
var dataTotal = 0.0f;
|
||||
var mag = 0;
|
||||
var dataSuffix = "";
|
||||
var host = downloadHost ?? UnknownHost;
|
||||
var host = downloadHost ?? modData.Translation.GetString(UnknownHost);
|
||||
|
||||
if (total < 0)
|
||||
{
|
||||
@@ -139,11 +139,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
|
||||
Action<string> onError = s => Game.RunAfterTick(() =>
|
||||
{
|
||||
Log.Write("install", "Download failed: " + s);
|
||||
var host = downloadHost ?? modData.Translation.GetString(UnknownHost);
|
||||
Log.Write("install", $"Download from {host} failed: " + s);
|
||||
|
||||
progressBar.Indeterminate = false;
|
||||
progressBar.Percentage = 100;
|
||||
getStatusText = () => "Error: " + s;
|
||||
getStatusText = () => $"{host}: Error: {s}";
|
||||
retryButton.IsVisible = () => true;
|
||||
cancelButton.OnClick = Ui.CloseWindow;
|
||||
});
|
||||
|
||||
@@ -101,18 +101,25 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
}
|
||||
|
||||
var checkbox = checkboxColumns.Dequeue();
|
||||
var optionValue = new CachedTransform<Session.Global, Session.LobbyOptionState>(
|
||||
gs => gs.LobbyOptions[option.Id]);
|
||||
var optionEnabled = new PredictedCachedTransform<Session.Global, bool>(
|
||||
gs => gs.LobbyOptions[option.Id].IsEnabled);
|
||||
|
||||
var optionLocked = new CachedTransform<Session.Global, bool>(
|
||||
gs => gs.LobbyOptions[option.Id].IsLocked);
|
||||
|
||||
checkbox.GetText = () => option.Name;
|
||||
if (option.Description != null)
|
||||
checkbox.GetTooltipText = () => option.Description;
|
||||
|
||||
checkbox.IsVisible = () => true;
|
||||
checkbox.IsChecked = () => optionValue.Update(orderManager.LobbyInfo.GlobalSettings).IsEnabled;
|
||||
checkbox.IsDisabled = () => configurationDisabled() || optionValue.Update(orderManager.LobbyInfo.GlobalSettings).IsLocked;
|
||||
checkbox.OnClick = () => orderManager.IssueOrder(Order.Command(
|
||||
$"option {option.Id} {!optionValue.Update(orderManager.LobbyInfo.GlobalSettings).IsEnabled}"));
|
||||
checkbox.IsChecked = () => optionEnabled.Update(orderManager.LobbyInfo.GlobalSettings);
|
||||
checkbox.IsDisabled = () => configurationDisabled() || optionLocked.Update(orderManager.LobbyInfo.GlobalSettings);
|
||||
checkbox.OnClick = () =>
|
||||
{
|
||||
var state = !optionEnabled.Update(orderManager.LobbyInfo.GlobalSettings);
|
||||
orderManager.IssueOrder(Order.Command($"option {option.Id} {state}"));
|
||||
optionEnabled.Predict(state);
|
||||
};
|
||||
}
|
||||
|
||||
foreach (var option in allOptions.Where(o => !(o is LobbyBooleanOption)))
|
||||
|
||||
@@ -657,12 +657,21 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
public static void SetupEditableReadyWidget(Widget parent, Session.Client c, OrderManager orderManager, MapPreview map, bool isEnabled)
|
||||
{
|
||||
var status = parent.Get<CheckboxWidget>("STATUS_CHECKBOX");
|
||||
status.IsChecked = () => orderManager.LocalClient.IsReady || c.Bot != null;
|
||||
status.IsVisible = () => true;
|
||||
status.IsDisabled = () => c.Bot != null || map.Status != MapStatus.Available || !isEnabled;
|
||||
|
||||
var state = orderManager.LocalClient.IsReady ? Session.ClientState.NotReady : Session.ClientState.Ready;
|
||||
status.OnClick = () => orderManager.IssueOrder(Order.Command($"state {state}"));
|
||||
if (c.Bot == null)
|
||||
{
|
||||
var isChecked = new PredictedCachedTransform<Session.Client, bool>(cc => cc.IsReady);
|
||||
status.IsChecked = () => isChecked.Update(c);
|
||||
status.OnClick = () =>
|
||||
{
|
||||
var state = isChecked.Update(c) ? Session.ClientState.NotReady : Session.ClientState.Ready;
|
||||
orderManager.IssueOrder(Order.Command($"state {state}"));
|
||||
isChecked.Predict(!c.IsReady);
|
||||
};
|
||||
}
|
||||
else
|
||||
status.IsChecked = () => true;
|
||||
}
|
||||
|
||||
public static void SetupReadyWidget(Widget parent, Session.Client c)
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
public class SystemInfoPromptLogic : ChromeLogic
|
||||
{
|
||||
// Increment the version number when adding new stats
|
||||
const int SystemInformationVersion = 4;
|
||||
const int SystemInformationVersion = 5;
|
||||
|
||||
static Dictionary<string, (string Label, string Value)> GetSystemInformation()
|
||||
{
|
||||
@@ -29,6 +29,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
{ "id", ("Anonymous ID", Game.Settings.Debug.UUID) },
|
||||
{ "platform", ("OS Type", Platform.CurrentPlatform.ToString()) },
|
||||
{ "arch", ("Architecture", Platform.CurrentArchitecture.ToString()) },
|
||||
{ "os", ("OS Version", Environment.OSVersion.ToString()) },
|
||||
{ "x64", ("OS is 64 bit", Environment.Is64BitOperatingSystem.ToString()) },
|
||||
{ "x64process", ("Process is 64 bit", Environment.Is64BitProcess.ToString()) },
|
||||
|
||||
@@ -188,7 +188,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
var animations = new ModelAnimation[] { animation };
|
||||
|
||||
ModelPreview preview = new ModelPreview(
|
||||
var preview = new ModelPreview(
|
||||
new ModelAnimation[] { animation }, WVec.Zero, 0,
|
||||
cachedScale,
|
||||
new WAngle(cachedLightPitch),
|
||||
|
||||
@@ -419,4 +419,40 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
return lastOutput;
|
||||
}
|
||||
}
|
||||
|
||||
public class PredictedCachedTransform<T, U>
|
||||
{
|
||||
readonly Func<T, U> transform;
|
||||
|
||||
bool initialized;
|
||||
T lastInput;
|
||||
U lastOutput;
|
||||
|
||||
bool predicted;
|
||||
U prediction;
|
||||
|
||||
public PredictedCachedTransform(Func<T, U> transform)
|
||||
{
|
||||
this.transform = transform;
|
||||
}
|
||||
|
||||
public void Predict(U value)
|
||||
{
|
||||
predicted = true;
|
||||
prediction = value;
|
||||
}
|
||||
|
||||
public U Update(T input)
|
||||
{
|
||||
if ((predicted || initialized) && ((input == null && lastInput == null) || (input != null && input.Equals(lastInput))))
|
||||
return predicted ? prediction : lastOutput;
|
||||
|
||||
predicted = false;
|
||||
initialized = true;
|
||||
lastInput = input;
|
||||
lastOutput = transform(input);
|
||||
|
||||
return lastOutput;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj" />
|
||||
<PackageReference Include="OpenRA-Freetype6" Version="1.0.4" />
|
||||
<PackageReference Include="OpenRA-OpenAL-CS" Version="1.0.16" />
|
||||
<PackageReference Include="OpenRA-SDL2-CS" Version="1.0.31" />
|
||||
<PackageReference Include="OpenRA-Freetype6" Version="1.0.9" />
|
||||
<PackageReference Include="OpenRA-OpenAL-CS" Version="1.0.19" />
|
||||
<PackageReference Include="OpenRA-SDL2-CS" Version="1.0.36" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="OpenRA.Platforms.Default.dll.config" Condition="'$(TargetPlatform)' != 'win-x64' And '$(TargetPlatform)' != 'win-x86'">
|
||||
|
||||
@@ -279,8 +279,6 @@ namespace OpenRA.Platforms.Default
|
||||
else
|
||||
windowSize = new Size((int)(surfaceSize.Width / windowScale), (int)(surfaceSize.Height / windowScale));
|
||||
|
||||
Console.WriteLine("Using window scale {0:F2}", windowScale);
|
||||
|
||||
if (Game.Settings.Game.LockMouseWindow)
|
||||
GrabWindowMouseFocus();
|
||||
else
|
||||
@@ -307,7 +305,32 @@ namespace OpenRA.Platforms.Default
|
||||
{
|
||||
SDL.SDL_SetWindowFullscreen(Window, (uint)SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
SDL.SDL_SetHint(SDL.SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
|
||||
|
||||
if (Platform.CurrentPlatform == PlatformType.OSX)
|
||||
{
|
||||
// Activating SDL_WINDOW_FULLSCREEN_DESKTOP on a display with a notch will automatically
|
||||
// reduce the window height and align the top-left of the window to the safe area.
|
||||
//
|
||||
// SDL (as of version 2.26) does not contain an API to query the safeAreaInsets before
|
||||
// the window is created. We work around this by checking the window height after going
|
||||
// fullscreen, and recalculating our sizes to match the new window geometry.
|
||||
//
|
||||
// This workaround will become redundant once window resizing is implemented.
|
||||
SDL.SDL_GetWindowSize(Window, out var width, out var height);
|
||||
if (height != windowSize.Height)
|
||||
{
|
||||
windowSize = new Size(width, height);
|
||||
|
||||
SDL.SDL_GL_GetDrawableSize(Window, out width, out height);
|
||||
surfaceSize = new Size(width, height);
|
||||
windowScale = width * 1f / windowSize.Width;
|
||||
|
||||
Console.WriteLine($"Using new resolution: {windowSize.Width}x{windowSize.Height}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"Using window scale {windowScale:F2}");
|
||||
}
|
||||
|
||||
// Run graphics rendering on a dedicated thread.
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenRA-SDL2-CS" Version="1.0.31" />
|
||||
<PackageReference Include="OpenRA-SDL2-CS" Version="1.0.36" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj" />
|
||||
|
||||
@@ -1549,7 +1549,7 @@ Container@PLAYER_WIDGETS:
|
||||
X: WINDOW_RIGHT - WIDTH - 5
|
||||
Y: 5
|
||||
Width: 230
|
||||
Height: 291
|
||||
Height: 314
|
||||
ImageCollection: sidebar
|
||||
ImageName: background-sidebar
|
||||
ClickThrough: false
|
||||
|
||||
@@ -84,7 +84,9 @@ Container@AUDIO_PANEL:
|
||||
Width: PARENT_RIGHT
|
||||
Height: 20
|
||||
Font: Regular
|
||||
Text: Mute Background Music
|
||||
Text: Mute Menu Music
|
||||
TooltipText: Mute background music when no specific track is playing
|
||||
TooltipContainer: SETTINGS_TOOLTIP_CONTAINER
|
||||
Container@MUSIC_VOLUME_CONTAINER:
|
||||
X: PARENT_RIGHT / 2 + 10
|
||||
Width: PARENT_RIGHT / 2 - 20
|
||||
|
||||
@@ -159,7 +159,9 @@ Container@DISPLAY_PANEL:
|
||||
Width: PARENT_RIGHT
|
||||
Height: 20
|
||||
Font: Regular
|
||||
Text: Player Stance Colors
|
||||
Text: Player Relationship Colors
|
||||
TooltipText: Change minimap and health bar colors based on relationship (own, enemy, ally, neutral)
|
||||
TooltipContainer: SETTINGS_TOOLTIP_CONTAINER
|
||||
Container@ROW:
|
||||
Width: PARENT_RIGHT - 24
|
||||
Height: 20
|
||||
@@ -173,6 +175,8 @@ Container@DISPLAY_PANEL:
|
||||
Height: 20
|
||||
Font: Regular
|
||||
Text: Show UI Feedback Notifications
|
||||
TooltipText: Show transient text notifications for UI events
|
||||
TooltipContainer: SETTINGS_TOOLTIP_CONTAINER
|
||||
Container@TRANSIENTS_CHECKBOX_CONTAINER:
|
||||
X: PARENT_RIGHT / 2 + 10
|
||||
Width: PARENT_RIGHT / 2 - 20
|
||||
@@ -182,6 +186,8 @@ Container@DISPLAY_PANEL:
|
||||
Height: 20
|
||||
Font: Regular
|
||||
Text: Show Game Event Notifications
|
||||
TooltipText: Show transient text notifications for game events
|
||||
TooltipContainer: SETTINGS_TOOLTIP_CONTAINER
|
||||
Container@ROW:
|
||||
Width: PARENT_RIGHT - 24
|
||||
Height: 20
|
||||
|
||||
@@ -84,7 +84,9 @@ Container@AUDIO_PANEL:
|
||||
Width: PARENT_RIGHT
|
||||
Height: 20
|
||||
Font: Regular
|
||||
Text: Mute Background Music
|
||||
Text: Mute Menu Music
|
||||
TooltipText: Mute background music when no specific track is playing
|
||||
TooltipContainer: SETTINGS_TOOLTIP_CONTAINER
|
||||
Container@MUSIC_VOLUME_CONTAINER:
|
||||
X: PARENT_RIGHT / 2 + 10
|
||||
Width: PARENT_RIGHT / 2 - 20
|
||||
|
||||
@@ -159,7 +159,9 @@ Container@DISPLAY_PANEL:
|
||||
Width: PARENT_RIGHT
|
||||
Height: 20
|
||||
Font: Regular
|
||||
Text: Player Stance Colors
|
||||
Text: Player Relationship Colors
|
||||
TooltipText: Change minimap and health bar colors based on relationship (own, enemy, ally, neutral)
|
||||
TooltipContainer: SETTINGS_TOOLTIP_CONTAINER
|
||||
Container@ROW:
|
||||
Width: PARENT_RIGHT - 24
|
||||
Height: 20
|
||||
@@ -173,6 +175,8 @@ Container@DISPLAY_PANEL:
|
||||
Height: 20
|
||||
Font: Regular
|
||||
Text: Show UI Feedback Notifications
|
||||
TooltipText: Show transient text notifications for UI events
|
||||
TooltipContainer: SETTINGS_TOOLTIP_CONTAINER
|
||||
Container@TRANSIENTS_CHECKBOX_CONTAINER:
|
||||
X: PARENT_RIGHT / 2 + 10
|
||||
Width: PARENT_RIGHT / 2 - 20
|
||||
@@ -182,6 +186,8 @@ Container@DISPLAY_PANEL:
|
||||
Height: 20
|
||||
Font: Regular
|
||||
Text: Show Game Event Notifications
|
||||
TooltipText: Show transient text notifications for game events
|
||||
TooltipContainer: SETTINGS_TOOLTIP_CONTAINER
|
||||
Container@ROW:
|
||||
Width: PARENT_RIGHT - 24
|
||||
Height: 20
|
||||
|
||||
@@ -313,7 +313,7 @@ Container@OBSERVER_WIDGETS:
|
||||
Container@ECONOMY_STATS_HEADERS:
|
||||
X: 0
|
||||
Y: 0
|
||||
Width: 640
|
||||
Width: 720
|
||||
Height: PARENT_BOTTOM
|
||||
Children:
|
||||
ColorBlock@HEADER_COLOR:
|
||||
@@ -384,6 +384,14 @@ Container@OBSERVER_WIDGETS:
|
||||
Text: Harvesters
|
||||
Align: Right
|
||||
Shadow: True
|
||||
Label@CARRYALLS_HEADER:
|
||||
X: 635
|
||||
Width: 80
|
||||
Height: PARENT_BOTTOM
|
||||
Font: Bold
|
||||
Text: Carryalls
|
||||
Align: Right
|
||||
Shadow: True
|
||||
Container@PRODUCTION_STATS_HEADERS:
|
||||
X: 0
|
||||
Y: 0
|
||||
@@ -717,7 +725,7 @@ Container@OBSERVER_WIDGETS:
|
||||
ScrollItem@ECONOMY_PLAYER_TEMPLATE:
|
||||
X: 0
|
||||
Y: 0
|
||||
Width: 640
|
||||
Width: 720
|
||||
Height: 25
|
||||
Background: scrollitem-nohover
|
||||
Children:
|
||||
@@ -785,6 +793,13 @@ Container@OBSERVER_WIDGETS:
|
||||
Height: PARENT_BOTTOM
|
||||
Align: Right
|
||||
Shadow: True
|
||||
Label@CARRYALLS:
|
||||
X: 635
|
||||
Y: 0
|
||||
Width: 80
|
||||
Height: PARENT_BOTTOM
|
||||
Align: Right
|
||||
Shadow: True
|
||||
ScrollItem@PRODUCTION_PLAYER_TEMPLATE:
|
||||
X: 0
|
||||
Y: 0
|
||||
|
||||
BIN
mods/d2k/maps/side-step.oramap
Normal file
BIN
mods/d2k/maps/side-step.oramap
Normal file
Binary file not shown.
BIN
mods/d2k/maps/source.oramap
Normal file
BIN
mods/d2k/maps/source.oramap
Normal file
Binary file not shown.
BIN
mods/d2k/maps/spice-mesa.oramap
Normal file
BIN
mods/d2k/maps/spice-mesa.oramap
Normal file
Binary file not shown.
BIN
mods/d2k/maps/stone-plateaus.oramap
Normal file
BIN
mods/d2k/maps/stone-plateaus.oramap
Normal file
Binary file not shown.
BIN
mods/d2k/maps/sunstroke.oramap
Normal file
BIN
mods/d2k/maps/sunstroke.oramap
Normal file
Binary file not shown.
@@ -885,6 +885,7 @@ repair_pad:
|
||||
Health:
|
||||
HP: 30000
|
||||
HitShape:
|
||||
TargetableOffsets: 1024,0,0, 0,-1024,0, 0,1024,0, -1024,0,0
|
||||
Type: Rectangle
|
||||
TopLeft: -1536, -512
|
||||
BottomRight: 1536, 512
|
||||
|
||||
@@ -175,8 +175,8 @@ Templates:
|
||||
Size: 1,2
|
||||
Categories: Sand-Rock-Cliff
|
||||
Tiles:
|
||||
0: Transition
|
||||
1: Transition
|
||||
0: Rough
|
||||
1: Rough
|
||||
Template@10:
|
||||
Id: 10
|
||||
Images: BLOXBASE.R8
|
||||
|
||||
@@ -165,5 +165,6 @@ DeviatorMissile:
|
||||
Range: 512
|
||||
Duration: 375
|
||||
InvalidTargets: Infantry, Structure
|
||||
ValidRelationships: Enemy, Neutral
|
||||
Warhead@4Concrete: DamagesConcrete
|
||||
Damage: 1000
|
||||
|
||||
@@ -13,6 +13,7 @@ Sound:
|
||||
Falloff: 0, 0, 100, 0
|
||||
Range: 0, 0c450, 4c0, 8c0
|
||||
BeyondTargetRange: 1c0
|
||||
MinDistance: 5c0
|
||||
Color: 00FFFFC8
|
||||
Warhead@1Dam: SpreadDamage
|
||||
Range: 0, 32
|
||||
|
||||
@@ -603,7 +603,7 @@ Container@PLAYER_WIDGETS:
|
||||
X: WINDOW_RIGHT - 250
|
||||
Y: 272
|
||||
Width: 238
|
||||
Height: 27
|
||||
Height: 28
|
||||
ImageCollection: sidebar
|
||||
ImageName: background-moneybin
|
||||
ClickThrough: false
|
||||
|
||||
Binary file not shown.
@@ -28,25 +28,25 @@ Players:
|
||||
Name: Creeps
|
||||
NonCombatant: True
|
||||
Faction: england
|
||||
Enemies: AntMan
|
||||
Allies: Neutral
|
||||
Enemies: AntMan
|
||||
PlayerReference@Spain:
|
||||
Name: Spain
|
||||
Playable: True
|
||||
AllowBots: False
|
||||
Playable: True
|
||||
Required: True
|
||||
LockFaction: True
|
||||
Faction: allies
|
||||
LockColor: True
|
||||
Color: F6D679
|
||||
LockSpawn: True
|
||||
LockTeam: True
|
||||
Enemies: AntMan
|
||||
Color: F6D679
|
||||
PlayerReference@AntMan:
|
||||
Name: AntMan
|
||||
Faciton: soviet
|
||||
Enemies: Spain, Creeps
|
||||
Bot: campaign
|
||||
Faction: soviet
|
||||
Enemies: Spain, Creeps
|
||||
|
||||
Actors:
|
||||
Actor0: brik
|
||||
|
||||
Binary file not shown.
@@ -44,7 +44,7 @@ Tick = function()
|
||||
USSR.MarkCompletedObjective(SovietObj)
|
||||
end
|
||||
|
||||
if USSR.HasNoRequiredUnits() then
|
||||
if USSR.HasNoRequiredUnits() and BadGuy.HasNoRequiredUnits() then
|
||||
Allies.MarkCompletedObjective(DestroyAll)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -27,6 +27,9 @@ AFLD:
|
||||
ParatroopersPower@paratroopers:
|
||||
DropItems: E1,E1,E1,E2,E2
|
||||
|
||||
HELI:
|
||||
-MustBeDestroyed:
|
||||
|
||||
ATEK:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
@@ -27,6 +27,9 @@ AFLD:
|
||||
ParatroopersPower@paratroopers:
|
||||
DropItems: E1,E1,E1,E2,E2
|
||||
|
||||
HELI:
|
||||
-MustBeDestroyed:
|
||||
|
||||
ATEK:
|
||||
Buildable:
|
||||
Prerequisites: ~disabled
|
||||
|
||||
Binary file not shown.
@@ -346,7 +346,7 @@ powerproxy.parabombs:
|
||||
AirstrikePower:
|
||||
Icon: parabombs
|
||||
Name: Parabombs (Single Use)
|
||||
Description: A Badger drops a load of parachuted bombs on your target.
|
||||
Description: A Badger drops a load of parachuted bombs\nat the selected location.
|
||||
OneShot: true
|
||||
AllowMultiple: true
|
||||
UnitType: badr.bomber
|
||||
|
||||
@@ -1560,7 +1560,7 @@ AFLD:
|
||||
Icon: paratroopers
|
||||
ChargeInterval: 7500
|
||||
Name: Paratroopers
|
||||
Description: A Badger drops a squad of infantry\nanywhere on the map.
|
||||
Description: A Badger drops a squad of infantry\nat the selected location.
|
||||
DropItems: E1R1,E1R1,E1R1,E3R1,E3R1
|
||||
ReinforcementsArrivedSpeechNotification: ReinforcementsArrived
|
||||
SelectTargetSpeechNotification: SelectTarget
|
||||
@@ -1583,7 +1583,7 @@ AFLD:
|
||||
Icon: parabombs
|
||||
ChargeInterval: 7500
|
||||
Name: Parabombs
|
||||
Description: A squad of Badgers drop parachuted\nbombs on your target.
|
||||
Description: A Badger drops a load of parachuted bombs\nat the selected location.
|
||||
SelectTargetSpeechNotification: SelectTarget
|
||||
SelectTargetTextNotification: Select target.
|
||||
CameraActor: camera
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user