Compare commits

...

77 Commits

Author SHA1 Message Date
Paul Chote
253e02ae08 Remove dependency on VCRUNTIME140.dll.
(cherry picked from commit bb6400f6f8585523c31dfeb7ab95073263aadd3c)
2023-02-13 21:27:35 +02:00
penev92
f707b9c975 Fixed Makefile check of MacOS architecture 2023-02-05 00:19:18 +01:00
abcdefg30
8629e578e3 Throw an ArgumentException when trying to translate null keys
(cherry picked from commit e4bb13ea07)
2023-02-03 08:36:24 +02:00
abcdefg30
c65cfd3528 Fix an NRE in ConnectionLogic
(cherry picked from commit 84add8a03d)
2023-02-03 08:34:57 +02:00
Azarus
7ad6cfda40 Add an option to check if an actor can be captured via Lua 2023-01-30 13:47:02 +02:00
Dean Simmons
393fd585b1 Only return file paths when returning zip contents
(cherry picked from commit e1b78c4821)
2023-01-26 19:26:59 +02:00
penev92
ea221f9bfb Fixed RA parabomb/paradrop support power tooltips
The AFLD's AirstrikePower@parabombs' description wasn't updated to the latest balance patch (3 Badgers -> 1 Badger)
2023-01-26 09:32:54 +01:00
N.N
b4ca81d1bd Fix deviator GrantExternalCondition Warhead
Warhead now applies only to enemy and neutral units.
2023-01-18 23:43:12 +01:00
RoosterDragon
49590bb5f7 Fix Locomotor cache coherency for disabled or paused mobile actors.
The cache in Locomotor that is populated via the UpdateCellBlocking method disagreed with the non-cached logic of IsBlockedBy when dealing with Mobile actors. The cache determined an actor to be moving if it was both movable and had horizontal movement types. IsBlockedBy determined an actor to be moving if it had horizontal movement types, but did not check if it was movable. This difference in checks could allow a mobile trait that was disabled or paused and which had horizontal movement to be treated differently be the two methods. UpdateCellBlocking would consider it not moving due to the disabled/paused trait. IsBlockedBy would consider it moving as it didn't care about the disabled/paused state of the trait.

Now, we unify the two methods to consider a mobile trait that is disabled/paused as not moving. This prevents HierarchicalPathFinder from crashing on the inconsistent state, i.e. when asked to path search through a cell of a mobile unit which has disabled or paused movement, but which has horizontal movement types from prior movement.
2023-01-17 18:58:34 +01:00
David Wilson
2d059c0d95 Fix to ensure Windows uninstaller removes reg entries from the 64 bit registry 2023-01-14 13:50:04 +01:00
penev92
d5dd6a9086 Added ITilesetSpecificPaletteInfo for linting 2023-01-10 16:37:56 +00:00
Gustas
a7ca0f0699 Fixed UnhardcodeBaseBuilderBotModule update rule
Update rules should should not read `modData.DefaultRules`

(cherry picked from commit 80b92fb667)
2023-01-10 18:13:01 +02:00
Gustas
4b02d9b700 Fixed UnhardcodeSquadManager update rule
Update rules should should not read `modData.DefaultRules`

(cherry picked from commit 29d21545a6)
2023-01-10 18:12:58 +02:00
Gustas
ebf55c8d86 Exclude Plugs from TS protection types
(cherry picked from commit 326f8115a0)
2023-01-10 18:12:51 +02:00
Paul Chote
f928922f3a Add BeforeUpdate* methods for update rules.
These make it possible to write more advanced update
rules that query state across multiple actors, or
based on resolved state.

