Compare commits
81 Commits
bleed
...
devtest-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc94d93fc8 | ||
|
|
810ba2c9ae | ||
|
|
77190ed1ac | ||
|
|
b543f738db | ||
|
|
40a9b47b44 | ||
|
|
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:
|
||||
@@ -12,10 +12,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install .NET 6.0
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Check Code
|
||||
run: |
|
||||
@@ -53,10 +53,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install .NET 6.0
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
|
||||
31
.github/workflows/documentation.yml
vendored
31
.github/workflows/documentation.yml
vendored
@@ -15,12 +15,12 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.inputs.tag }}
|
||||
|
||||
- name: Install .NET 6
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
make all
|
||||
|
||||
- name: Clone Wiki
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: openra/openra.wiki
|
||||
token: ${{ secrets.DOCS_TOKEN }}
|
||||
@@ -66,12 +66,12 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.inputs.tag }}
|
||||
|
||||
- name: Install .NET 6
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
|
||||
@@ -79,19 +79,29 @@ jobs:
|
||||
run: |
|
||||
make all
|
||||
|
||||
- name: Clone docs.openra.net
|
||||
uses: actions/checkout@v2
|
||||
- name: Clone docs.openra.net (Playtest)
|
||||
if: startsWith(github.event.inputs.tag, 'playtest-')
|
||||
uses: actions/checkout@v3
|
||||
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@v3
|
||||
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
|
||||
|
||||
20
.github/workflows/packaging.yml
vendored
20
.github/workflows/packaging.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare Environment
|
||||
run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
|
||||
@@ -37,10 +37,10 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install .NET 6.0
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
|
||||
@@ -62,21 +62,21 @@ jobs:
|
||||
file: build/linux/*
|
||||
|
||||
macos:
|
||||
name: macOS Disk Images
|
||||
name: macOS Disk Image
|
||||
runs-on: macos-11
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install .NET 6.0
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
|
||||
- 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 }}
|
||||
@@ -101,10 +101,10 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install .NET 6.0
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
|
||||
|
||||
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.19" />
|
||||
<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;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace OpenRA.Server
|
||||
frame = BitConverter.ToInt32(bytes, 4);
|
||||
state = ReceiveState.Data;
|
||||
|
||||
if (expectLength < 0 || expectLength > MaxOrderLength)
|
||||
if (expectLength < 0 || (server.Type != ServerType.Local && expectLength > MaxOrderLength))
|
||||
{
|
||||
Log.Write("server", $"Closing socket connection to {EndPoint} because of excessive order length: {expectLength}");
|
||||
return;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,7 +20,7 @@ mcv:
|
||||
Armor:
|
||||
Type: light
|
||||
Encyclopedia:
|
||||
Description: The Mobile Construction Vehicle must be driven to a suitable deployment area. After locating an appropriate area of rock, the MCV can be transformed into a Construction Yard.\n\nMCVs are resistant to bullets and some high explosives. They are vulnerable to missiles and high caliber guns.
|
||||
Description: The Mobile Construction Vehicle must be driven to a suitable deployment area. After locating an appropriate area of rock, the MCV can be transformed into a Construction Yard.\n\nMCVs are resistant to bullets and light explosives. They are vulnerable to missiles and high caliber guns.
|
||||
Order: 180
|
||||
Category: Units
|
||||
Mobile:
|
||||
@@ -192,7 +192,7 @@ quad:
|
||||
Weapon: Rocket
|
||||
LocalOffset: 128,64,64, 128,-64,64
|
||||
Encyclopedia:
|
||||
Description: Stronger than the Trike in both armor and firepower, the Quad is a four-wheeled vehicle firing armor-piercing rockets. The Quad is effective against most vehicles.\n\nQuads are resistant to bullets and high explosives, to a lesser degree. However, Quads are vulnerable to missiles and high caliber guns.
|
||||
Description: Stronger than the Trike in both armor and firepower, the Quad is a four-wheeled vehicle firing armor-piercing rockets. The Quad is effective against most vehicles.\n\nQuads are resistant to bullets and explosives, to a lesser degree. However, Quads are vulnerable to missiles and high caliber guns.
|
||||
Order: 110
|
||||
Category: Units
|
||||
AttackFrontal:
|
||||
@@ -227,7 +227,7 @@ siege_tank:
|
||||
Armor:
|
||||
Type: light
|
||||
Encyclopedia:
|
||||
Description: Siege Tanks are very effective against infantry and lightly armored vehicles - but very weak against heavily armored targets. They fire over a long range.\n\nSiege Tanks are resistant to bullets, and to some degree, high explosives. These units are vulnerable to missiles and high caliber guns.
|
||||
Description: Siege Tanks are very effective against infantry and lightly armored vehicles - but very weak against heavily armored targets. They fire over a long range.\n\nSiege Tanks are resistant to bullets, and to some degree, explosives. These units are vulnerable to missiles and high caliber guns.
|
||||
Order: 170
|
||||
Category: Units
|
||||
Mobile:
|
||||
@@ -341,7 +341,7 @@ sonic_tank:
|
||||
Weapon: Sound
|
||||
LocalOffset: 600,0,427
|
||||
Encyclopedia:
|
||||
Description: The Sonic Tank is most effective against infantry and lightly armored vehicles - but weaker against armored targets.\n\nThe Sonic Tank will damage all units in its firing path.\n\nThey are very resistant to bullets and high explosives, but vulnerable to missiles and high caliber guns.
|
||||
Description: The Sonic Tank is most effective against infantry and lightly armored vehicles - but weaker against armored targets.\n\nThe Sonic Tank will damage all units in its firing path.\n\nThey are very resistant to bullets and small explosives, but vulnerable to missiles and high caliber guns.
|
||||
Order: 200
|
||||
Category: Units
|
||||
AttackFrontal:
|
||||
@@ -626,7 +626,7 @@ combat_tank_h:
|
||||
Tooltip:
|
||||
Name: Harkonnen Combat Tank
|
||||
Encyclopedia:
|
||||
Description: The Combat Tank is effective against most vehicles, less so against lightly armored vehicles.\n\nThe Harkonnen Combat Tank is stronger but slower.
|
||||
Description: The Combat Tank is effective against most vehicles, less so against lightly armored vehicles.\n\nThe Harkonnen Combat Tank is stronger than its counterparts, but also slower.
|
||||
Order: 160
|
||||
Category: Units
|
||||
Buildable:
|
||||
@@ -649,7 +649,7 @@ combat_tank_o:
|
||||
Turreted:
|
||||
TurnSpeed: 20
|
||||
Encyclopedia:
|
||||
Description: The Combat Tank is effective against most vehicles, less so against lightly armored vehicles.\n\nThe Ordos Combat Tank is faster but weaker.
|
||||
Description: The Combat Tank is effective against most vehicles, less so against lightly armored vehicles.\n\nThe Ordos Combat Tank is the fastest variant of the Combat Tank, but it is also the weakest.
|
||||
Order: 150
|
||||
Category: Units
|
||||
Armament:
|
||||
|
||||
@@ -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.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user