(cherry picked from commit 129db98a2f)
2023-01-10 18:05:11 +02:00
penev92
bc3743d9ff Added an update rule for adding ControlGroups 2023-01-10 00:14:45 +01:00
penev92
8aa6600655 Added an update rule for DomainIndex removal
Also for adding the new HPF-related PathFinderOverlay and HierarchicalPathFinderOverlay that were added at the same time.
2023-01-10 00:14:04 +01:00
Ivaylo Draganov
215b3d8ab3 Clarify the wording and explain the meaning of some labels in settings
(cherry picked from commit 04648a66e6)
2023-01-09 21:59:41 +02:00
Matthias Mailänder
a397670dce Fix URL button blocking the party button. 2023-01-08 23:07:44 +01:00
Matthias Mailänder
43d7f9c6df Bump DiscordRichPresence library. 2023-01-08 23:07:42 +01:00
Gustas
6904ea8c6b Fixed a spelling error in Discovery ant mission 2023-01-07 17:02:19 +01:00
Gustas
d702d58d1a Remove bogus bridge tiles from temperate tileset 2023-01-07 17:02:18 +01:00
abcdefg30
e0b0c5624f Remove custom network frame counting from ReplayConnection 2023-01-04 09:35:06 +00:00
abcdefg30
33773f895c Specify targetable offsets for the repair pad in Dune 2000 2023-01-03 13:33:20 +01:00
Matthias Mailänder
9f759f3cc2 Enforce use of 'var' instead of explicit type.
(cherry picked from commit 19ecddcd86)
2022-12-29 10:05:20 +02:00
Paul Chote
9fb4302b2f Downgrade OpenAL-soft to 1.10.1.
1.12.2 introduces noticeable behaviour changes
to source positioning and cash ticks. This also
fixes missing sound backends on Linux.
2022-12-28 00:19:39 +01:00
abcdefg30
4685c1a6b1 Change into the correct directory before trying to push
Each workflow step starts back in the default directory,
so we need to checkout the repository folder again
2022-12-24 16:04:51 +01:00
abcdefg30
70bc5b097d Let the documentation workflow fetch the required git branch 2022-12-24 16:04:45 +01:00
Paul Chote
7691507baf Fix pseudo-fullscreen window size on macs with a notch. 2022-12-24 12:12:12 +01:00
abcdefg30
8d6cebe654 Fix map actors not being spawned with the correct owner 2022-12-24 11:20:58 +02:00
Matthias Mailänder
daf10c34a6 Fix invalid actors not spawning in-game. 2022-12-23 18:33:50 +01:00
Matthias Mailänder
d96ec21b95 In map editor replace invalid actor owners with neutral. 2022-12-23 18:33:49 +01:00
Paul Chote
aea1182bb5 Implement state prediction for lobby ready checkbox. 2022-12-23 16:34:05 +02:00
Paul Chote
b413b97a52 Implement state prediction for debug menu checkboxes. 2022-12-23 16:34:05 +02:00
Paul Chote
7daa27f123 Implement state prediction for lobby checkboxes. 2022-12-23 16:34:05 +02:00
Paul Chote
f3f98d8750 Package macOS releases as a universal binary.
* Minimum macOS version is raised to 10.11.
* App bundles ship 3 versions of the runtime and engine binaries,
  and a fat launcher that selects the appropriate runtime/apphost.
* Mono is used for macOS 10.11 - 10.14, or if OPENRA_PREFER_MONO
  environment variable has been set.
2022-12-23 12:55:53 +02:00
Paul Chote
69eeb2dc84 Update native deps to include macOS and Linux arm64 binaries. 2022-12-23 12:55:53 +02:00
Paul Chote
7cee29ff70 Report CPU arch in logs and sysinfo. 2022-12-23 12:54:29 +02:00
Paul Chote
5e80fee913 Fix MiniYaml source locations being lost when merging. 2022-12-23 10:45:24 +02:00
Gustas
c1474204e2 Added carryalls to spectator Economy statistics 2022-12-21 21:09:16 +01:00
N.N
223f9408fd Change tile 9 to Rough TerrainType 2022-12-21 19:57:19 +02:00
Gustas
674ca8555b Fix contrail using the wrong colors 2022-12-21 19:36:12 +02:00
abcdefg30
275325b0cf Run the CI workflow on PRs targeting prep 2022-12-21 13:28:48 +02:00
Paul Chote
06e399d3ce Bubble unhandled double-click events to OnClick. 2022-12-20 23:06:19 +01:00
RoosterDragon
d6d77eab7c HPF is aware of map projection changes.
An event is added to Map to indicate when the cell projection is changed. This is important as this can mean Map.Contains(CPos) could now return different results for the cell. The HierarchicalPathFinder is made aware of these changes so it can rebuild any out-of-date information. This fixes prevent a crash if a cell that was previously outside the map changes height and becomes inside the map. The local path search will explore the cell as it is inside the map - but if the HPF was unaware if had been updated, it will still consider the cell to be outside the map and unreachable, resulting in a crash.
2022-12-20 09:48:44 +13:00
RoosterDragon
2392566c1d HPF handles searches from unreachable source cells into cut off areas.
If a path search is performed by the HierarchicalPathFinder when the source cell is unreachable location, a path is still allowed and starts from one of the cells adjacent to the location. If moving into one of these cells results in the actor moving into an isolated area that cannot reach the target this would previously crash as no abstract path could be found. Now we handle such locations by giving them a unreachable cost so the path search will not attempt to explore them.

Imagine a map split into two by a one tile wide line of impassable cliffs. If an actor is on top of these cliffs it is allowed to path because it can "jump off" the cliff and move into the cell immediately either side of it. However it is important which side it chooses to jump into, as one it has moved off the cliff it loses access to the other side. The previous error came about because the path might try and search on the side that couldn't reach the target location. Now we avoid that being considered.
2022-12-20 09:48:36 +13:00
Matthias Mailänder
028467f150 Fix documentation deployment. 2022-12-18 12:57:21 +13:00
abcdefg30
eee4b04248 Fix the actor edit panel not always getting closed properly 2022-12-18 12:25:21 +13:00
Gustas
f551b542f3 Fix map editor sliders stealing focus 2022-12-15 21:06:25 +01:00
Smittytron
5d5419702b Remove MustDestroy from longbows in Soviet 11 2022-12-10 16:16:24 +02:00
Smittytron
47e89b7d76 Fix Siberian Conflict 3 win trigger 2022-12-10 16:16:13 +02:00
N.N
9bb0409f19 Update D2k map pool 2022-12-07 23:39:44 +01:00
abcdefg30
80ac494948 Fix carryables vanishing when another unit is picked up 2022-12-04 17:41:44 +02:00
abcdefg30
eb82f2b975 Remove a stray $ from infront of the version string when writing logs 2022-12-03 23:49:24 +02:00
abcdefg30
a5c8db82da Mark the script as executable 2022-12-02 23:32:41 +01:00
abcdefg30
2a26ff6818 Revert "Update Ubuntu"
This reverts commit ac351f6e65.
2022-12-02 23:32:39 +01:00
Matthias Mailänder
5a11d54956 Execute self-contained binaries with runtime as backfall 2022-12-02 23:03:58 +01:00
Gustas
26c5a8b76c Add cell reference HPF crash messages 2022-12-02 17:50:13 +01:00
Matthias Mailänder
efcfab78dc Unify file extensions. 2022-12-01 19:54:07 +02:00
Matthias Mailänder
ac351f6e65 Update Ubuntu 2022-12-01 19:54:07 +02:00
abcdefg30
42bdc9e53e Replace influence instead of throwing an exception in AddInfluence 2022-12-01 18:13:16 +01:00
Gustas
3097efc5b6 Fix UnreserveCarryable unreserving carryables of other carriers 2022-12-01 18:13:16 +01:00
Gustas
853422409f Fix AutoCaryall not unreserving when cargo dies 2022-12-01 18:13:16 +01:00
abcdefg30
f097250394 Make sure PickupUnit runs a TakeOff activity before ending 2022-12-01 18:13:16 +01:00
Paul Chote
96e0f96c12 Remove redundant order copies. 2022-11-27 23:00:53 +01:00
Paul Chote
0a4c4162be Always serialize orders. 2022-11-27 23:00:52 +01:00
Paul Chote
10ff24f24e Fix frame number not being included in pre-serialized order packets. 2022-11-27 23:00:51 +01:00
Matthias Mailänder
aeb96d98f3 Update notarisation to XCode 13 tooling. 2022-11-27 20:32:57 +01:00
Paul Chote
cf4bfdcdfb Fix display bounds when running on macbooks with a notch. 2022-11-26 20:45:22 +01:00
dnqbob
d55af8d75d HarvesterBotModule should not command harvesters that cannot be ordered 2022-11-24 23:22:04 +01:00
Matthias Mailänder
dd9e75d017 Show the host in the download failed error message. 2022-11-23 23:36:10 +01:00
Matthias Mailänder
1b6220962e Fix unknown host not getting translated. 2022-11-23 23:36:08 +01:00
Vapre
f70f2acb39 ExceptionHandler, fix npe. 2022-11-22 12:35:33 +01:00
Matthias Mailänder
32e2507bff Revert "Scripts: Check exit status of background process"
This reverts commit 3f106bef72.
2022-11-20 20:36:58 +02:00
Matthias Mailänder
22a42b7dc9 Manually add game speeds to the linter. 2022-11-18 23:14:54 +01:00
Gustas
c01e4043e8 Introduce MinDistance to AreaBeam projectile 2022-11-17 20:51:28 +01:00
Thomas Christlieb
c8665c98a6 fix misclicks through sidebar 2022-11-16 23:22:39 +01:00
113 changed files with 1252 additions and 848 deletions

View File

@@ -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

View File

@@ -3,7 +3,7 @@ name: Continuous Integration
on:
push:
pull_request:
branches: [ bleed ]
branches: [ bleed, 'prep-*' ]
jobs:
linux:

View File

@@ -79,19 +79,29 @@ jobs:
run: |
make all
- name: Clone docs.openra.net
- name: Clone docs.openra.net (Playtest)
if: startsWith(github.event.inputs.tag, 'playtest-')
uses: actions/checkout@v2
with:
repository: openra/docs
token: ${{ secrets.DOCS_TOKEN }}
path: docs
ref: playtest
- name: Clone docs.openra.net (Release)
if: startsWith(github.event.inputs.tag, 'release-')
uses: actions/checkout@v2
with:
repository: openra/docs
token: ${{ secrets.DOCS_TOKEN }}
path: docs
ref: release
- name: Update docs.openra.net (Playtest)
if: startsWith(github.event.inputs.tag, 'playtest-')
env:
GIT_TAG: ${{ github.event.inputs.tag }}
run: |
git checkout playtest
./utility.sh all --docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/traits.md"
./utility.sh all --weapon-docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/weapons.md"
./utility.sh all --sprite-sequence-docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/sprite-sequences.md"
@@ -102,7 +112,6 @@ jobs:
env:
GIT_TAG: ${{ github.event.inputs.tag }}
run: |
git checkout release
./utility.sh all --docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/traits.md"
./utility.sh all --weapon-docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/weapons.md"
./utility.sh all --sprite-sequence-docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/sprite-sequences.md"
@@ -115,15 +124,17 @@ jobs:
cd docs
git config --local user.email "actions@github.com"
git config --local user.name "GitHub Actions"
git add *.md
git add api/*.md
git commit -m "Update auto-generated documentation for ${GIT_TAG}"
- name: Push docs.openra.net (Release)
if: startsWith(github.event.inputs.tag, 'release-')
run: |
cd docs
git push origin release
- name: Push docs.openra.net (Playtest)
if: startsWith(github.event.inputs.tag, 'playtest-')
run: |
cd docs
git push origin playtest

View File

@@ -62,7 +62,7 @@ jobs:
file: build/linux/*
macos:
name: macOS Disk Images
name: macOS Disk Image
runs-on: macos-11
steps:
- name: Clone Repository
@@ -76,7 +76,7 @@ jobs:
- name: Prepare Environment
run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
- name: Package Disk Images
- name: Package Disk Image
env:
MACOS_DEVELOPER_IDENTITY: ${{ secrets.MACOS_DEVELOPER_IDENTITY }}
MACOS_DEVELOPER_CERTIFICATE_BASE64: ${{ secrets.MACOS_DEVELOPER_CERTIFICATE_BASE64 }}
@@ -87,7 +87,7 @@ jobs:
mkdir -p build/macos
./packaging/macos/buildpackage.sh "${GIT_TAG}" "${PWD}/build/macos"
- name: Upload Packages
- name: Upload Package
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -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 #####################
#

View File

@@ -55,7 +55,8 @@ namespace OpenRA.FileSystem
get
{
foreach (ZipEntry entry in pkg)
yield return entry.Name;
if (entry.IsFile)
yield return entry.Name;
}
}

View File

@@ -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

View File

@@ -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];
}

View File

@@ -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)

View File

@@ -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;

View File

@@ -83,7 +83,7 @@ namespace OpenRA
public MiniYamlNode Clone()
{
return new MiniYamlNode(Key, Value.Clone());
return new MiniYamlNode(Key, Value.Clone(), Comment, Location);
}
}

View File

@@ -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));
}

View File

@@ -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);
}
}

View File

@@ -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)
{

View File

@@ -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]}");
}

View File

@@ -8,7 +8,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Linguini.Bundle" Version="0.3.1" />
<PackageReference Include="OpenRA-Eluant" Version="1.0.18" />
<PackageReference Include="OpenRA-Eluant" Version="1.0.20" />
<PackageReference Include="Mono.NAT" Version="3.0.3" />
<PackageReference Include="SharpZipLib" Version="1.3.3" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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
{

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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>()))

View File

@@ -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" />

View File

@@ -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];
}

View File

@@ -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);

View File

@@ -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));

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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];

View File

@@ -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);

View File

@@ -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;

View File

@@ -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)))

View File

@@ -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)

View File

@@ -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); }
}

View File

@@ -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)

View File

@@ -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); }
}

View File

@@ -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>();

View File

@@ -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;
}

View File

@@ -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))

View File

@@ -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));

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}
}

View File

@@ -97,6 +97,8 @@ namespace OpenRA.Mods.Common.UpdateRules
new UnhardcodeBaseBuilderBotModule(),
new UnhardcodeVeteranProductionIconOverlay(),
new RenameContrailProperties(),
new RemoveDomainIndex(),
new AddControlGroups(),
})
};

View File

@@ -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; }
}
}

View File

@@ -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));

View File

@@ -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;

View File

@@ -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)

View File

@@ -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)
{

View File

@@ -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");

View File

@@ -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;
}

View File

@@ -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));
}

View File

@@ -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();

View File

@@ -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;
});

View File

@@ -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)))

View File

@@ -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)

View File

@@ -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()) },

View File

@@ -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),

View File

@@ -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;
}
}
}

View File

@@ -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'">

View File

@@ -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.

View File

@@ -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" />

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

Binary file not shown.

BIN
mods/d2k/maps/source.oramap Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -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

View File

@@ -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

View File

@@ -165,5 +165,6 @@ DeviatorMissile:
Range: 512
Duration: 375
InvalidTargets: Infantry, Structure
ValidRelationships: Enemy, Neutral
Warhead@4Concrete: DamagesConcrete
Damage: 1000

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -27,6 +27,9 @@ AFLD:
ParatroopersPower@paratroopers:
DropItems: E1,E1,E1,E2,E2
HELI:
-MustBeDestroyed:
ATEK:
Buildable:
Prerequisites: ~disabled

View File

@@ -27,6 +27,9 @@ AFLD:
ParatroopersPower@paratroopers:
DropItems: E1,E1,E1,E2,E2
HELI:
-MustBeDestroyed:
ATEK:
Buildable:
Prerequisites: ~disabled

Binary file not shown.

View File

@@ -346,7 +346,7 @@ powerproxy.parabombs:
AirstrikePower:
Icon: parabombs
Name: Parabombs (Single Use)
Description: A Badger drops a load of parachuted bombs on your target.
Description: A Badger drops a load of parachuted bombs\nat the selected location.
OneShot: true
AllowMultiple: true
UnitType: badr.bomber

View File

@@ -1560,7 +1560,7 @@ AFLD:
Icon: paratroopers
ChargeInterval: 7500
Name: Paratroopers
Description: A Badger drops a squad of infantry\nanywhere on the map.
Description: A Badger drops a squad of infantry\nat the selected location.
DropItems: E1R1,E1R1,E1R1,E3R1,E3R1
ReinforcementsArrivedSpeechNotification: ReinforcementsArrived
SelectTargetSpeechNotification: SelectTarget
@@ -1583,7 +1583,7 @@ AFLD:
Icon: parabombs
ChargeInterval: 7500
Name: Parabombs
Description: A squad of Badgers drop parachuted\nbombs on your target.
Description: A Badger drops a load of parachuted bombs\nat the selected location.
SelectTargetSpeechNotification: SelectTarget
SelectTargetTextNotification: Select target.
CameraActor: camera

Some files were not shown because too many files have changed in this diff Show More