Compare commits

...

33 Commits

Author SHA1 Message Date
teinarss
857da21b0f Add support for dotnet core for Windows 2020-12-23 18:41:27 +01:00
teinarss
73bba97aaa Update MasterServerPinger to modern approach 2020-12-22 20:57:40 +01:00
abcdefg30
d6e9cdab5b Add the 9th Dark Tournament map as "Oil Spill" 2020-12-21 21:08:39 +01:00
Paul Chote
1a177bc2de Remove unused variables from Map.SavePreview. 2020-12-19 13:07:01 +01:00
Paul Chote
e0b3e631fe Remove obsolete null checks. 2020-12-19 13:07:01 +01:00
Paul Chote
2518a353af Add lint test for invalid map tiles. 2020-12-19 13:07:01 +01:00
Paul Chote
989800efff Fix missing tiles in upstream maps. 2020-12-19 13:07:01 +01:00
Paul Chote
c02846e2cb Replace invalid tiles on map load. 2020-12-19 13:07:01 +01:00
Matthias Mailänder
09db4a0e25 Enable debug mode by default. 2020-12-15 23:06:05 +01:00
Trevor Nichols
a85da9d86c Implement loader for MegV3 file format 2020-12-15 11:11:05 +01:00
Paul Chote
920d00bbae Use nearest-neighbour scaling in --debug-chrome-regions utility command. 2020-12-14 22:38:25 +01:00
Ivaylo Draganov
62475279ee Remove unneeded chrome yaml declarations 2020-12-14 20:52:38 +01:00
Matthias Mailänder
d8e979d283 Remove dead code. 2020-12-14 20:38:50 +01:00
Paul Chote
299b8880dd Fix api output directory. 2020-12-14 18:33:35 +01:00
Paul Chote
61027e4067 Fix docs.openra.net repository reference. 2020-12-14 18:33:35 +01:00
Paul Chote
a7249c10dc Fix docs.openra.net documentation workflow. 2020-12-13 15:35:31 +01:00
Paul Chote
611d12ac78 Migrate CI and packaging from Travis CI to GitHub Actions. 2020-12-12 20:17:29 +00:00
abcdefg30
ef9f26a60d Fix MissionObjectives not properly ending the game 2020-12-12 14:59:49 +01:00
abcdefg30
aeaffc0a8e Properly restrict the spectator view in mission maps 2020-12-12 14:59:49 +01:00
Paul Chote
e3084e230e Switch mirror for nsis3 package. 2020-12-12 14:54:50 +01:00
Paul Chote
4c01c772f8 Fix incorrect animation playing when moving infantry stop to attack. 2020-12-12 14:40:13 +01:00
Paul Chote
ed94f7680a Revert "Fix WithInfantryBody wrongly overwriting attack animations"
This reverts commit 1a63cc4a41.
2020-12-12 14:40:13 +01:00
yuantse
408d66cdaf Add Allies 09a 2020-12-12 13:18:08 +01:00
Smittytron
c4a0f2f169 Add Counterstrike mission Sarin Gas 3: Controlled Burn 2020-12-12 13:15:18 +01:00
Paul Chote
26b28d26da Prevent Civilians from wandering onto Tiberium. 2020-12-12 12:54:46 +01:00
Paul Chote
8ded6dafd4 Add AvoidTerrainTypes to ScaredyCat. 2020-12-12 12:54:46 +01:00
Paul Chote
57a94ad667 Add AvoidTerrainTypes to Wanders. 2020-12-12 12:54:46 +01:00
Paul Chote
53933a4d8f Fix restart black screen race condition. 2020-12-11 22:57:57 +01:00
Paul Chote
6606d7dd93 Add DisplayFaction details to the replay metadata. 2020-12-11 18:05:44 +01:00
Paul Chote
7a256dcafa Fix rally point target line exit display. 2020-12-11 17:25:30 +01:00
abcdefg30
7899c52b6d Add an update rule 2020-12-11 17:13:02 +01:00
abcdefg30
919c670502 Update the rules of the default mods 2020-12-11 17:13:02 +01:00
abcdefg30
aac3174efc Rename Stances to Relationships in the yaml api 2020-12-11 17:13:02 +01:00
170 changed files with 5226 additions and 727 deletions

55
.github/workflows/ci.yaml vendored Normal file
View File

@@ -0,0 +1,55 @@
name: Continuous Integration
on:
push:
pull_request:
branches: [ bleed ]
jobs:
linux-mono:
name: Linux (mono)
runs-on: ubuntu-20.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
- name: Check Code
run: |
mono --version
make check
- name: Check Mods
run: |
sudo apt-get install lua5.1
make check-scripts
make test
windows:
name: Windows (Net 5.0)
runs-on: windows-2019
steps:
- name: Clone Repository
uses: actions/checkout@v2
- name: Install .NET 5
uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'
- name: Check Code
shell: powershell
run: |
# Work around runtime failures on the GH Actions runner
dotnet nuget locals all --clear
.\make.ps1 check
dotnet build OpenRA.Test\OpenRA.Test.csproj -c Debug --nologo -p:TargetPlatform=win-x64
dotnet test bin\OpenRA.Test.dll --test-adapter-path:.
- name: Check Mods
run: |
chocolatey install lua --version 5.1.5.52
$ENV:Path = $ENV:Path + ";C:\Program Files (x86)\Lua\5.1\"
.\make.ps1 check-scripts
.\make.ps1 test

107
.github/workflows/documentation.yml vendored Normal file
View File

@@ -0,0 +1,107 @@
name: Deploy Documentation
on:
workflow_dispatch:
inputs:
tag:
description: 'Git Tag'
required: true
default: 'release-xxxxxxxx'
jobs:
wiki:
name: Update Wiki
if: github.repository == 'openra/openra'
runs-on: ubuntu-20.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.tag }}
- name: Prepare Environment
run: |
make all
- name: Clone Wiki
uses: actions/checkout@v2
with:
repository: openra/openra.wiki
token: ${{ secrets.DOCS_TOKEN }}
path: wiki
- name: Update Wiki (Playtest)
if: startsWith(github.event.inputs.tag, 'playtest-')
env:
GIT_TAG: ${{ github.event.inputs.tag }}
run: |
./utility.sh all --settings-docs "${GIT_TAG}" > "wiki/Settings (playtest).md"
- name: Update Wiki (Release)
if: startsWith(github.event.inputs.tag, 'release-')
env:
GIT_TAG: ${{ github.event.inputs.tag }}
run: |
./utility.sh all --settings-docs "${GIT_TAG}" > "wiki/Settings.md"
- name: Push Wiki
env:
GIT_TAG: ${{ github.event.inputs.tag }}
run: |
cd wiki
git config --local user.email "actions@github.com"
git config --local user.name "GitHub Actions"
git add --all
git commit -m "Update auto-generated documentation for ${GIT_TAG}"
git push origin master
docs:
name: Update docs.openra.net
if: github.repository == 'openra/openra'
runs-on: ubuntu-20.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.tag }}
- name: Prepare Environment
run: |
make all
- name: Clone docs.openra.net
uses: actions/checkout@v2
with:
repository: openra/docs
token: ${{ secrets.DOCS_TOKEN }}
path: docs
- name: Update docs.openra.net (Playtest)
if: startsWith(github.event.inputs.tag, 'playtest-')
env:
GIT_TAG: ${{ github.event.inputs.tag }}
run: |
./utility.sh all --docs "${GIT_TAG}" > "docs/api/playtest/traits.md"
./utility.sh all --weapon-docs "${GIT_TAG}" > "docs/api/playtest/weapons.md"
./utility.sh all --lua-docs "${GIT_TAG}" > "docs/api/playtest/lua.md"
- name: Update docs.openra.net (Release)
if: startsWith(github.event.inputs.tag, 'release-')
env:
GIT_TAG: ${{ github.event.inputs.tag }}
run: |
./utility.sh all --docs "${GIT_TAG}" > "docs/api/release/traits.md"
./utility.sh all --weapon-docs "${GIT_TAG}" > "docs/api/release/weapons.md"
./utility.sh all --lua-docs "${GIT_TAG}" > "docs/api/release/lua.md"
- name: Push docs.openra.net
env:
GIT_TAG: ${{ github.event.inputs.tag }}
run: |
cd docs
git config --local user.email "actions@github.com"
git config --local user.name "GitHub Actions"
git add --all
git commit -m "Update auto-generated documentation for ${GIT_TAG}"
git push origin master

88
.github/workflows/itch.yml vendored Normal file
View File

@@ -0,0 +1,88 @@
name: Deploy itch.io Packages
on:
workflow_dispatch:
inputs:
tag:
description: 'Git Tag'
required: true
default: 'release-xxxxxxxx'
jobs:
itch:
name: Deploy to itch.io
runs-on: ubuntu-20.04
if: github.repository == 'openra/openra'
steps:
- name: Download Packages
env:
GIT_TAG: ${{ github.event.inputs.tag }}
run: |
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-${GIT_TAG}-x64.exe"
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-${GIT_TAG}-x64-winportable.zip" -O "OpenRA-${GIT_TAG}-x64-win-itch.zip"
wget -q "https://github.com${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-${GIT_TAG}.dmg"
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-Dune-2000-x86_64.AppImage"
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-Red-Alert-x86_64.AppImage"
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-Tiberian-Dawn-x86_64.AppImage"
wget -q "https://raw.githubusercontent.com/${{ github.repository }}/${GIT_TAG}/packaging/.itch.toml"
zip -u "OpenRA-${GIT_TAG}-x64-win-itch.zip" .itch.toml
- name: Publish Windows Installer
uses: josephbmanley/butler-publish-itchio-action@master
env:
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
CHANNEL: win
ITCH_GAME: openra
ITCH_USER: openra-developers
VERSION: ${{ github.event.inputs.tag }}
PACKAGE: OpenRA-${{ github.event.inputs.tag }}}-x64.exe"
- name: Publish Windows Itch Bundle
uses: josephbmanley/butler-publish-itchio-action@master
env:
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
CHANNEL: itch
ITCH_GAME: openra
ITCH_USER: openra-developers
VERSION: ${{ github.event.inputs.tag }}
PACKAGE: OpenRA-${{ github.event.inputs.tag }}-x64-win-itch.zip
- name: Publish macOS Package
uses: josephbmanley/butler-publish-itchio-action@master
env:
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
CHANNEL: macos
ITCH_GAME: openra
ITCH_USER: openra-developers
VERSION: ${{ github.event.inputs.tag }}
PACKAGE: OpenRA-${{ github.event.inputs.tag }}}.dmg"
- name: Publish RA AppImage
uses: josephbmanley/butler-publish-itchio-action@master
env:
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
CHANNEL: linux-ra
ITCH_GAME: openra
ITCH_USER: openra-developers
VERSION: ${{ github.event.inputs.tag }}
PACKAGE: OpenRA-Red-Alert-x86_64.AppImage
- name: Publish TD AppImage
uses: josephbmanley/butler-publish-itchio-action@master
env:
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
CHANNEL: linux-cnc
ITCH_GAME: openra
ITCH_USER: openra-developers
VERSION: ${{ github.event.inputs.tag }}
PACKAGE: OpenRA-Tiberian-Dawn-x86_64.AppImage
- name: Publish D2k AppImage
uses: josephbmanley/butler-publish-itchio-action@master
env:
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
CHANNEL: linux-d2k
ITCH_GAME: openra
ITCH_USER: openra-developers
VERSION: ${{ github.event.inputs.tag }}
PACKAGE: OpenRA-Dune-2000-x86_64.AppImage

94
.github/workflows/packaging.yml vendored Normal file
View File

@@ -0,0 +1,94 @@
name: Release Packaging
on:
push:
tags:
- 'release-*'
- 'playtest-*'
- 'devtest-*'
jobs:
linux:
name: Linux AppImages
runs-on: ubuntu-20.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
- name: Prepare Environment
run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
- name: Package AppImages
run: |
mkdir -p build/linux
./packaging/linux/buildpackage.sh "${GIT_TAG}" "${PWD}/build/linux"
- name: Upload Packages
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref }}
overwrite: true
file_glob: true
file: build/linux/*
macos:
name: macOS Disk Images
runs-on: macos-10.15
steps:
- name: Clone Repository
uses: actions/checkout@v2
- name: Prepare Environment
run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
- name: Package Disk Images
env:
MACOS_DEVELOPER_IDENTITY: ${{ secrets.MACOS_DEVELOPER_IDENTITY }}
MACOS_DEVELOPER_CERTIFICATE_BASE64: ${{ secrets.MACOS_DEVELOPER_CERTIFICATE_BASE64 }}
MACOS_DEVELOPER_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_DEVELOPER_CERTIFICATE_PASSWORD }}
MACOS_DEVELOPER_USERNAME: ${{ secrets.MACOS_DEVELOPER_USERNAME }}
MACOS_DEVELOPER_PASSWORD: ${{ secrets.MACOS_DEVELOPER_PASSWORD }}
run: |
mkdir -p build/macos
./packaging/macos/buildpackage.sh "${GIT_TAG}" "${PWD}/build/macos"
- name: Upload Packages
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref }}
overwrite: true
file_glob: true
file: build/macos/*
windows:
name: Windows Installers
runs-on: ubuntu-20.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
- name: Install .NET 5
uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'
- name: Prepare Environment
run: |
echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
sudo apt install nsis wine64
- name: Package Installers
run: |
mkdir -p build/windows
./packaging/windows/buildpackage.sh "${GIT_TAG}" "${PWD}/build/windows"
- name: Upload Packages
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref }}
overwrite: true
file_glob: true
file: build/windows/*

View File

@@ -1,92 +0,0 @@
# Travis-CI Build for OpenRA
# see travis-ci.org for details
language: csharp
mono: 6.4.0
os: linux
dist: xenial
jobs:
include:
- os: linux
dist: xenial
- os: osx
if: tag IS present
osx_image: xcode10
addons:
apt:
packages:
- lua5.1
- dpkg
- zsync
- imagemagick
# Environment variables
env:
secure: "C0+Hlfa0YGErxUuWV00Tj6p45otC/D3YwYFuLpi2mj1rDFn/4dgh5WRngjvdDBVbXJ3duaZ78jPHWm1jr7vn2jqj9yETsCIK9psWd38ep/FEBM0SDr6MUD89OuXk/YyvxJAE+UXF6bXg7giey09g/CwBigjMW7ynET3wNAWPHPs="
# Fetch dependencies
# Run the build script
# Check source code with StyleCop
# call OpenRA to check for YAML errors
# Run the NUnit tests
script:
- make all
- |
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
make check || travis_terminate 1;
make check-scripts || travis_terminate 1;
make test || travis_terminate 1;
mono ~/.nuget/packages/nunit.consolerunner/3.11.1/tools/nunit3-console.exe --noresult bin/OpenRA.Test.dll || travis_terminate 1;
fi
# Only watch the development branch and tagged release.
branches:
only:
- /^release-.*$/
- /^playtest-.*$/
- /^devtest-.*$/
- /^prep-.*$/
- bleed
# Notify developers when build passed/failed.
notifications:
irc:
if: repo = OpenRA/OpenRA
template:
- "%{repository}#%{build_number} %{commit} %{author}: %{message} %{build_url}"
channels:
- "irc.freenode.net#openra"
use_notice: true
skip_join: true
before_deploy:
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
wget https://mirrors.edge.kernel.org/ubuntu/pool/universe/n/nsis/nsis-common_3.04-1_all.deb;
wget https://mirrors.edge.kernel.org/ubuntu/pool/universe/n/nsis/nsis_3.04-1_amd64.deb;
sudo dpkg -i nsis-common_3.04-1_all.deb;
sudo dpkg -i nsis_3.04-1_amd64.deb;
echo ${TRAVIS_REPO_SLUG};
if [[ "${TRAVIS_REPO_SLUG}" == "OpenRA/OpenRA" ]]; then
cd packaging && ./update-wiki.sh ${TRAVIS_TAG} && ./update-docs.sh ${TRAVIS_TAG} && cd ..;
fi;
fi
- export PATH=${PATH}:${HOME}/usr/bin
- DOTVERSION=`echo ${TRAVIS_TAG} | sed "s/-/\\./g"`
- cd packaging
- mkdir build
- ./package-all.sh ${TRAVIS_TAG} ${PWD}/build/
- if [[ "${TRAVIS_REPO_SLUG}" == "OpenRA/OpenRA" ]]; then
./upload-itch.sh ${TRAVIS_TAG} ${PWD}/build/;
fi
deploy:
provider: releases
token: ${GH_DEPLOY_API_KEY}
file_glob: true
file: build/*
skip_cleanup: true
on:
all_branches: true
tags: true

View File

@@ -8,18 +8,16 @@ Windows
Compiling OpenRA requires the following dependencies:
* [Windows PowerShell >= 4.0](http://microsoft.com/powershell) (included by default in recent Windows 10 versions)
* [.NET Framework 4.7.2 (Developer Pack)](https://dotnet.microsoft.com/download/dotnet-framework/net472) (or via Visual Studio 2017)
* [.NET Core 2.2 SDK](https://dotnet.microsoft.com/download/dotnet-core/2.2) (or via Visual Studio 2017)
* [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0) (or via Visual Studio)
To compile OpenRA, open the `OpenRA.sln` solution in the main folder, build it from the command-line with MSBuild or use the Makefile analogue command `make all` scripted in PowerShell syntax.
To compile OpenRA, open the `OpenRA.sln` solution in the main folder, build it from the command-line with `dotnet` or use the Makefile analogue command `make all` scripted in PowerShell syntax.
Run the game with `launch-game.cmd`. It can be handed arguments that specify the exact mod one wishes to run, for example, run `launch-game.cmd Game.Mod=ra` to launch Red Alert, `launch-game.cmd Game.Mod=cnc` to start Tiberian dawn or `launch-game.cmd Game.Mod=d2k` to launch Dune 2000.
Linux
=====
Mono, version 5.18 or later, is required to compile OpenRA. You can add the [upstream mono repository](https://www.mono-project.com/download/stable/#download-lin) for your distro to obtain the latest version if your system packages are not sufficient.
Mono, version 6.4 or later, is required to compile OpenRA. You can add the [upstream mono repository](https://www.mono-project.com/download/stable/#download-lin) for your distro to obtain the latest version if your system packages are not sufficient.
To compile OpenRA, run `make` from the command line. After this one can run the game with `./launch-game.sh`. It is also possible to specify the mod you wish to run from the command line, e.g. with `./launch-game.sh Game.Mod=ts` if you wish to try the experimental Tiberian Sun mod.
@@ -99,7 +97,7 @@ macOS
=====
Before compiling OpenRA you must install the following dependencies:
* [Mono >= 5.18](https://www.mono-project.com/download/stable/#download-mac)
* [Mono >= 6.4](https://www.mono-project.com/download/stable/#download-mac)
To compile OpenRA, run `make` from the command line. Run with `./launch-game.sh`.

View File

@@ -28,14 +28,14 @@
############################## TOOLCHAIN ###############################
#
# List of .NET assemblies that we can guarantee exist
WHITELISTED_OPENRA_ASSEMBLIES = OpenRA.exe OpenRA.Utility.exe OpenRA.Server.exe OpenRA.Platforms.Default.dll OpenRA.Game.dll OpenRA.Mods.Common.dll OpenRA.Mods.Cnc.dll OpenRA.Mods.D2k.dll
WHITELISTED_OPENRA_ASSEMBLIES = OpenRA.dll OpenRA.Utility.dll OpenRA.Server.dll OpenRA.Platforms.Default.dll OpenRA.Game.dll OpenRA.Mods.Common.dll OpenRA.Mods.Cnc.dll OpenRA.Mods.D2k.dll
# These are explicitly shipped alongside our core files by the packaging script
WHITELISTED_THIRDPARTY_ASSEMBLIES = ICSharpCode.SharpZipLib.dll FuzzyLogicLibrary.dll Eluant.dll BeaconLib.dll Open.Nat.dll SDL2-CS.dll OpenAL-CS.Core.dll DiscordRPC.dll Newtonsoft.Json.dll
# These are shipped in our custom minimal mono runtime and also available in the full system-installed .NET/mono stack
# This list *must* be kept in sync with the files packaged by the AppImageSupport and OpenRALauncherOSX repositories
WHITELISTED_CORE_ASSEMBLIES = mscorlib.dll System.dll System.Configuration.dll System.Core.dll System.Numerics.dll System.Security.dll System.Xml.dll Mono.Security.dll netstandard.dll
WHITELISTED_CORE_ASSEMBLIES = mscorlib.dll System.dll System.Configuration.dll System.Core.dll System.Numerics.dll System.Security.dll System.Xml.dll Mono.Security.dll netstandard.dll Microsoft.Win32.Registry.dll System.Security.AccessControl.dll System.Security.Principal.Windows.dll System.Xml.Linq.dll System.Runtime.Serialization.dll
######################### UTILITIES/SETTINGS ###########################
#
@@ -78,13 +78,13 @@ endif
endif
endif
OPENRA_UTILITY = ENGINE_DIR=".." $(MONO) --debug bin/OpenRA.Utility.exe
OPENRA_UTILITY = ENGINE_DIR=".." $(MONO) --debug bin/OpenRA.Utility.dll
##################### DEVELOPMENT BUILDS AND TESTS #####################
#
all:
@command -v $(firstword $(MSBUILD)) >/dev/null || (echo "OpenRA requires the '$(MSBUILD)' tool provided by Mono >= 5.18."; exit 1)
@$(MSBUILD) -t:Build -restore -p:Configuration=Release -p:TargetPlatform=$(TARGETPLATFORM)
@$(MSBUILD) -t:Build -restore -p:Configuration=Release -p:TargetPlatform=$(TARGETPLATFORM) -p:Mono=true -p:DefineConstants="MONO"
ifeq ($(TARGETPLATFORM), unix-generic)
@./configure-system-libraries.sh
endif
@@ -92,13 +92,13 @@ endif
clean:
@-$(RM_RF) ./bin ./*/bin ./*/obj
@$(MSBUILD) -t:Clean
@$(MSBUILD) -t:Clean -p:Mono=true
@-$(RM_F) IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP
check:
@echo
@echo "Compiling in debug mode..."
@$(MSBUILD) -t:build -restore -p:Configuration=Debug
@$(MSBUILD) -t:build -restore -p:Configuration=Debug -p:TargetPlatform=$(TARGETPLATFORM) -p:Mono=true -p:DefineConstants="MONO"
@echo
@echo "Checking runtime assemblies..."
@$(OPENRA_UTILITY) all --check-runtime-assemblies $(WHITELISTED_OPENRA_ASSEMBLIES) $(WHITELISTED_THIRDPARTY_ASSEMBLIES) $(WHITELISTED_CORE_ASSEMBLIES)

View File

@@ -297,6 +297,7 @@ namespace OpenRA
EngineVersion = "Unknown";
Console.WriteLine("Engine version is {0}", EngineVersion);
Console.WriteLine("Runtime: {0}", Platform.RuntimeVersion);
// Special case handling of Game.Mod argument: if it matches a real filesystem path
// then we use this to override the mod search path, and replace it with the mod id
@@ -329,9 +330,16 @@ namespace OpenRA
try
{
var rendererPath = Path.Combine(Platform.BinDir, "OpenRA.Platforms." + p + ".dll");
var assembly = Assembly.LoadFile(rendererPath);
#if !MONO
var loader = new AssemblyLoader(rendererPath);
var platformType = loader.LoadDefaultAssembly().GetTypes().SingleOrDefault(t => typeof(IPlatform).IsAssignableFrom(t));
#else
var assembly = Assembly.LoadFile(rendererPath);
var platformType = assembly.GetTypes().SingleOrDefault(t => typeof(IPlatform).IsAssignableFrom(t));
#endif
if (platformType == null)
throw new InvalidOperationException("Platform dll must include exactly one IPlatform implementation.");
@@ -934,9 +942,11 @@ namespace OpenRA
AdvertiseOnline = false
};
// Always connect to local games using the same loopback connection
// Exposing multiple endpoints introduces a race condition on the client's PlayerIndex (sometimes 0, sometimes 1)
// This would break the Restart button, which relies on the PlayerIndex always being the same for local servers
var endpoints = new List<IPEndPoint>
{
new IPEndPoint(IPAddress.IPv6Loopback, 0),
new IPEndPoint(IPAddress.Loopback, 0)
};
server = new Server.Server(endpoints, settings, ModData, ServerType.Local);

View File

@@ -119,6 +119,8 @@ namespace OpenRA
IsBot = runtimePlayer.IsBot,
FactionName = runtimePlayer.Faction.Name,
FactionId = runtimePlayer.Faction.InternalName,
DisplayFactionName = runtimePlayer.DisplayFaction.Name,
DisplayFactionId = runtimePlayer.DisplayFaction.InternalName,
Color = runtimePlayer.Color,
Team = client.Team,
SpawnPoint = runtimePlayer.SpawnPoint,
@@ -157,6 +159,10 @@ namespace OpenRA
public string FactionId;
public Color Color;
/// <summary>The faction (including Random, etc.) that was selected in the lobby.</summary>
public string DisplayFactionName;
public string DisplayFactionId;
/// <summary>The team ID on start-up, or 0 if the player is not part of a team.</summary>
public int Team;
public int SpawnPoint;

View File

@@ -168,10 +168,7 @@ namespace OpenRA.Graphics
for (var x = 0; x < template.Size.X; x++)
{
var tile = new TerrainTile(template.Id, (byte)(i++));
var tileInfo = tileset.GetTileInfo(tile);
// Empty tile
if (tileInfo == null)
if (!tileset.TryGetTileInfo(tile, out var tileInfo))
continue;
var sprite = TileSprite(tile);

View File

@@ -206,6 +206,8 @@ namespace OpenRA
public readonly MiniYaml NotificationDefinitions;
public readonly MiniYaml TranslationDefinitions;
public readonly Dictionary<CPos, TerrainTile> ReplacedInvalidTerrainTiles = new Dictionary<CPos, TerrainTile>();
// Generated data
public readonly MapGrid Grid;
public IReadOnlyPackage Package { get; private set; }
@@ -287,7 +289,6 @@ namespace OpenRA
this.modData = modData;
var size = new Size(width, height);
Grid = modData.Manifest.Get<MapGrid>();
var tileRef = new TerrainTile(tileset.Templates.First().Key, 0);
Title = "Name your map here";
Author = "Your name here";
@@ -310,7 +311,7 @@ namespace OpenRA
Tiles.CellEntryChanged += UpdateRamp;
}
Tiles.Clear(tileRef);
Tiles.Clear(tileset.DefaultTerrainTile);
PostInit();
}
@@ -430,12 +431,18 @@ namespace OpenRA
foreach (var uv in AllCells.MapCoords)
CustomTerrain[uv] = byte.MaxValue;
// Cache initial ramp state
// Replace invalid tiles and cache ramp state
var tileset = Rules.TileSet;
foreach (var uv in AllCells)
foreach (var uv in AllCells.MapCoords)
{
var tile = tileset.GetTileInfo(Tiles[uv]);
Ramp[uv] = tile != null ? tile.RampType : (byte)0;
if (!tileset.TryGetTileInfo(Tiles[uv], out var info))
{
ReplacedInvalidTerrainTiles[uv.ToCPos(this)] = Tiles[uv];
Tiles[uv] = tileset.DefaultTerrainTile;
info = tileset.GetTileInfo(tileset.DefaultTerrainTile);
}
Ramp[uv] = info.RampType;
}
AllEdgeCells = UpdateEdgeCells();
@@ -443,8 +450,7 @@ namespace OpenRA
void UpdateRamp(CPos cell)
{
var tile = Rules.TileSet.GetTileInfo(Tiles[cell]);
Ramp[cell] = tile != null ? tile.RampType : (byte)0;
Ramp[cell] = Rules.TileSet.GetTileInfo(Tiles[cell]).RampType;
}
void InitializeCellProjection()
@@ -670,32 +676,26 @@ namespace OpenRA
Color left, right;
var tileset = Rules.TileSet;
var type = tileset.GetTileInfo(Tiles[uv]);
if (type != null)
if (type.MinColor != type.MaxColor)
{
if (type.MinColor != type.MaxColor)
{
left = Exts.ColorLerp(Game.CosmeticRandom.NextFloat(), type.MinColor, type.MaxColor);
right = Exts.ColorLerp(Game.CosmeticRandom.NextFloat(), type.MinColor, type.MaxColor);
}
else
left = right = type.MinColor;
if (tileset.MinHeightColorBrightness != 1.0f || tileset.MaxHeightColorBrightness != 1.0f)
{
var scale = float2.Lerp(tileset.MinHeightColorBrightness, tileset.MaxHeightColorBrightness, Height[uv] * 1f / Grid.MaximumTerrainHeight);
left = Color.FromArgb((int)(scale * left.R).Clamp(0, 255), (int)(scale * left.G).Clamp(0, 255), (int)(scale * left.B).Clamp(0, 255));
right = Color.FromArgb((int)(scale * right.R).Clamp(0, 255), (int)(scale * right.G).Clamp(0, 255), (int)(scale * right.B).Clamp(0, 255));
}
left = Exts.ColorLerp(Game.CosmeticRandom.NextFloat(), type.MinColor, type.MaxColor);
right = Exts.ColorLerp(Game.CosmeticRandom.NextFloat(), type.MinColor, type.MaxColor);
}
else
left = right = Color.Black;
left = right = type.MinColor;
if (tileset.MinHeightColorBrightness != 1.0f || tileset.MaxHeightColorBrightness != 1.0f)
{
var scale = float2.Lerp(tileset.MinHeightColorBrightness, tileset.MaxHeightColorBrightness, Height[uv] * 1f / Grid.MaximumTerrainHeight);
left = Color.FromArgb((int)(scale * left.R).Clamp(0, 255), (int)(scale * left.G).Clamp(0, 255), (int)(scale * left.B).Clamp(0, 255));
right = Color.FromArgb((int)(scale * right.R).Clamp(0, 255), (int)(scale * right.G).Clamp(0, 255), (int)(scale * right.B).Clamp(0, 255));
}
return (left, right);
}
public byte[] SavePreview()
{
var tileset = Rules.TileSet;
var actorTypes = Rules.Actors.Values.Where(a => a.HasTraitInfo<IMapPreviewSignatureInfo>());
var actors = ActorDefinitions.Where(a => actorTypes.Where(ai => ai.Name == a.Value.Value).Any());
var positions = new List<(MPos Position, Color Color)>();
@@ -715,76 +715,73 @@ namespace OpenRA
foreach (var worldimpsi in worldimpsis)
worldimpsi.PopulateMapPreviewSignatureCells(this, worldActorInfo, null, positions);
using (var stream = new MemoryStream())
var isRectangularIsometric = Grid.Type == MapGridType.RectangularIsometric;
// Fudge the heightmap offset by adding as much extra as we need / can.
// This tries to correct for our incorrect assumption that MPos == PPos
var heightOffset = Math.Min(Grid.MaximumTerrainHeight, MapSize.Y - Bounds.Bottom);
var width = Bounds.Width;
var height = Bounds.Height + heightOffset;
var bitmapWidth = width;
if (isRectangularIsometric)
bitmapWidth = 2 * bitmapWidth - 1;
var stride = bitmapWidth * 4;
var pxStride = 4;
var minimapData = new byte[stride * height];
(Color Left, Color Right) terrainColor = default((Color, Color));
for (var y = 0; y < height; y++)
{
var isRectangularIsometric = Grid.Type == MapGridType.RectangularIsometric;
// Fudge the heightmap offset by adding as much extra as we need / can.
// This tries to correct for our incorrect assumption that MPos == PPos
var heightOffset = Math.Min(Grid.MaximumTerrainHeight, MapSize.Y - Bounds.Bottom);
var width = Bounds.Width;
var height = Bounds.Height + heightOffset;
var bitmapWidth = width;
if (isRectangularIsometric)
bitmapWidth = 2 * bitmapWidth - 1;
var stride = bitmapWidth * 4;
var pxStride = 4;
var minimapData = new byte[stride * height];
(Color Left, Color Right) terrainColor = default((Color, Color));
for (var y = 0; y < height; y++)
for (var x = 0; x < width; x++)
{
for (var x = 0; x < width; x++)
var uv = new MPos(x + Bounds.Left, y + Bounds.Top);
// FirstOrDefault will return a (MPos.Zero, Color.Transparent) if positions is empty
var actorColor = positions.FirstOrDefault(ap => ap.Position == uv).Color;
if (actorColor.A == 0)
terrainColor = GetTerrainColorPair(uv);
if (isRectangularIsometric)
{
var uv = new MPos(x + Bounds.Left, y + Bounds.Top);
// FirstOrDefault will return a (MPos.Zero, Color.Transparent) if positions is empty
var actorColor = positions.FirstOrDefault(ap => ap.Position == uv).Color;
if (actorColor.A == 0)
terrainColor = GetTerrainColorPair(uv);
if (isRectangularIsometric)
// Odd rows are shifted right by 1px
var dx = uv.V & 1;
var xOffset = pxStride * (2 * x + dx);
if (x + dx > 0)
{
// Odd rows are shifted right by 1px
var dx = uv.V & 1;
var xOffset = pxStride * (2 * x + dx);
if (x + dx > 0)
{
var z = y * stride + xOffset - pxStride;
var c = actorColor.A == 0 ? terrainColor.Left : actorColor;
minimapData[z++] = c.R;
minimapData[z++] = c.G;
minimapData[z++] = c.B;
minimapData[z] = c.A;
}
if (xOffset < stride)
{
var z = y * stride + xOffset;
var c = actorColor.A == 0 ? terrainColor.Right : actorColor;
minimapData[z++] = c.R;
minimapData[z++] = c.G;
minimapData[z++] = c.B;
minimapData[z] = c.A;
}
}
else
{
var z = y * stride + pxStride * x;
var z = y * stride + xOffset - pxStride;
var c = actorColor.A == 0 ? terrainColor.Left : actorColor;
minimapData[z++] = c.R;
minimapData[z++] = c.G;
minimapData[z++] = c.B;
minimapData[z] = c.A;
}
if (xOffset < stride)
{
var z = y * stride + xOffset;
var c = actorColor.A == 0 ? terrainColor.Right : actorColor;
minimapData[z++] = c.R;
minimapData[z++] = c.G;
minimapData[z++] = c.B;
minimapData[z] = c.A;
}
}
else
{
var z = y * stride + pxStride * x;
var c = actorColor.A == 0 ? terrainColor.Left : actorColor;
minimapData[z++] = c.R;
minimapData[z++] = c.G;
minimapData[z++] = c.B;
minimapData[z] = c.A;
}
}
var png = new Png(minimapData, bitmapWidth, height);
return png.Save();
}
var png = new Png(minimapData, bitmapWidth, height);
return png.Save();
}
public bool Contains(CPos cell)

View File

@@ -23,6 +23,8 @@ namespace OpenRA
}
public override int GetHashCode() { return Type.GetHashCode() ^ Index.GetHashCode(); }
public override string ToString() { return Type + "," + Index; }
}
public struct ResourceTile

View File

@@ -221,25 +221,30 @@ namespace OpenRA
public byte GetTerrainIndex(TerrainTile r)
{
if (!Templates.TryGetValue(r.Type, out var tpl))
return defaultWalkableTerrainIndex;
if (tpl.Contains(r.Index))
{
var tile = tpl[r.Index];
if (tile != null && tile.TerrainType != byte.MaxValue)
return tile.TerrainType;
}
var tile = Templates[r.Type][r.Index];
if (tile.TerrainType != byte.MaxValue)
return tile.TerrainType;
return defaultWalkableTerrainIndex;
}
public TerrainTileInfo GetTileInfo(TerrainTile r)
{
if (!Templates.TryGetValue(r.Type, out var tpl))
return null;
return tpl.Contains(r.Index) ? tpl[r.Index] : null;
return Templates[r.Type][r.Index];
}
public bool TryGetTileInfo(TerrainTile r, out TerrainTileInfo info)
{
if (!Templates.TryGetValue(r.Type, out var tpl) || !tpl.Contains(r.Index))
{
info = null;
return false;
}
info = tpl[r.Index];
return info != null;
}
public TerrainTile DefaultTerrainTile { get { return new TerrainTile(Templates.First().Key, 0); } }
}
}

View File

@@ -15,6 +15,7 @@ using System.IO;
using System.Linq;
using System.Reflection;
using OpenRA.Primitives;
using OpenRA.Support;
namespace OpenRA
{
@@ -41,26 +42,49 @@ namespace OpenRA
var resolvedPath = FileSystem.FileSystem.ResolveAssemblyPath(path, manifest, mods);
if (resolvedPath == null)
throw new FileNotFoundException("Assembly `{0}` not found.".F(path));
#if !MONO
var loader = new AssemblyLoader(resolvedPath);
var platformType = loader.LoadDefaultAssembly();
assemblyList.Add(platformType);
// .NET doesn't provide any way of querying the metadata of an assembly without either:
// (a) loading duplicate data into the application domain, breaking the world.
// (b) crashing if the assembly has already been loaded.
// We can't check the internal name of the assembly, so we'll work off the data instead
var hash = CryptoUtil.SHA1Hash(File.ReadAllBytes(resolvedPath));
if (!ResolvedAssemblies.TryGetValue(hash, out var assembly))
{
assembly = Assembly.LoadFile(resolvedPath);
ResolvedAssemblies.Add(hash, assembly);
}
assemblyList.Add(assembly);
#else
LoadAssembly(assemblyList, resolvedPath);
#endif
}
AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
assemblies = assemblyList.SelectMany(asm => asm.GetNamespaces().Select(ns => (asm, ns))).ToArray();
}
void LoadAssembly(List<Assembly> assemblyList, string resolvedPath)
{
// .NET doesn't provide any way of querying the metadata of an assembly without either:
// (a) loading duplicate data into the application domain, breaking the world.
// (b) crashing if the assembly has already been loaded.
// We can't check the internal name of the assembly, so we'll work off the data instead
var hash = CryptoUtil.SHA1Hash(File.ReadAllBytes(resolvedPath));
if (!ResolvedAssemblies.TryGetValue(hash, out var assembly))
{
assembly = Assembly.LoadFile(resolvedPath);
ResolvedAssemblies.Add(hash, assembly);
// Allow mods to use libraries.
var assemblyPath = Path.GetDirectoryName(resolvedPath);
if (assemblyPath != null)
{
foreach (var referencedAssembly in assembly.GetReferencedAssemblies())
{
var depedencyPath = Path.Combine(assemblyPath, referencedAssembly.Name + ".dll");
if (File.Exists(depedencyPath))
LoadAssembly(assemblyList, depedencyPath);
}
}
}
assemblyList.Add(assembly);
}
Assembly ResolveAssembly(object sender, ResolveEventArgs e)
{
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net472</TargetFramework>
<TargetFramework>netstandard2.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<UseVSHostingProcess>false</UseVSHostingProcess>
@@ -39,6 +39,11 @@
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
<AdditionalFiles Include="../stylecop.json" />
</ItemGroup>
<ItemGroup Condition="'$(Mono)' == ''">
<PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="5.0.0-preview.3-runtime.20214.6" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
</ItemGroup>
<Target Name="DisableAnalyzers" BeforeTargets="CoreCompile" Condition="'$(Configuration)'=='Release'">
<!-- Disable code style analysis on Release builds to improve compile-time performance -->
<ItemGroup Condition="'$(Configuration)'=='Release'">

View File

@@ -64,7 +64,7 @@ namespace OpenRA
public readonly Shroud Shroud;
public readonly FrozenActorLayer FrozenActorLayer;
/// <summary>The faction (including Random, etc) that was selected in the lobby.</summary>
/// <summary>The faction (including Random, etc.) that was selected in the lobby.</summary>
public readonly FactionInfo DisplayFaction;
/// <summary>The spawn point index that was assigned for client-based players.</summary>
@@ -80,7 +80,8 @@ namespace OpenRA
{
get
{
return spectating || WinState != WinState.Undefined;
// Players in mission maps must not leave the player view
return !inMissionMap && (spectating || WinState != WinState.Undefined);
}
}

View File

@@ -0,0 +1,362 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
// Not used/usable on Mono. Only used for Dotnet Core. Based on https://github.com/natemcmaster/DotNetCorePlugins
#if !MONO
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
using Microsoft.Extensions.DependencyModel;
namespace OpenRA.Support
{
public class AssemblyLoader
{
readonly string mainAssembly;
readonly AssemblyLoadContext context;
public Assembly LoadDefaultAssembly() => context.LoadFromAssemblyPath(mainAssembly);
public AssemblyLoader(string assemblyFile)
{
mainAssembly = assemblyFile;
var baseDir = Path.GetDirectoryName(assemblyFile);
context = CreateLoadContext(baseDir, assemblyFile);
}
static AssemblyLoadContext CreateLoadContext(string baseDir, string assemblyFile)
{
var depsJsonFile = Path.Combine(baseDir, Path.GetFileNameWithoutExtension(assemblyFile) + ".deps.json");
var builder = new AssemblyLoadContextBuilder();
builder.TryAddDependencyContext(depsJsonFile, out _);
builder.SetBaseDirectory(baseDir);
return builder.Build();
}
}
public class AssemblyLoadContextBuilder
{
readonly Dictionary<string, ManagedLibrary> managedLibraries = new Dictionary<string, ManagedLibrary>(StringComparer.Ordinal);
readonly Dictionary<string, NativeLibrary> nativeLibraries = new Dictionary<string, NativeLibrary>(StringComparer.Ordinal);
string basePath;
public AssemblyLoadContext Build()
{
return new ManagedLoadContext(basePath, managedLibraries, nativeLibraries);
}
public AssemblyLoadContextBuilder SetBaseDirectory(string path)
{
if (string.IsNullOrEmpty(path))
throw new ArgumentException("Argument must not be null or empty.", nameof(path));
if (!Path.IsPathRooted(path))
throw new ArgumentException("Argument must be a full path.", nameof(path));
basePath = path;
return this;
}
public AssemblyLoadContextBuilder AddManagedLibrary(ManagedLibrary library)
{
managedLibraries.Add(library.Name.Name, library);
return this;
}
public AssemblyLoadContextBuilder AddNativeLibrary(NativeLibrary library)
{
ValidateRelativePath(library.AppLocalPath);
nativeLibraries.Add(library.Name, library);
return this;
}
static void ValidateRelativePath(string probingPath)
{
if (string.IsNullOrEmpty(probingPath))
throw new ArgumentException("Value must not be null or empty.", nameof(probingPath));
if (Path.IsPathRooted(probingPath))
throw new ArgumentException("Argument must be a relative path.", nameof(probingPath));
}
}
class ManagedLoadContext : AssemblyLoadContext
{
readonly string basePath;
readonly Dictionary<string, ManagedLibrary> managedAssemblies;
readonly Dictionary<string, NativeLibrary> nativeLibraries;
static readonly string[] NativeLibraryExtensions;
static readonly string[] NativeLibraryPrefixes;
static readonly string[] ManagedAssemblyExtensions =
{
".dll",
".ni.dll",
".exe",
".ni.exe"
};
static ManagedLoadContext()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
NativeLibraryPrefixes = new[] { "" };
NativeLibraryExtensions = new[] { ".dll" };
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
NativeLibraryPrefixes = new[] { "", "lib", };
NativeLibraryExtensions = new[] { ".dylib" };
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
NativeLibraryPrefixes = new[] { "", "lib" };
NativeLibraryExtensions = new[] { ".so", ".so.1" };
}
else
{
NativeLibraryPrefixes = Array.Empty<string>();
NativeLibraryExtensions = Array.Empty<string>();
}
}
public ManagedLoadContext(string baseDirectory, Dictionary<string, ManagedLibrary> managedAssemblies, Dictionary<string, NativeLibrary> nativeLibraries)
{
basePath = baseDirectory ?? throw new ArgumentNullException(nameof(baseDirectory));
this.managedAssemblies = managedAssemblies ?? throw new ArgumentNullException(nameof(managedAssemblies));
this.nativeLibraries = nativeLibraries ?? throw new ArgumentNullException(nameof(nativeLibraries));
}
protected override Assembly Load(AssemblyName assemblyName)
{
// If default context is preferred, check first for types in the default context unless the dependency has been declared as private
try
{
var defaultAssembly = Default.LoadFromAssemblyName(assemblyName);
if (defaultAssembly != null)
return null;
}
catch
{
// Swallow errors in loading from the default context
}
if (managedAssemblies.TryGetValue(assemblyName.Name, out var library) && SearchForLibrary(library, out var path))
return LoadFromAssemblyPath(path);
return null;
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
foreach (var prefix in NativeLibraryPrefixes)
if (nativeLibraries.TryGetValue(prefix + unmanagedDllName, out var library) && SearchForLibrary(library, prefix, out var path))
return LoadUnmanagedDllFromPath(path);
return base.LoadUnmanagedDll(unmanagedDllName);
}
bool SearchForLibrary(ManagedLibrary library, out string path)
{
// 1. Search in base path
foreach (var ext in ManagedAssemblyExtensions)
{
var local = Path.Combine(basePath, library.Name.Name + ext);
if (File.Exists(local))
{
path = local;
return true;
}
}
path = null;
return false;
}
bool SearchForLibrary(NativeLibrary library, string prefix, out string path)
{
// 1. Search in base path
foreach (var ext in NativeLibraryExtensions)
{
var candidate = Path.Combine(basePath, $"{prefix}{library.Name}{ext}");
if (File.Exists(candidate))
{
path = candidate;
return true;
}
}
// 2. Search in base path + app local (for portable deployments of netcoreapp)
var local = Path.Combine(basePath, library.AppLocalPath);
if (File.Exists(local))
{
path = local;
return true;
}
path = null;
return false;
}
}
public class ManagedLibrary
{
public AssemblyName Name { get; private set; }
public static ManagedLibrary CreateFromPackage(string assetPath)
{
return new ManagedLibrary
{
Name = new AssemblyName(Path.GetFileNameWithoutExtension(assetPath))
};
}
}
public class NativeLibrary
{
public string Name { get; private set; }
public string AppLocalPath { get; private set; }
public static NativeLibrary CreateFromPackage(string assetPath)
{
return new NativeLibrary
{
Name = Path.GetFileNameWithoutExtension(assetPath),
AppLocalPath = assetPath
};
}
}
public static class DependencyContextExtensions
{
public static AssemblyLoadContextBuilder TryAddDependencyContext(this AssemblyLoadContextBuilder builder, string depsFilePath, out Exception error)
{
error = null;
try
{
builder.AddDependencyContext(depsFilePath);
}
catch (Exception ex)
{
error = ex;
}
return builder;
}
public static AssemblyLoadContextBuilder AddDependencyContext(this AssemblyLoadContextBuilder builder, string depsFilePath)
{
var reader = new DependencyContextJsonReader();
using (var file = File.OpenRead(depsFilePath))
{
var deps = reader.Read(file);
builder.SetBaseDirectory(Path.GetDirectoryName(depsFilePath));
builder.AddDependencyContext(deps);
}
return builder;
}
static string GetFallbackRid()
{
string ridBase;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
ridBase = "win10";
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
ridBase = "linux";
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
ridBase = "osx.10.12";
else
return "any";
switch (RuntimeInformation.OSArchitecture)
{
case Architecture.X86:
return ridBase + "-x86";
case Architecture.X64:
return ridBase + "-x64";
case Architecture.Arm:
return ridBase + "-arm";
case Architecture.Arm64:
return ridBase + "-arm64";
}
return ridBase;
}
public static AssemblyLoadContextBuilder AddDependencyContext(this AssemblyLoadContextBuilder builder, DependencyContext dependencyContext)
{
var ridGraph = dependencyContext.RuntimeGraph.Any()
? dependencyContext.RuntimeGraph
: DependencyContext.Default.RuntimeGraph;
var rid = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.GetRuntimeIdentifier();
var fallbackRid = GetFallbackRid();
var fallbackGraph = ridGraph.FirstOrDefault(g => g.Runtime == rid)
?? ridGraph.FirstOrDefault(g => g.Runtime == fallbackRid)
?? new RuntimeFallbacks("any");
foreach (var managed in dependencyContext.ResolveRuntimeAssemblies(fallbackGraph))
builder.AddManagedLibrary(managed);
foreach (var native in dependencyContext.ResolveNativeAssets(fallbackGraph))
builder.AddNativeLibrary(native);
return builder;
}
static IEnumerable<ManagedLibrary> ResolveRuntimeAssemblies(this DependencyContext depContext, RuntimeFallbacks runtimeGraph)
{
var rids = GetRids(runtimeGraph);
return from library in depContext.RuntimeLibraries
from assetPath in SelectAssets(rids, library.RuntimeAssemblyGroups)
select ManagedLibrary.CreateFromPackage(assetPath);
}
static IEnumerable<NativeLibrary> ResolveNativeAssets(this DependencyContext depContext, RuntimeFallbacks runtimeGraph)
{
var rids = GetRids(runtimeGraph);
return from library in depContext.RuntimeLibraries
from assetPath in SelectAssets(rids, library.NativeLibraryGroups)
where !assetPath.EndsWith(".a", StringComparison.Ordinal)
select NativeLibrary.CreateFromPackage(assetPath);
}
static IEnumerable<string> GetRids(RuntimeFallbacks runtimeGraph)
{
return Enumerable.Concat(new[] { runtimeGraph.Runtime }, runtimeGraph?.Fallbacks ?? Enumerable.Empty<string>());
}
static IEnumerable<string> SelectAssets(IEnumerable<string> rids, IEnumerable<RuntimeAssetGroup> groups)
{
foreach (var rid in rids)
{
var group = groups.FirstOrDefault(g => g.Runtime == rid);
if (group != null)
return group.AssetPaths;
}
return groups.GetDefaultAssets();
}
}
}
#endif

View File

@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net472</TargetFramework>
<TargetFramework Condition="'$(Mono)' == ''">net5.0</TargetFramework>
<TargetFramework Condition="'$(Mono)' != ''">netstandard2.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<UseVSHostingProcess>false</UseVSHostingProcess>
@@ -17,6 +18,8 @@
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
<Configurations>Release;Debug</Configurations>
<AssemblyName>OpenRA</AssemblyName>
<IsPublishable Condition="'$(CopyGenericLauncher)' == 'False'">false</IsPublishable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<PropertyGroup>
<TargetPlatform Condition="$([MSBuild]::IsOsPlatform('Windows'))">win-x64</TargetPlatform>

View File

@@ -32,11 +32,6 @@ namespace OpenRA.Mods.Cnc.FileFormats
public static class AudReader
{
public static byte[] LoadSound(byte[] raw, ref int index)
{
return ImaAdpcmReader.LoadImaAdpcmSound(raw, ref index);
}
public static float SoundLength(Stream s)
{
var sampleRate = s.ReadUInt16();

View File

@@ -0,0 +1,138 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using OpenRA.FileSystem;
using OpenRA.Primitives;
namespace OpenRA.Mods.Cnc.FileSystem
{
/// <summary>
/// This class supports loading unencrypted V3 .meg files using
/// reference documentation from here https://modtools.petrolution.net/docs/MegFileFormat
/// </summary>
public class MegV3Loader : IPackageLoader
{
const uint UnencryptedMegID = 0xFFFFFFFF;
// Float value 0.99, but it is simpler to read and compare as an integer
const uint MegVersion = 0x3F7D70A4;
public bool TryParsePackage(Stream s, string filename, OpenRA.FileSystem.FileSystem context, out IReadOnlyPackage package)
{
var position = s.Position;
var id = s.ReadUInt32();
var version = s.ReadUInt32();
s.Position = position;
if (id != UnencryptedMegID || version != MegVersion)
{
package = null;
return false;
}
package = new MegFile(s, filename);
return true;
}
public sealed class MegFile : IReadOnlyPackage
{
readonly Stream s;
readonly Dictionary<string, (uint Offset, int Length)> contents = new Dictionary<string, (uint Offset, int Length)>();
public MegFile(Stream s, string filename)
{
Name = filename;
this.s = s;
var id = s.ReadUInt32();
var version = s.ReadUInt32();
if (id != UnencryptedMegID || version != MegVersion)
throw new Exception("Invalid file signature for meg file");
var headerSize = s.ReadUInt32();
var numStrings = s.ReadUInt32();
var numFiles = s.ReadUInt32();
var stringsSize = s.ReadUInt32();
var stringsStart = s.Position;
var filenames = new List<string>();
// The file names are an indexed array of strings
for (var i = 0; i < numStrings; i++)
{
var length = s.ReadUInt16();
filenames.Add(s.ReadASCII(length));
}
// The header indicates where we should be, so verify it
if (s.Position != stringsSize + stringsStart)
throw new Exception("File name table in .meg file inconsistent");
// Now we load each file entry and associated info
for (var i = 0; i < numFiles; i++)
{
// Ignore flags, crc, index
s.Position += 10;
var size = s.ReadUInt32();
var offset = s.ReadUInt32();
var nameIndex = s.ReadUInt16();
contents[filenames[nameIndex]] = (offset, (int)size);
}
if (s.Position != headerSize)
throw new Exception("Expected to be at data start offset");
}
public string Name { get; }
public IEnumerable<string> Contents => contents.Keys;
public bool Contains(string filename)
{
return contents.ContainsKey(filename);
}
public void Dispose()
{
s.Dispose();
}
public Stream GetStream(string filename)
{
// Look up the index of the filename
if (!contents.TryGetValue(filename, out var index))
return null;
return SegmentStream.CreateWithoutOwningStream(s, index.Offset, index.Length);
}
public IReadOnlyPackage OpenPackage(string filename, OpenRA.FileSystem.FileSystem context)
{
var childStream = GetStream(filename);
if (childStream == null)
return null;
if (context.TryParsePackage(childStream, filename, out var package))
return package;
childStream.Dispose();
return null;
}
}
}
}

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<TargetFramework>netstandard2.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<LangVersion>7.3</LangVersion>
@@ -11,6 +11,7 @@
<PlatformTarget>AnyCPU</PlatformTarget>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
<IsPublishable Condition="'$(CopyCncDll)' == 'False'">false</IsPublishable>
</PropertyGroup>
<ItemGroup>
<!-- Work around an issue where Rider does not detect files in the project root using the default glob -->
@@ -37,4 +38,4 @@
<Analyzer Remove="@(Analyzer)" />
</ItemGroup>
</Target>
</Project>
</Project>

View File

@@ -79,8 +79,8 @@ namespace OpenRA.Mods.Cnc.Traits
[Desc("The condition to grant to self while disguised.")]
public readonly string DisguisedCondition = null;
[Desc("What diplomatic stances can this actor disguise as.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Ally | PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Player relationships the owner of the disguise target needs.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally | PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Target types of actors that this actor disguise as.")]
public readonly BitSet<TargetableType> TargetTypes = new BitSet<TargetableType>("Disguise");
@@ -299,7 +299,7 @@ namespace OpenRA.Mods.Cnc.Traits
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
{
var stance = self.Owner.RelationshipWith(target.Owner);
if (!info.ValidStances.HasStance(stance))
if (!info.ValidRelationships.HasStance(stance))
return false;
return info.TargetTypes.Overlaps(target.GetAllTargetTypes());
@@ -308,7 +308,7 @@ namespace OpenRA.Mods.Cnc.Traits
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
{
var stance = self.Owner.RelationshipWith(target.Owner);
if (!info.ValidStances.HasStance(stance))
if (!info.ValidRelationships.HasStance(stance))
return false;
return info.TargetTypes.Overlaps(target.Info.GetAllTargetTypes());

View File

@@ -48,7 +48,7 @@ namespace OpenRA.Mods.Cnc.Traits
protected override bool ShouldRender(Actor self)
{
return infiltrators.Any(i => Info.ValidStances.HasStance(i.RelationshipWith(self.World.RenderPlayer)));
return infiltrators.Any(i => Info.ValidRelationships.HasStance(i.RelationshipWith(self.World.RenderPlayer)));
}
}
}

View File

@@ -30,8 +30,8 @@ namespace OpenRA.Mods.Cnc.Traits
[Desc("Color to use for the target line.")]
public readonly Color TargetLineColor = Color.Crimson;
[Desc("What diplomatic stances can be infiltrated by this actor.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Player relationships the owner of the infiltration target needs.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Behaviour when entering the target.",
"Possible values are Exit, Suicide, Dispose.")]
@@ -101,10 +101,10 @@ namespace OpenRA.Mods.Cnc.Traits
{
case TargetType.Actor:
return Info.Types.Overlaps(target.Actor.GetEnabledTargetTypes()) &&
Info.ValidStances.HasStance(self.Owner.RelationshipWith(target.Actor.Owner));
Info.ValidRelationships.HasStance(self.Owner.RelationshipWith(target.Actor.Owner));
case TargetType.FrozenActor:
return target.FrozenActor.IsValid && Info.Types.Overlaps(target.FrozenActor.TargetTypes) &&
Info.ValidStances.HasStance(self.Owner.RelationshipWith(target.FrozenActor.Owner));
Info.ValidRelationships.HasStance(self.Owner.RelationshipWith(target.FrozenActor.Owner));
default:
return false;
}
@@ -136,7 +136,7 @@ namespace OpenRA.Mods.Cnc.Traits
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
{
var stance = self.Owner.RelationshipWith(target.Owner);
if (!info.ValidStances.HasStance(stance))
if (!info.ValidRelationships.HasStance(stance))
return false;
return info.Types.Overlaps(target.GetAllTargetTypes());
@@ -145,7 +145,7 @@ namespace OpenRA.Mods.Cnc.Traits
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
{
var stance = self.Owner.RelationshipWith(target.Owner);
if (!info.ValidStances.HasStance(stance))
if (!info.ValidRelationships.HasStance(stance))
return false;
return info.Types.Overlaps(target.Info.GetAllTargetTypes());

View File

@@ -124,7 +124,7 @@ namespace OpenRA.Mods.Common.Activities
foreach (var t in enterActor.TraitsImplementing<INotifyCapture>())
t.OnCapture(enterActor, self, oldOwner, self.Owner, captures.Info.CaptureTypes);
if (self.Owner.RelationshipWith(oldOwner).HasStance(captures.Info.PlayerExperienceStances))
if (self.Owner.RelationshipWith(oldOwner).HasStance(captures.Info.PlayerExperienceRelationships))
self.Owner.PlayerActor.TraitOrDefault<PlayerExperience>()?.GiveExperience(captures.Info.PlayerExperience);
if (captures.Info.ConsumedByCapture)

View File

@@ -38,7 +38,7 @@ namespace OpenRA.Mods.Common.Activities
// Make sure we can still repair the target before entering
// (but not before, because this may stop the actor in the middle of nowhere)
var stance = self.Owner.RelationshipWith(enterActor.Owner);
if (enterHealth == null || enterHealth.DamageState == DamageState.Undamaged || enterEngineerRepariable == null || enterEngineerRepariable.IsTraitDisabled || !info.ValidStances.HasStance(stance))
if (enterHealth == null || enterHealth.DamageState == DamageState.Undamaged || enterEngineerRepariable == null || enterEngineerRepariable.IsTraitDisabled || !info.ValidRelationships.HasStance(stance))
{
Cancel(self, true);
return false;
@@ -61,7 +61,7 @@ namespace OpenRA.Mods.Common.Activities
return;
var stance = self.Owner.RelationshipWith(enterActor.Owner);
if (!info.ValidStances.HasStance(stance))
if (!info.ValidRelationships.HasStance(stance))
return;
if (enterHealth.DamageState == DamageState.Undamaged)

View File

@@ -89,9 +89,6 @@ namespace OpenRA.Mods.Common.Widgets
var tile = world.Map.Tiles[cell];
var tileInfo = world.Map.Rules.TileSet.GetTileInfo(tile);
if (tileInfo == null)
return false;
var terrainType = world.Map.Rules.TileSet.TerrainInfo[tileInfo.TerrainType];
if (mapResources[cell].Type == ResourceType.ResourceType)

View File

@@ -23,16 +23,14 @@ namespace OpenRA.Mods.Common.Effects
readonly RallyPoint rp;
readonly Animation flag;
readonly Animation circles;
readonly ExitInfo[] exits;
List<WPos> targetLineNodes = new List<WPos> { };
List<CPos> cachedLocations;
public RallyPointIndicator(Actor building, RallyPoint rp, ExitInfo[] exits)
public RallyPointIndicator(Actor building, RallyPoint rp)
{
this.building = building;
this.rp = rp;
this.exits = exits;
if (rp.Info.Image != null)
{
@@ -73,21 +71,8 @@ namespace OpenRA.Mods.Common.Effects
if (targetLineNodes.Count == 0)
return;
var exitPos = building.CenterPosition;
// Find closest exit
var dist = int.MaxValue;
foreach (var exit in exits)
{
var ep = building.CenterPosition + exit.SpawnOffset;
var len = (targetLineNodes[0] - ep).Length;
if (len < dist)
{
dist = len;
exitPos = ep;
}
}
var exit = building.NearestExitOrDefault(targetLineNodes[0]);
var exitPos = exit != null ? building.World.Map.CenterOfCell(building.Location + exit.Info.ExitCell) : building.CenterPosition;
targetLineNodes.Insert(0, exitPos);
}

View File

@@ -0,0 +1,25 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Linq;
namespace OpenRA.Mods.Common.Lint
{
public class CheckMapTiles : ILintMapPass
{
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Map map)
{
foreach (var kv in map.ReplacedInvalidTerrainTiles)
emitError("Cell {0} references invalid terrain tile {1}.".F(kv.Key, kv.Value));
}
}
}

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<TargetFramework>netstandard2.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<LangVersion>7.3</LangVersion>
@@ -11,6 +11,7 @@
<PlatformTarget>AnyCPU</PlatformTarget>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<!-- Work around an issue where Rider does not detect files in the project root using the default glob -->
@@ -24,6 +25,7 @@
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj">
<Private>False</Private>
</ProjectReference>
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
<PackageReference Include="OpenRA-FuzzyLogicLibrary" Version="1.0.1" />
<PackageReference Include="DiscordRichPresence" Version="1.0.150" />
<PackageReference Include="rix0rrr.BeaconLib" Version="1.0.2" />

View File

@@ -96,8 +96,8 @@ namespace OpenRA.Mods.Common.Projectiles
[Desc("Terrain where the projectile explodes instead of bouncing.")]
public readonly HashSet<string> InvalidBounceTerrain = new HashSet<string>();
[Desc("If projectile touches an actor with one of these stances during or after the first bounce, trigger explosion.")]
public readonly PlayerRelationship ValidBounceBlockerStances = PlayerRelationship.Enemy | PlayerRelationship.Neutral;
[Desc("Trigger the explosion if the projectile touches an actor thats owner has these player relationships.")]
public readonly PlayerRelationship ValidBounceBlockerRelationships = PlayerRelationship.Enemy | PlayerRelationship.Neutral;
[Desc("Altitude above terrain below which to explode. Zero effectively deactivates airburst.")]
public readonly WDist AirburstAltitude = WDist.Zero;
@@ -319,7 +319,7 @@ namespace OpenRA.Mods.Common.Projectiles
if (checkTargetType && !Target.FromActor(victim).IsValidFor(firedBy))
continue;
if (!info.ValidBounceBlockerStances.HasStance(firedBy.Owner.RelationshipWith(victim.Owner)))
if (!info.ValidBounceBlockerRelationships.HasStance(firedBy.Owner.RelationshipWith(victim.Owner)))
continue;
// If the impact position is within any actor's HitShape, we have a direct hit

View File

@@ -13,6 +13,7 @@ using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BeaconLib;
using OpenRA.Network;
using OpenRA.Server;
@@ -107,7 +108,7 @@ namespace OpenRA.Mods.Common.Server
{
isBusy = true;
Action a = () =>
Task.Run(() =>
{
try
{
@@ -160,9 +161,7 @@ namespace OpenRA.Mods.Common.Server
}
isBusy = false;
};
a.BeginInvoke(null, null);
});
}
}
}

View File

@@ -20,8 +20,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Accepted `DeliversCash` types. Leave empty to accept all types.")]
public readonly HashSet<string> ValidTypes = new HashSet<string>();
[Desc("Stance the delivering actor needs to enter.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Ally;
[Desc("Player relationships the owner of the delivering actor needs.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally;
[Desc("Play a randomly selected sound from this list when accepting cash.")]
public readonly string[] Sounds = { };
@@ -31,7 +31,7 @@ namespace OpenRA.Mods.Common.Traits
public class AcceptsDeliveredCash : INotifyCashTransfer
{
AcceptsDeliveredCashInfo info;
readonly AcceptsDeliveredCashInfo info;
public AcceptsDeliveredCash(Actor self, AcceptsDeliveredCashInfo info)
{

View File

@@ -20,8 +20,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Accepted `DeliversExperience` types. Leave empty to accept all types.")]
public readonly HashSet<string> ValidTypes = new HashSet<string>();
[Desc("Stance the delivering actor needs to enter.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Ally;
[Desc("Player relationships the owner of the delivering actor needs.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally;
public override object Create(ActorInitializer init) { return new AcceptsDeliveredExperience(init.Self, this); }
}

View File

@@ -68,8 +68,8 @@ namespace OpenRA.Mods.Common.Traits
public WeaponInfo WeaponInfo { get; private set; }
public WDist ModifiedRange { get; private set; }
public readonly PlayerRelationship TargetStances = PlayerRelationship.Enemy;
public readonly PlayerRelationship ForceTargetStances = PlayerRelationship.Enemy | PlayerRelationship.Neutral | PlayerRelationship.Ally;
public readonly PlayerRelationship TargetRelationships = PlayerRelationship.Enemy;
public readonly PlayerRelationship ForceTargetRelationships = PlayerRelationship.Enemy | PlayerRelationship.Neutral | PlayerRelationship.Ally;
// TODO: instead of having multiple Armaments and unique AttackBase,
// an actor should be able to have multiple AttackBases with

View File

@@ -377,7 +377,7 @@ namespace OpenRA.Mods.Common.Traits
return Armaments.Where(a =>
!a.IsTraitDisabled
&& (owner == null || (forceAttack ? a.Info.ForceTargetStances : a.Info.TargetStances).HasStance(self.Owner.RelationshipWith(owner)))
&& (owner == null || (forceAttack ? a.Info.ForceTargetRelationships : a.Info.TargetRelationships).HasStance(self.Owner.RelationshipWith(owner)))
&& a.Weapon.IsValidAgainst(t, self.World, self));
}
@@ -408,7 +408,7 @@ namespace OpenRA.Mods.Common.Traits
var stances = PlayerRelationship.None;
foreach (var armament in Armaments)
if (!armament.IsTraitDisabled)
stances |= armament.Info.TargetStances;
stances |= armament.Info.TargetRelationships;
return stances;
}

View File

@@ -326,7 +326,7 @@ namespace OpenRA.Mods.Common.Traits
return activeTargetPriorities.Any(ati =>
{
// Incompatible stances
if (!ati.ValidStances.HasStance(self.Owner.RelationshipWith(owner)))
if (!ati.ValidRelationships.HasStance(self.Owner.RelationshipWith(owner)))
return false;
// Incompatible target types
@@ -391,7 +391,7 @@ namespace OpenRA.Mods.Common.Traits
return false;
// Incompatible stances
if (!ati.ValidStances.HasStance(self.Owner.RelationshipWith(owner)))
if (!ati.ValidRelationships.HasStance(self.Owner.RelationshipWith(owner)))
return false;
// Incompatible target types

View File

@@ -23,8 +23,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Target types that can't be AutoTargeted.", "Overrules ValidTargets.")]
public readonly BitSet<TargetableType> InvalidTargets;
[Desc("Stances between actor's and target's owner which can be AutoTargeted.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Ally | PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Relationships between actor's and target's owner needed for AutoTargeting.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally | PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("ValidTargets with larger priorities will be AutoTargeted before lower priorities.")]
public readonly int Priority = 1;

View File

@@ -37,8 +37,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Should visibility (Shroud, Fog, Cloak, etc) be considered when searching for capturable targets?")]
public readonly bool CheckCaptureTargetsForVisibility = true;
[Desc("Player stances that capturers should attempt to target.")]
public readonly PlayerRelationship CapturableStances = PlayerRelationship.Enemy | PlayerRelationship.Neutral;
[Desc("Player relationships that capturers should attempt to target.")]
public readonly PlayerRelationship CapturableRelationships = PlayerRelationship.Enemy | PlayerRelationship.Neutral;
public override object Create(ActorInitializer init) { return new CaptureManagerBotModule(init.Self, this); }
}
@@ -133,7 +133,7 @@ namespace OpenRA.Mods.Common.Traits
return;
var randPlayer = world.Players.Where(p => !p.Spectating
&& Info.CapturableStances.HasStance(player.RelationshipWith(p))).Random(world.LocalRandom);
&& Info.CapturableRelationships.HasStance(player.RelationshipWith(p))).Random(world.LocalRandom);
var targetOptions = Info.CheckCaptureTargetsForVisibility
? GetVisibleActorsBelongingToPlayer(randPlayer)

View File

@@ -75,13 +75,7 @@ namespace OpenRA.Mods.Common.Traits
void INotifyCreated.Created(Actor self)
{
// Display only the first level of priority
var priorityExits = self.Info.TraitInfos<ExitInfo>()
.GroupBy(e => e.Priority)
.FirstOrDefault();
var exits = priorityExits != null ? priorityExits.ToArray() : new ExitInfo[0];
self.World.Add(new RallyPointIndicator(self, this, exits));
self.World.Add(new RallyPointIndicator(self, this));
}
public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner)

View File

@@ -22,8 +22,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("CaptureTypes (from the Captures trait) that are able to capture this.")]
public readonly BitSet<CaptureType> Types = default(BitSet<CaptureType>);
[Desc("What diplomatic stances can be captured by this actor.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("What player relationships the target's owner needs to be captured by this actor.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Cancel the actor's current activity when getting captured.")]
public readonly bool CancelActivity = false;

View File

@@ -50,7 +50,7 @@ namespace OpenRA.Mods.Common.Traits
// Actors with FrozenUnderFog should therefore not disable the Capturable trait.
var stance = captor.Owner.RelationshipWith(frozenActor.Owner);
return frozenActor.Info.TraitInfos<CapturableInfo>()
.Any(c => c.ValidStances.HasStance(stance) && captures.Info.CaptureTypes.Overlaps(c.Types));
.Any(c => c.ValidRelationships.HasStance(stance) && captures.Info.CaptureTypes.Overlaps(c.Types));
}
}
@@ -108,13 +108,13 @@ namespace OpenRA.Mods.Common.Traits
allyCapturableTypes = neutralCapturableTypes = enemyCapturableTypes = default(BitSet<CaptureType>);
foreach (var c in enabledCapturable)
{
if (c.Info.ValidStances.HasStance(PlayerRelationship.Ally))
if (c.Info.ValidRelationships.HasStance(PlayerRelationship.Ally))
allyCapturableTypes = allyCapturableTypes.Union(c.Info.Types);
if (c.Info.ValidStances.HasStance(PlayerRelationship.Neutral))
if (c.Info.ValidRelationships.HasStance(PlayerRelationship.Neutral))
neutralCapturableTypes = neutralCapturableTypes.Union(c.Info.Types);
if (c.Info.ValidStances.HasStance(PlayerRelationship.Enemy))
if (c.Info.ValidRelationships.HasStance(PlayerRelationship.Enemy))
enemyCapturableTypes = enemyCapturableTypes.Union(c.Info.Types);
}
}

View File

@@ -43,8 +43,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Experience granted to the capturing player.")]
public readonly int PlayerExperience = 0;
[Desc("Stance that the structure's previous owner needs to have for the capturing player to receive Experience.")]
public readonly PlayerRelationship PlayerExperienceStances = PlayerRelationship.Enemy;
[Desc("Relationships that the structure's previous owner needs to have for the capturing player to receive Experience.")]
public readonly PlayerRelationship PlayerExperienceRelationships = PlayerRelationship.Enemy;
[Desc("Cursor to display when the health of the target actor is above the sabotage threshold.")]
public readonly string SabotageCursor = "capture";

View File

@@ -30,8 +30,8 @@ namespace OpenRA.Mods.Common.Traits
"Ignored if 0 (actors are selected regardless of vertical distance).")]
public readonly WDist MaximumVerticalOffset = WDist.Zero;
[Desc("What diplomatic stances are affected.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Ally;
[Desc("What player relationships are affected.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally;
[Desc("Condition is applied permanently to this actor.")]
public readonly bool AffectsParent = false;
@@ -111,7 +111,7 @@ namespace OpenRA.Mods.Common.Traits
return;
var stance = self.Owner.RelationshipWith(a.Owner);
if (!Info.ValidStances.HasStance(stance))
if (!Info.ValidRelationships.HasStance(stance))
return;
var external = a.TraitsImplementing<ExternalCondition>()
@@ -135,7 +135,7 @@ namespace OpenRA.Mods.Common.Traits
if ((produced.CenterPosition - self.CenterPosition).HorizontalLengthSquared <= Info.Range.LengthSquared)
{
var stance = self.Owner.RelationshipWith(produced.Owner);
if (!Info.ValidStances.HasStance(stance))
if (!Info.ValidRelationships.HasStance(stance))
return;
var external = produced.TraitsImplementing<ExternalCondition>()

View File

@@ -17,8 +17,8 @@ namespace OpenRA.Mods.Common.Traits
{
public class CreatesShroudInfo : AffectsShroudInfo
{
[Desc("Stance the watching player needs to see the generated shroud.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Relationship the watching player needs to see the generated shroud.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Neutral | PlayerRelationship.Enemy;
public override object Create(ActorInitializer init) { return new CreatesShroud(init.Self, this); }
}
@@ -43,7 +43,7 @@ namespace OpenRA.Mods.Common.Traits
protected override void AddCellsToPlayerShroud(Actor self, Player p, PPos[] uv)
{
if (!info.ValidStances.HasStance(self.Owner.RelationshipWith(p)))
if (!info.ValidRelationships.HasStance(self.Owner.RelationshipWith(p)))
return;
p.Shroud.AddSource(this, Shroud.SourceType.Shroud, uv);

View File

@@ -99,7 +99,7 @@ namespace OpenRA.Mods.Common.Traits
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
{
var targetInfo = target.Info.TraitInfoOrDefault<AcceptsDeliveredCashInfo>();
if (targetInfo == null || !targetInfo.ValidStances.HasStance(target.Owner.RelationshipWith(self.Owner)))
if (targetInfo == null || !targetInfo.ValidRelationships.HasStance(target.Owner.RelationshipWith(self.Owner)))
return false;
if (targetInfo.ValidTypes.Count == 0)
@@ -112,7 +112,7 @@ namespace OpenRA.Mods.Common.Traits
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
{
var targetInfo = target.Info.TraitInfoOrDefault<AcceptsDeliveredCashInfo>();
if (targetInfo == null || !targetInfo.ValidStances.HasStance(target.Owner.RelationshipWith(self.Owner)))
if (targetInfo == null || !targetInfo.ValidRelationships.HasStance(target.Owner.RelationshipWith(self.Owner)))
return false;
if (targetInfo.ValidTypes.Count == 0)

View File

@@ -109,7 +109,7 @@ namespace OpenRA.Mods.Common.Traits
return false;
var targetInfo = target.Info.TraitInfoOrDefault<AcceptsDeliveredExperienceInfo>();
if (targetInfo == null || !targetInfo.ValidStances.HasStance(target.Owner.RelationshipWith(self.Owner)))
if (targetInfo == null || !targetInfo.ValidRelationships.HasStance(target.Owner.RelationshipWith(self.Owner)))
return false;
if (targetInfo.ValidTypes.Count == 0)
@@ -129,7 +129,7 @@ namespace OpenRA.Mods.Common.Traits
return false;
var targetInfo = target.Info.TraitInfoOrDefault<AcceptsDeliveredExperienceInfo>();
if (targetInfo == null || !targetInfo.ValidStances.HasStance(target.Owner.RelationshipWith(self.Owner)))
if (targetInfo == null || !targetInfo.ValidRelationships.HasStance(target.Owner.RelationshipWith(self.Owner)))
return false;
if (targetInfo.ValidTypes.Count == 0)

View File

@@ -47,8 +47,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Color to use for the target line.")]
public readonly Color TargetLineColor = Color.Crimson;
public readonly PlayerRelationship TargetStances = PlayerRelationship.Enemy | PlayerRelationship.Neutral;
public readonly PlayerRelationship ForceTargetStances = PlayerRelationship.Enemy | PlayerRelationship.Neutral | PlayerRelationship.Ally;
public readonly PlayerRelationship TargetRelationships = PlayerRelationship.Enemy | PlayerRelationship.Neutral;
public readonly PlayerRelationship ForceTargetRelationships = PlayerRelationship.Enemy | PlayerRelationship.Neutral | PlayerRelationship.Ally;
[Desc("Cursor to display when hovering over a demolishable target.")]
public readonly string Cursor = "c4";
@@ -118,10 +118,10 @@ namespace OpenRA.Mods.Common.Traits
return false;
var stance = target.Owner.RelationshipWith(self.Owner);
if (!info.TargetStances.HasStance(stance) && !modifiers.HasModifier(TargetModifiers.ForceAttack))
if (!info.TargetRelationships.HasStance(stance) && !modifiers.HasModifier(TargetModifiers.ForceAttack))
return false;
if (!info.ForceTargetStances.HasStance(stance) && modifiers.HasModifier(TargetModifiers.ForceAttack))
if (!info.ForceTargetRelationships.HasStance(stance) && modifiers.HasModifier(TargetModifiers.ForceAttack))
return false;
return target.TraitsImplementing<IDemolishable>().Any(i => i.IsValidTarget(target, self));
@@ -130,10 +130,10 @@ namespace OpenRA.Mods.Common.Traits
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
{
var stance = target.Owner.RelationshipWith(self.Owner);
if (!info.TargetStances.HasStance(stance) && !modifiers.HasModifier(TargetModifiers.ForceAttack))
if (!info.TargetRelationships.HasStance(stance) && !modifiers.HasModifier(TargetModifiers.ForceAttack))
return false;
if (!info.ForceTargetStances.HasStance(stance) && modifiers.HasModifier(TargetModifiers.ForceAttack))
if (!info.ForceTargetRelationships.HasStance(stance) && modifiers.HasModifier(TargetModifiers.ForceAttack))
return false;
return target.Info.TraitInfos<IDemolishableInfo>().Any(i => i.IsValidTarget(target.Info, self));

View File

@@ -33,8 +33,8 @@ namespace OpenRA.Mods.Common.Traits
"Possible values are Exit, Suicide, Dispose.")]
public readonly EnterBehaviour EnterBehaviour = EnterBehaviour.Dispose;
[Desc("What diplomatic stances allow target to be repaired by this actor.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Ally;
[Desc("What player relationship the target's owner needs to be repaired by this actor.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally;
[Desc("Sound to play when repairing is done.")]
public readonly string RepairSound = null;
@@ -117,7 +117,7 @@ namespace OpenRA.Mods.Common.Traits
if (!engineerRepairable.Info.Types.IsEmpty && !engineerRepairable.Info.Types.Overlaps(info.Types))
return false;
if (!info.ValidStances.HasStance(target.Owner.RelationshipWith(self.Owner)))
if (!info.ValidRelationships.HasStance(target.Owner.RelationshipWith(self.Owner)))
return false;
if (target.GetDamageState() == DamageState.Undamaged)
@@ -139,7 +139,7 @@ namespace OpenRA.Mods.Common.Traits
if (!engineerRepairable.Types.IsEmpty && !engineerRepairable.Types.Overlaps(info.Types))
return false;
if (!info.ValidStances.HasStance(target.Owner.RelationshipWith(self.Owner)))
if (!info.ValidRelationships.HasStance(target.Owner.RelationshipWith(self.Owner)))
return false;
if (target.DamageState == DamageState.Undamaged)

View File

@@ -23,8 +23,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Percentage of the killed actor's Cost or CustomSellValue to be given.")]
public readonly int Percentage = 10;
[Desc("Stance the attacking player needs to receive the bounty.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Player relationships the attacking player needs to receive the bounty.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Whether to show a floating text announcing the won bounty.")]
public readonly bool ShowBounty = true;
@@ -64,7 +64,7 @@ namespace OpenRA.Mods.Common.Traits
if (e.Attacker == null || e.Attacker.Disposed || IsTraitDisabled)
return;
if (!Info.ValidStances.HasStance(e.Attacker.Owner.RelationshipWith(self.Owner)))
if (!Info.ValidRelationships.HasStance(e.Attacker.Owner.RelationshipWith(self.Owner)))
return;
if (!Info.DeathTypes.IsEmpty && !e.Damage.DamageTypes.Overlaps(Info.DeathTypes))

View File

@@ -21,8 +21,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("If -1, use the value of the unit cost.")]
public readonly int Experience = -1;
[Desc("Stance the attacking player needs to receive the experience.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Player relationships the attacking player needs to receive the experience.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Percentage of the `Experience` value that is being granted to the killing actor.")]
public readonly int ActorExperienceModifier = 10000;
@@ -59,7 +59,7 @@ namespace OpenRA.Mods.Common.Traits
if (exp == 0 || e.Attacker == null || e.Attacker.Disposed)
return;
if (!info.ValidStances.HasStance(e.Attacker.Owner.RelationshipWith(self.Owner)))
if (!info.ValidRelationships.HasStance(e.Attacker.Owner.RelationshipWith(self.Owner)))
return;
exp = Util.ApplyPercentageModifiers(exp, experienceModifiers);

View File

@@ -9,6 +9,8 @@
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
@@ -28,6 +30,9 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Chance (out of 100) the unit has to enter panic mode when attacking.")]
public readonly int AttackPanicChance = 20;
[Desc("The terrain types that this actor should avoid running on to while panicking.")]
public readonly HashSet<string> AvoidTerrainTypes = new HashSet<string>();
[SequenceReference(prefix: true)]
public readonly string PanicSequencePrefix = "panic-";
@@ -39,6 +44,7 @@ namespace OpenRA.Mods.Common.Traits
readonly ScaredyCatInfo info;
readonly Mobile mobile;
readonly Actor self;
readonly Func<CPos, bool> avoidTerrainFilter;
[Sync]
int panicStartedTick;
@@ -52,6 +58,9 @@ namespace OpenRA.Mods.Common.Traits
this.self = self;
this.info = info;
mobile = self.Trait<Mobile>();
if (info.AvoidTerrainTypes.Count > 0)
avoidTerrainFilter = c => info.AvoidTerrainTypes.Contains(self.World.Map.GetTerrainInfo(c).Type);
}
public void Panic()
@@ -79,7 +88,10 @@ namespace OpenRA.Mods.Common.Traits
if (!Panicking)
return;
mobile.Nudge(self);
// Note: This is just a modified copy of Mobile.Nudge
var cell = mobile.GetAdjacentCell(self.Location, avoidTerrainFilter);
if (cell != null)
self.QueueActivity(false, mobile.MoveTo(cell.Value, 0));
}
void INotifyDamage.Damaged(Actor self, AttackInfo e)

View File

@@ -19,8 +19,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Range of the deflection.")]
public readonly WDist Range = WDist.Zero;
[Desc("What diplomatic stances are affected.")]
public readonly PlayerRelationship DeflectionStances = PlayerRelationship.Ally | PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("What player relationships are affected.")]
public readonly PlayerRelationship DeflectionRelationships = PlayerRelationship.Ally | PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Chance of deflecting missiles.")]
public readonly int Chance = 100;
@@ -31,7 +31,7 @@ namespace OpenRA.Mods.Common.Traits
public class JamsMissiles : ConditionalTrait<JamsMissilesInfo>
{
public WDist Range { get { return IsTraitDisabled ? WDist.Zero : Info.Range; } }
public PlayerRelationship DeflectionStances { get { return Info.DeflectionStances; } }
public PlayerRelationship DeflectionStances { get { return Info.DeflectionRelationships; } }
public int Chance { get { return Info.Chance; } }
public JamsMissiles(JamsMissilesInfo info)

View File

@@ -364,14 +364,14 @@ namespace OpenRA.Mods.Common.Traits
self.QueueActivity(false, MoveTo(cell.Value, 0));
}
public CPos? GetAdjacentCell(CPos nextCell)
public CPos? GetAdjacentCell(CPos nextCell, Func<CPos, bool> preferToAvoid = null)
{
var availCells = new List<CPos>();
var notStupidCells = new List<CPos>();
foreach (CVec direction in CVec.Directions)
{
var p = ToCell + direction;
if (CanEnterCell(p) && CanStayInCell(p))
if (CanEnterCell(p) && CanStayInCell(p) && (preferToAvoid == null || !preferToAvoid(p)))
availCells.Add(p);
else if (p != nextCell && p != ToCell)
notStupidCells.Add(p);

View File

@@ -20,8 +20,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("This actor will remain visible (but not updated visually) under fog, once discovered.")]
public class FrozenUnderFogInfo : TraitInfo, Requires<BuildingInfo>, IDefaultVisibilityInfo
{
[Desc("Players with these stances can always see the actor.")]
public readonly PlayerRelationship AlwaysVisibleStances = PlayerRelationship.Ally;
[Desc("Players with these relationships can always see the actor.")]
public readonly PlayerRelationship AlwaysVisibleRelationships = PlayerRelationship.Ally;
public override object Create(ActorInitializer init) { return new FrozenUnderFog(init, this); }
}
@@ -106,7 +106,7 @@ namespace OpenRA.Mods.Common.Traits
return true;
var stance = self.Owner.RelationshipWith(byPlayer);
return info.AlwaysVisibleStances.HasStance(stance) || IsVisibleInner(self, byPlayer);
return info.AlwaysVisibleRelationships.HasStance(stance) || IsVisibleInner(self, byPlayer);
}
void ITick.Tick(Actor self)

View File

@@ -19,8 +19,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("The actor stays invisible under the shroud.")]
public class HiddenUnderShroudInfo : TraitInfo, IDefaultVisibilityInfo
{
[Desc("Players with these stances can always see the actor.")]
public readonly PlayerRelationship AlwaysVisibleStances = PlayerRelationship.Ally;
[Desc("Players with these relationships can always see the actor.")]
public readonly PlayerRelationship AlwaysVisibleRelationships = PlayerRelationship.Ally;
[Desc("Possible values are CenterPosition (reveal when the center is visible) and ",
"Footprint (reveal when any footprint cell is visible).")]
@@ -56,7 +56,7 @@ namespace OpenRA.Mods.Common.Traits
return true;
var stance = self.Owner.RelationshipWith(byPlayer);
return Info.AlwaysVisibleStances.HasStance(stance) || IsVisibleInner(self, byPlayer);
return Info.AlwaysVisibleRelationships.HasStance(stance) || IsVisibleInner(self, byPlayer);
}
IEnumerable<IRenderable> IRenderModifier.ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r)

View File

@@ -12,6 +12,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
@@ -58,15 +59,19 @@ namespace OpenRA.Mods.Common.Traits
[NotificationReference("Speech")]
public readonly string LeaveNotification = null;
public override object Create(ActorInitializer init) { return new MissionObjectives(init.World, this); }
public override object Create(ActorInitializer init) { return new MissionObjectives(init.Self.Owner, this); }
}
public class MissionObjectives : INotifyWinStateChanged, ISync, IResolveOrder
public class MissionObjectives : INotifyWinStateChanged, ISync, IResolveOrder, IWorldLoaded
{
public readonly MissionObjectivesInfo Info;
readonly List<MissionObjective> objectives = new List<MissionObjective>();
readonly Player player;
public ReadOnlyList<MissionObjective> Objectives;
Player[] enemies;
Player[] allies;
[Sync]
public int ObjectivesHash
{
@@ -83,12 +88,21 @@ namespace OpenRA.Mods.Common.Traits
// The player's WinState is only updated when his allies have all completed their objective as well.
public WinState WinStateCooperative { get; private set; }
public MissionObjectives(World world, MissionObjectivesInfo info)
public MissionObjectives(Player player, MissionObjectivesInfo info)
{
Info = info;
this.player = player;
Objectives = new ReadOnlyList<MissionObjective>(objectives);
}
void IWorldLoaded.WorldLoaded(World w, WorldRenderer wr)
{
// Players and NonCombatants are fixed once the game starts, but the result of IsAlliedWith
// may change once players are marked as spectators, so cache these
allies = player.World.Players.Where(p => !p.NonCombatant && player.IsAlliedWith(p)).ToArray();
enemies = player.World.Players.Where(p => !p.NonCombatant && player.RelationshipWith(p) == PlayerRelationship.Enemy).ToArray();
}
public int Add(Player player, string description, string type, bool required = true, bool inhibitAnnouncement = false)
{
var newID = objectives.Count;
@@ -140,10 +154,9 @@ namespace OpenRA.Mods.Common.Traits
void CheckIfGameIsOver(Player player)
{
var players = player.World.Players.Where(p => !p.NonCombatant);
var gameOver = players.All(p => p.WinState != WinState.Undefined || !p.HasObjectives);
var gameOver = player.World.Players.All(p => p.NonCombatant || p.WinState != WinState.Undefined || !p.HasObjectives);
if (gameOver)
{
Game.RunAfterDelay(Info.GameOverDelay, () =>
{
if (!Game.IsCurrentWorld(player.World))
@@ -153,17 +166,14 @@ namespace OpenRA.Mods.Common.Traits
player.World.SetPauseState(true);
player.World.PauseStateLocked = true;
});
}
}
void INotifyWinStateChanged.OnPlayerWon(Player player)
{
var players = player.World.Players.Where(p => !p.NonCombatant);
var enemies = players.Where(p => !p.IsAlliedWith(player));
if (Info.Cooperative)
{
WinStateCooperative = WinState.Won;
var allies = players.Where(p => p.IsAlliedWith(player));
if (allies.All(p => p.PlayerActor.Trait<MissionObjectives>().WinStateCooperative == WinState.Won))
{
@@ -193,13 +203,9 @@ namespace OpenRA.Mods.Common.Traits
void INotifyWinStateChanged.OnPlayerLost(Player player)
{
var players = player.World.Players.Where(p => !p.NonCombatant);
var enemies = players.Where(p => !p.IsAlliedWith(player));
if (Info.Cooperative)
{
WinStateCooperative = WinState.Lost;
var allies = players.Where(p => p.IsAlliedWith(player));
if (allies.Any(p => p.PlayerActor.Trait<MissionObjectives>().WinStateCooperative == WinState.Lost))
{

View File

@@ -20,8 +20,8 @@ namespace OpenRA.Mods.Common.Traits.Radar
{
public readonly bool UseLocation = false;
[Desc("Player stances who can view this actor on radar.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Ally | PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Player relationships who can view this actor on radar.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally | PlayerRelationship.Neutral | PlayerRelationship.Enemy;
public override object Create(ActorInitializer init) { return new AppearsOnRadar(this); }
}
@@ -42,7 +42,7 @@ namespace OpenRA.Mods.Common.Traits.Radar
public void PopulateRadarSignatureCells(Actor self, List<(CPos Cell, Color Color)> destinationBuffer)
{
var viewer = self.World.RenderPlayer ?? self.World.LocalPlayer;
if (IsTraitDisabled || (viewer != null && !Info.ValidStances.HasStance(self.Owner.RelationshipWith(viewer))))
if (IsTraitDisabled || (viewer != null && !Info.ValidRelationships.HasStance(self.Owner.RelationshipWith(viewer))))
return;
var color = Game.Settings.Game.UsePlayerStanceColors ? self.Owner.PlayerStanceColor(self) : self.Owner.Color;

View File

@@ -20,7 +20,7 @@ namespace OpenRA.Mods.Common.Traits.Render
class CashTricklerBarInfo : TraitInfo, Requires<CashTricklerInfo>
{
[Desc("Defines to which players the bar is to be shown.")]
public readonly PlayerRelationship DisplayStances = PlayerRelationship.Ally;
public readonly PlayerRelationship DisplayRelationships = PlayerRelationship.Ally;
public readonly Color Color = Color.Magenta;
@@ -43,7 +43,7 @@ namespace OpenRA.Mods.Common.Traits.Render
float ISelectionBar.GetValue()
{
var viewer = self.World.RenderPlayer ?? self.World.LocalPlayer;
if (viewer != null && !info.DisplayStances.HasStance(self.Owner.RelationshipWith(viewer)))
if (viewer != null && !info.DisplayRelationships.HasStance(self.Owner.RelationshipWith(viewer)))
return 0;
var complete = cashTricklers.Min(ct => (float)ct.Ticks / ct.Info.Interval);

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.Traits.Render
class SupportPowerChargeBarInfo : ConditionalTraitInfo
{
[Desc("Defines to which players the bar is to be shown.")]
public readonly PlayerRelationship DisplayStances = PlayerRelationship.Ally;
public readonly PlayerRelationship DisplayRelationships = PlayerRelationship.Ally;
public readonly Color Color = Color.Magenta;
@@ -48,7 +48,7 @@ namespace OpenRA.Mods.Common.Traits.Render
return 0;
var viewer = self.World.RenderPlayer ?? self.World.LocalPlayer;
if (viewer != null && !Info.DisplayStances.HasStance(self.Owner.RelationshipWith(viewer)))
if (viewer != null && !Info.DisplayRelationships.HasStance(self.Owner.RelationshipWith(viewer)))
return 0;
return 1 - (float)power.RemainingTicks / power.TotalTicks;

View File

@@ -24,8 +24,8 @@ namespace OpenRA.Mods.Common.Traits.Render
[Desc("Position in the actor's selection box to draw the decoration.")]
public readonly string Position = "TopLeft";
[Desc("Player stances who can view the decoration.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Ally;
[Desc("Player relationships who can view the decoration.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally;
[Desc("Should this be visible only when selected?")]
public readonly bool RequiresSelection = false;
@@ -82,7 +82,7 @@ namespace OpenRA.Mods.Common.Traits.Render
if (self.World.RenderPlayer != null)
{
var stance = self.Owner.RelationshipWith(self.World.RenderPlayer);
if (!Info.ValidStances.HasStance(stance))
if (!Info.ValidRelationships.HasStance(stance))
return false;
}

View File

@@ -148,7 +148,12 @@ namespace OpenRA.Mods.Common.Traits.Render
void INotifyAttack.PreparingAttack(Actor self, in Target target, Armament a, Barrel barrel)
{
Attacking(self, target, a);
// Lambdas can't use 'in' variables, so capture a copy for later
var attackTarget = target;
// HACK: The FrameEndTask makes sure that this runs after Tick(), preventing that from
// overriding the animation when an infantry unit stops to attack
self.World.AddFrameEndTask(_ => Attacking(self, attackTarget, a));
}
void INotifyAttack.Attacking(Actor self, in Target target, Armament a, Barrel barrel) { }
@@ -160,10 +165,6 @@ namespace OpenRA.Mods.Common.Traits.Render
protected virtual void Tick(Actor self)
{
// Attacking takes care of reverting back to PlayStandAnimation
if (state == AnimationState.Attacking)
return;
if (rsm != null)
{
if (wasModifying != rsm.IsModifyingSequence)

View File

@@ -40,9 +40,9 @@ namespace OpenRA.Mods.Common.Traits.Render
[Desc("If set, the color of the owning player will be used instead of `Color`.")]
public readonly bool UsePlayerColor = false;
[Desc("Stances of players which will be able to see the circle.",
[Desc("Player relationships which will be able to see the circle.",
"Valid values are combinations of `None`, `Ally`, `Enemy` and `Neutral`.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Ally;
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally;
[Desc("When to show the range circle. Valid values are `Always`, and `WhenSelected`")]
public readonly RangeCircleVisibility Visible = RangeCircleVisibility.WhenSelected;
@@ -91,7 +91,7 @@ namespace OpenRA.Mods.Common.Traits.Render
return false;
var p = self.World.RenderPlayer;
return p == null || Info.ValidStances.HasStance(self.Owner.RelationshipWith(p)) || (p.Spectating && !p.NonCombatant);
return p == null || Info.ValidRelationships.HasStance(self.Owner.RelationshipWith(p)) || (p.Spectating && !p.NonCombatant);
}
}

View File

@@ -18,8 +18,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Reveal this actor's last position when killed.")]
public class RevealOnDeathInfo : ConditionalTraitInfo
{
[Desc("Stances relative to the actors' owner that shroud will be revealed for.")]
public readonly PlayerRelationship RevealForStances = PlayerRelationship.Ally;
[Desc("Relationships relative to the actors' owner that shroud will be revealed for.")]
public readonly PlayerRelationship RevealForRelationships = PlayerRelationship.Ally;
[Desc("Duration of the reveal.")]
public readonly int Duration = 25;
@@ -66,7 +66,7 @@ namespace OpenRA.Mods.Common.Traits
w.Add(new RevealShroudEffect(self.CenterPosition, info.Radius,
info.RevealGeneratedShroud ? Shroud.SourceType.Visibility : Shroud.SourceType.PassiveVisibility,
owner, info.RevealForStances, duration: info.Duration));
owner, info.RevealForRelationships, duration: info.Duration));
});
}
}

View File

@@ -21,8 +21,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("The armament types which trigger revealing.")]
public readonly string[] ArmamentNames = { "primary", "secondary" };
[Desc("Stances relative to the target player this actor will be revealed to during firing.")]
public readonly PlayerRelationship RevealForStancesRelativeToTarget = PlayerRelationship.Ally;
[Desc("Player relationships relative to the target player this actor will be revealed to during firing.")]
public readonly PlayerRelationship RevealForRelationships = PlayerRelationship.Ally;
[Desc("Duration of the reveal.")]
public readonly int Duration = 25;
@@ -60,7 +60,7 @@ namespace OpenRA.Mods.Common.Traits
{
self.World.AddFrameEndTask(w => w.Add(new RevealShroudEffect(self.CenterPosition, info.Radius,
info.RevealGeneratedShroud ? Shroud.SourceType.Visibility : Shroud.SourceType.PassiveVisibility,
targetPlayer, info.RevealForStancesRelativeToTarget, duration: info.Duration)));
targetPlayer, info.RevealForRelationships, duration: info.Duration)));
}
}

View File

@@ -16,8 +16,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Reveals shroud and fog across the whole map while active.")]
public class RevealsMapInfo : ConditionalTraitInfo
{
[Desc("Stance the watching player needs to see the shroud removed.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Ally;
[Desc("Relationships the watching player needs to see the shroud removed.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally;
[Desc("Can this actor reveal shroud generated by the `GeneratesShroud` trait?")]
public readonly bool RevealGeneratedShroud = true;
@@ -38,7 +38,7 @@ namespace OpenRA.Mods.Common.Traits
protected void AddCellsToPlayerShroud(Actor self, Player p, PPos[] uv)
{
if (!Info.ValidStances.HasStance(self.Owner.RelationshipWith(p)))
if (!Info.ValidRelationships.HasStance(self.Owner.RelationshipWith(p)))
return;
p.Shroud.AddSource(this, type, uv);

View File

@@ -17,8 +17,8 @@ namespace OpenRA.Mods.Common.Traits
{
public class RevealsShroudInfo : AffectsShroudInfo
{
[Desc("Stance the watching player needs to see the shroud removed.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Ally;
[Desc("Relationships the watching player needs to see the shroud removed.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally;
[Desc("Can this actor reveal shroud generated by the GeneratesShroud trait?")]
public readonly bool RevealGeneratedShroud = true;
@@ -49,7 +49,7 @@ namespace OpenRA.Mods.Common.Traits
protected override void AddCellsToPlayerShroud(Actor self, Player p, PPos[] uv)
{
if (!info.ValidStances.HasStance(self.Owner.RelationshipWith(p)))
if (!info.ValidRelationships.HasStance(self.Owner.RelationshipWith(p)))
return;
p.Shroud.AddSource(this, type, uv);

View File

@@ -21,8 +21,8 @@ namespace OpenRA.Mods.Common.Traits.Sound
[Desc("Voice to play.")]
public readonly string Voice = null;
[Desc("Player stances who can hear this voice.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Ally | PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Player relationships who can hear this voice.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally | PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Play the voice to the owning player even if Stance.Ally is not included in ValidStances.")]
public readonly bool PlayToOwner = true;
@@ -53,7 +53,7 @@ namespace OpenRA.Mods.Common.Traits.Sound
if (player == null)
return;
if (Info.ValidStances.HasStance(self.Owner.RelationshipWith(player)))
if (Info.ValidRelationships.HasStance(self.Owner.RelationshipWith(player)))
self.PlayVoice(Info.Voice);
else if (Info.PlayToOwner && self.Owner == player)
self.PlayVoice(Info.Voice);

View File

@@ -39,8 +39,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Sound to instantly play at the targeted area.")]
public readonly string OnFireSound = null;
[Desc("Player stances which condition can be applied to.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Ally;
[Desc("Player relationships which condition can be applied to.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally;
[SequenceReference]
[Desc("Sequence to play for granting actor when activated.",
@@ -96,7 +96,7 @@ namespace OpenRA.Mods.Common.Traits
return units.Distinct().Where(a =>
{
if (!info.ValidStances.HasStance(Self.Owner.RelationshipWith(a.Owner)))
if (!info.ValidRelationships.HasStance(Self.Owner.RelationshipWith(a.Owner)))
return false;
return a.TraitsImplementing<ExternalCondition>()

View File

@@ -94,8 +94,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Can the camera reveal shroud generated by the GeneratesShroud trait?")]
public readonly bool RevealGeneratedShroud = true;
[Desc("Reveal cells to players with these stances only.")]
public readonly PlayerRelationship CameraStances = PlayerRelationship.Ally;
[Desc("Reveal cells to players with these relationships only.")]
public readonly PlayerRelationship CameraRelationships = PlayerRelationship.Ally;
[Desc("Amount of time before detonation to spawn the camera.")]
public readonly int CameraSpawnAdvance = 25;
@@ -180,7 +180,7 @@ namespace OpenRA.Mods.Common.Traits
var type = info.RevealGeneratedShroud ? Shroud.SourceType.Visibility
: Shroud.SourceType.PassiveVisibility;
self.World.AddFrameEndTask(w => w.Add(new RevealShroudEffect(targetPosition, info.CameraRange, type, self.Owner, info.CameraStances,
self.World.AddFrameEndTask(w => w.Add(new RevealShroudEffect(targetPosition, info.CameraRange, type, self.Owner, info.CameraRelationships,
info.FlightDelay - info.CameraSpawnAdvance, info.CameraSpawnAdvance + info.CameraRemoveDelay)));
}

View File

@@ -77,7 +77,7 @@ namespace OpenRA.Mods.Common.Traits
public readonly string IncomingSpeechNotification = null;
[Desc("Defines to which players the timer is shown.")]
public readonly PlayerRelationship DisplayTimerStances = PlayerRelationship.None;
public readonly PlayerRelationship DisplayTimerRelationships = PlayerRelationship.None;
[Desc("Beacons are only supported on the Airstrike, Paratroopers, and Nuke powers")]
public readonly bool DisplayBeacon = false;

View File

@@ -20,8 +20,8 @@ namespace OpenRA.Mods.Common.Traits
[Translate]
public readonly string Description = "";
[Desc("Player stances who can view the description.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Ally | PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Player relationships who can view the description.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally | PlayerRelationship.Neutral | PlayerRelationship.Enemy;
public override object Create(ActorInitializer init) { return new TooltipDescription(init.Self, this); }
}
@@ -53,7 +53,7 @@ namespace OpenRA.Mods.Common.Traits
if (Owner == null || forPlayer == null)
return false;
return Info.ValidStances.HasStance(Owner.RelationshipWith(forPlayer));
return Info.ValidRelationships.HasStance(Owner.RelationshipWith(forPlayer));
}
public string TooltipText

View File

@@ -9,6 +9,8 @@
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
@@ -27,6 +29,9 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Maximum amount of ticks the actor will sit idly before starting to wander.")]
public readonly int MaxMoveDelay = 0;
[Desc("The terrain types that this actor should avoid wandering on to.")]
public readonly HashSet<string> AvoidTerrainTypes = new HashSet<string>();
public override object Create(ActorInitializer init) { return new Wanders(init.Self, this); }
}
@@ -75,8 +80,8 @@ namespace OpenRA.Mods.Common.Traits
return;
var targetCell = PickTargetLocation();
if (targetCell != CPos.Zero)
DoAction(self, targetCell);
if (targetCell.HasValue)
DoAction(self, targetCell.Value);
}
void INotifyIdle.TickIdle(Actor self)
@@ -84,7 +89,7 @@ namespace OpenRA.Mods.Common.Traits
TickIdle(self);
}
CPos PickTargetLocation()
CPos? PickTargetLocation()
{
var target = self.CenterPosition + new WVec(0, -1024 * effectiveMoveRadius, 0).Rotate(WRot.FromFacing(self.World.SharedRandom.Next(255)));
var targetCell = self.World.Map.CellContaining(target);
@@ -95,7 +100,15 @@ namespace OpenRA.Mods.Common.Traits
if (++ticksIdle % info.ReduceMoveRadiusDelay == 0)
effectiveMoveRadius--;
return CPos.Zero; // We'll be back the next tick; better to sit idle for a few seconds than prolong this tick indefinitely with a loop
// We'll be back the next tick; better to sit idle for a few seconds than prolong this tick indefinitely with a loop
return null;
}
if (info.AvoidTerrainTypes.Count > 0)
{
var terrainType = self.World.Map.GetTerrainInfo(targetCell).Type;
if (Info.AvoidTerrainTypes.Contains(terrainType))
return null;
}
ticksIdle = 0;

View File

@@ -60,6 +60,8 @@ namespace OpenRA.Mods.Common.Traits
IsBot = client.Bot != null,
FactionName = resolvedFaction.Name,
FactionId = resolvedFaction.InternalName,
DisplayFactionName = clientFaction.Name,
DisplayFactionId = clientFaction.InternalName,
Color = client.Color,
Team = client.Team,
SpawnPoint = resolvedSpawnPoint,

View File

@@ -78,10 +78,7 @@ namespace OpenRA.Mods.Common.Traits
for (var x = 0; x < TerrainTemplate.Size.X; x++)
{
var tile = new TerrainTile(TerrainTemplate.Id, (byte)i++);
var tileInfo = world.Map.Rules.TileSet.GetTileInfo(tile);
// Empty tile
if (tileInfo == null)
if (!world.Map.Rules.TileSet.TryGetTileInfo(tile, out var tileInfo))
continue;
var sprite = wr.Theater.TileSprite(tile, 0);

View File

@@ -0,0 +1,104 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class RenameStances : UpdateRule
{
public override string Name { get { return "Renamed player 'Stances' to 'Relationships'."; } }
public override string Description
{
get
{
return "'Stances' in regards to a player have been renamed to 'Relationships'.\n" +
"The yaml values did not change.";
}
}
readonly (string TraitName, string OldName, string NewName)[] traits =
{
("Disguise", "ValidStances", "ValidRelationships"),
("Infiltrates", "ValidStances", "ValidRelationships"),
("AcceptsDeliveredCash", "ValidStances", "ValidRelationships"),
("AcceptsDeliveredExperience", "ValidStances", "ValidRelationships"),
("Armament", "TargetStances", "TargetRelationships"),
("Armament", "ForceTargetStances", "ForceTargetRelationships"),
("AutoTargetPriority", "ValidStances", "ValidRelationships"),
("CaptureManagerBotModule", "CapturableStances", "CapturableRelationships"),
("Capturable", "ValidStances", "ValidRelationships"),
("Captures", "PlayerExperienceStances", "PlayerExperienceRelationships"),
("ProximityExternalCondition", "ValidStances", "ValidRelationships"),
("CreatesShroud", "ValidStances", "ValidRelationships"),
("Demolition", "TargetStances", "TargetRelationships"),
("Demolition", "ForceTargetStances", "ForceTargetRelationships"),
("EngineerRepair", "ValidStances", "ValidRelationships"),
("GivesBounty", "ValidStances", "ValidRelationships"),
("GivesExperience", "ValidStances", "ValidRelationships"),
("JamsMissiles", "DeflectionStances", "DeflectionRelationships"),
("FrozenUnderFog", "AlwaysVisibleStances", "AlwaysVisibleRelationships"),
("HiddenUnderShroud", "AlwaysVisibleStances", "AlwaysVisibleRelationships"),
("HiddenUnderFog", "AlwaysVisibleStances", "AlwaysVisibleRelationships"),
("AppearsOnRadar", "ValidStances", "ValidRelationships"),
("CashTricklerBar", "DisplayStances", "DisplayRelationships"),
("SupportPowerChargeBar", "DisplayStances", "DisplayRelationships"),
("WithAmmoPipsDecoration", "ValidStances", "ValidRelationships"),
("WithCargoPipsDecoration", "ValidStances", "ValidRelationships"),
("WithDecoration", "ValidStances", "ValidRelationships"),
("WithHarvesterPipsDecoration", "ValidStances", "ValidRelationships"),
("WithNameTagDecoration", "ValidStances", "ValidRelationships"),
("WithResourceStoragePipsDecoration", "ValidStances", "ValidRelationships"),
("WithTextDecoration", "ValidStances", "ValidRelationships"),
("WithRangeCircle", "ValidStances", "ValidRelationships"),
("RevealOnDeath", "RevealForStances", "RevealForRelationships"),
("RevealOnFire", "RevealForStancesRelativeToTarget", "RevealForRelationships"),
("RevealsMap", "ValidStances", "ValidRelationships"),
("RevealsShroud", "ValidStances", "ValidRelationships"),
("VoiceAnnouncement", "ValidStances", "ValidRelationships"),
("GrantExternalConditionPower", "ValidStances", "ValidRelationships"),
("NukePower", "CameraStances", "CameraRelationships"),
("NukePower", "DisplayTimerStances", "DisplayTimerRelationships"),
("AttackOrderPower", "DisplayTimerStances", "DisplayTimerRelationships"),
("ChronoshiftPower", "DisplayTimerStances", "DisplayTimerRelationships"),
("DropPodsPower", "DisplayTimerStances", "DisplayTimerRelationships"),
("GpsPower", "DisplayTimerStances", "DisplayTimerRelationships"),
("GrantPrerequisiteChargeDrainPower", "DisplayTimerStances", "DisplayTimerRelationships"),
("IonCannonPower", "DisplayTimerStances", "DisplayTimerRelationships"),
("AirstrikePower", "DisplayTimerStances", "DisplayTimerRelationships"),
("GrantExternalConditionPower", "DisplayTimerStances", "DisplayTimerRelationships"),
("ParatroopersPower", "DisplayTimerStances", "DisplayTimerRelationships"),
("ProduceActorPower", "DisplayTimerStances", "DisplayTimerRelationships"),
("SpawnActorPower", "DisplayTimerStances", "DisplayTimerRelationships"),
("TooltipDescription", "ValidStances", "ValidRelationships")
};
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
{
foreach (var field in traits)
foreach (var traitNode in actorNode.ChildrenMatching(field.TraitName))
traitNode.RenameChildrenMatching(field.OldName, field.NewName);
yield break;
}
public override IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNode weaponNode)
{
foreach (var projectileNode in weaponNode.ChildrenMatching("Projectile"))
projectileNode.RenameChildrenMatching("ValidBounceBlockerStances", "ValidBounceBlockerRelationships");
foreach (var warheadNode in weaponNode.ChildrenMatching("Warhead"))
warheadNode.RenameChildrenMatching("ValidStances", "ValidRelationships");
yield break;
}
}
}

View File

@@ -73,6 +73,7 @@ namespace OpenRA.Mods.Common.UpdateRules
new RenameSelfHealing(),
new ReplaceBurns(),
new RemoveMuzzleSplitFacings(),
new RenameStances(),
new RemoveTurnToDock(),
new RenameSmudgeSmokeFields(),
new RenameCircleContrast(),

View File

@@ -104,6 +104,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
" for (var j = 0; j < ctx.canvas.height / 4; j++)",
" for (var i = j % 2; i < ctx.canvas.width / 4; i += 2)",
" ctx.fillRect(4 * i, 4 * j, 4, 4);",
" ctx.imageSmoothingEnabled = false;",
" ctx.drawImage(image, 0, 0, c.width, c.height);",
" ctx.strokeStyle = \"#ffff00\";",
" ctx.lineWidth = 1;",

View File

@@ -80,7 +80,7 @@ namespace OpenRA.Mods.Common.Warheads
public override bool IsValidAgainst(Actor victim, Actor firedBy)
{
var stance = firedBy.Owner.RelationshipWith(victim.Owner);
if (!ValidStances.HasStance(stance))
if (!ValidRelationships.HasStance(stance))
return false;
// A target type is valid if it is in the valid targets list, and not in the invalid targets list.

View File

@@ -31,8 +31,8 @@ namespace OpenRA.Mods.Common.Warheads
[Desc("What types of targets are unaffected.", "Overrules ValidTargets.")]
public readonly BitSet<TargetableType> InvalidTargets;
[Desc("What diplomatic stances are affected.")]
public readonly PlayerRelationship ValidStances = PlayerRelationship.Ally | PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("What player relationships are affected.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally | PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Can this warhead affect the actor that fired it.")]
public readonly bool AffectsParent = false;
@@ -63,7 +63,7 @@ namespace OpenRA.Mods.Common.Warheads
return false;
var stance = firedBy.Owner.RelationshipWith(victim.Owner);
if (!ValidStances.HasStance(stance))
if (!ValidRelationships.HasStance(stance))
return false;
// A target type is valid if it is in the valid targets list, and not in the invalid targets list.
@@ -81,7 +81,7 @@ namespace OpenRA.Mods.Common.Warheads
// AffectsParent checks do not make sense for FrozenActors, so skip to stance checks
var stance = firedBy.Owner.RelationshipWith(victim.Owner);
if (!ValidStances.HasStance(stance))
if (!ValidRelationships.HasStance(stance))
return false;
// A target type is valid if it is in the valid targets list, and not in the invalid targets list.

View File

@@ -36,7 +36,7 @@ namespace OpenRA.Mods.Common.Widgets
powers = world.ActorsWithTrait<SupportPowerManager>()
.Where(p => !p.Actor.IsDead && !p.Actor.Owner.NonCombatant)
.SelectMany(s => s.Trait.Powers.Values)
.Where(p => p.Instances.Any() && p.Info.DisplayTimerStances != PlayerRelationship.None && !p.Disabled);
.Where(p => p.Instances.Any() && p.Info.DisplayTimerRelationships != PlayerRelationship.None && !p.Disabled);
// Timers in replays should be synced to the effective game time, not the playback time.
timestep = world.Timestep;
@@ -53,7 +53,7 @@ namespace OpenRA.Mods.Common.Widgets
{
var owner = p.Instances[0].Self.Owner;
var viewer = owner.World.RenderPlayer ?? owner.World.LocalPlayer;
return viewer == null || p.Info.DisplayTimerStances.HasStance(owner.RelationshipWith(viewer));
return viewer == null || p.Info.DisplayTimerRelationships.HasStance(owner.RelationshipWith(viewer));
});
texts = displayedPowers.Select(p =>

View File

@@ -81,10 +81,7 @@ namespace OpenRA.Mods.Common.Widgets
for (var x = 0; x < Template.Size.X; x++)
{
var tile = new TerrainTile(Template.Id, (byte)(i++));
var tileInfo = tileset.GetTileInfo(tile);
// Empty tile
if (tileInfo == null)
if (!tileset.TryGetTileInfo(tile, out var tileInfo))
continue;
var sprite = worldRenderer.Theater.TileSprite(tile, 0);

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<TargetFramework>netstandard2.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<LangVersion>7.3</LangVersion>
@@ -11,6 +11,7 @@
<PlatformTarget>AnyCPU</PlatformTarget>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
<IsPublishable Condition="'$(CopyD2kDll)' == 'False'">false</IsPublishable>
</PropertyGroup>
<ItemGroup>
<!-- Work around an issue where Rider does not detect files in the project root using the default glob -->
@@ -37,4 +38,4 @@
<Analyzer Remove="@(Analyzer)" />
</ItemGroup>
</Target>
</Project>
</Project>

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<TargetFramework>netstandard2.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<LangVersion>7.3</LangVersion>
@@ -11,6 +11,7 @@
<PlatformTarget>AnyCPU</PlatformTarget>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<PropertyGroup>
<TargetPlatform Condition="$([MSBuild]::IsOsPlatform('Windows'))">win-x64</TargetPlatform>

View File

@@ -59,7 +59,6 @@ namespace OpenRA.Platforms.Default
Name = "ThreadedGraphicsContext RenderThread",
IsBackground = true
};
renderThread.SetApartmentState(ApartmentState.STA);
lock (syncObject)
{
// Start and wait for the rendering thread to have initialized before returning.

View File

@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net472</TargetFramework>
<TargetFramework Condition="'$(Mono)' == ''">net5.0</TargetFramework>
<TargetFramework Condition="'$(Mono)' != ''">netstandard2.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<UseVSHostingProcess>false</UseVSHostingProcess>

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>7.3</LangVersion>
<DebugSymbols>true</DebugSymbols>
@@ -10,6 +10,7 @@
<PlatformTarget>AnyCPU</PlatformTarget>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
<IsPublishable>false</IsPublishable>
</PropertyGroup>
<ItemGroup>
<!-- Work around an issue where Rider does not detect files in the project root using the default glob -->
@@ -22,14 +23,12 @@
<ItemGroup>
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj" />
<ProjectReference Include="..\OpenRA.Mods.Common\OpenRA.Mods.Common.csproj">
<Private>False</Private>
<Private>True</Private>
</ProjectReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit.Console" Version="3.11.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0-beta.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
<AdditionalFiles Include="../stylecop.json" />
@@ -40,4 +39,4 @@
<Analyzer Remove="@(Analyzer)" />
</ItemGroup>
</Target>
</Project>
</Project>

View File

@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net472</TargetFramework>
<TargetFramework Condition="'$(Mono)' == ''">net5.0</TargetFramework>
<TargetFramework Condition="'$(Mono)' != ''">netstandard2.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<UseVSHostingProcess>false</UseVSHostingProcess>

View File

@@ -1,7 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>winexe</OutputType>
<TargetFramework>net472</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<ApplicationIcon>$(LauncherIcon)</ApplicationIcon>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<UseVSHostingProcess>false</UseVSHostingProcess>
@@ -10,14 +12,12 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
<OutputPath>../bin</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<PlatformTarget>AnyCPU</PlatformTarget>
<ExternalConsole>false</ExternalConsole>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
<Configurations>Release;Debug</Configurations>
<AssemblyName>$(LauncherName)</AssemblyName>
<ApplicationIcon>$(LauncherIcon)</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<!-- Work around an issue where Rider does not detect files in the project root using the default glob -->

View File

@@ -58,9 +58,7 @@ Global
{FE6C8CC0-2F07-442A-B29F-17617B3B7FC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FE6C8CC0-2F07-442A-B29F-17617B3B7FC6}.Release|Any CPU.Build.0 = Release|Any CPU
{6CB8E1B7-6B36-4D93-8633-7C573E194AC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6CB8E1B7-6B36-4D93-8633-7C573E194AC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CB8E1B7-6B36-4D93-8633-7C573E194AC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6CB8E1B7-6B36-4D93-8633-7C573E194AC4}.Release|Any CPU.Build.0 = Release|Any CPU
{54DAE0E0-3125-49D3-992E-A0E931EB5FC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{54DAE0E0-3125-49D3-992E-A0E931EB5FC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{54DAE0E0-3125-49D3-992E-A0E931EB5FC8}.Release|Any CPU.ActiveCfg = Release|Any CPU

View File

@@ -4,7 +4,7 @@ A Libre/Free Real Time Strategy game engine supporting early Westwood classics.
* Website: [http://www.openra.net](http://www.openra.net)
* IRC: \#openra on irc.freenode.net
* Repository: [https://github.com/OpenRA/OpenRA](https://github.com/OpenRA/OpenRA) [![Travis CI build status](https://travis-ci.org/OpenRA/OpenRA.svg?branch=bleed)](https://travis-ci.org/OpenRA/OpenRA) [![AppVeyor build status](https://ci.appveyor.com/api/projects/status/axc9k6jd25ej2o4w?svg=true)](https://ci.appveyor.com/project/OpenRA/openra)
* Repository: [https://github.com/OpenRA/OpenRA](https://github.com/OpenRA/OpenRA) ![Continuous Integration](https://github.com/OpenRA/OpenRA/workflows/Continuous%20Integration/badge.svg)
Please read the [FAQ](http://wiki.openra.net/FAQ) in our [Wiki](http://wiki.openra.net) and report problems at [http://bugs.openra.net](http://bugs.openra.net).

View File

@@ -1,28 +0,0 @@
version: 1.0.{build}
image: Visual Studio 2017
install:
build_script:
- make all
test_script:
- nunit3-console bin/OpenRA.Test.dll --result=myresults.xml;format=AppVeyor
after_test:
- appveyor DownloadFile "https://github.com/OpenRA/GeoIP-Database/releases/download/monthly/IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP" -FileName IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP
- pip install Pillow
- python -c "from PIL import Image; i = Image.open('packaging/artwork/ra_256x256.png'); i.save('ra.ico')"
- python -c "from PIL import Image; i = Image.open('packaging/artwork/cnc_256x256.png'); i.save('cnc.ico')"
- python -c "from PIL import Image; i = Image.open('packaging/artwork/d2k_256x256.png'); i.save('d2k.ico')"
- msbuild -t:Build "OpenRA.WindowsLauncher/OpenRA.WindowsLauncher.csproj" -restore -p:Configuration=Release -p:TargetPlatform="win-x64" -p:LauncherName="RedAlert" -p:LauncherIcon="../ra.ico" -p:ModID="ra" -p:DisplayName="Red Alert" -p:FaqUrl="http://wiki.openra.net/FAQ"
- msbuild -t:Build "OpenRA.WindowsLauncher/OpenRA.WindowsLauncher.csproj" -restore -p:Configuration=Release -p:TargetPlatform="win-x64" -p:LauncherName="TiberianDawn" -p:LauncherIcon="../cnc.ico" -p:ModID="cnc" -p:DisplayName="Tiberian Dawn" -p:FaqUrl="http://wiki.openra.net/FAQ"
- msbuild -t:Build "OpenRA.WindowsLauncher/OpenRA.WindowsLauncher.csproj" -restore -p:Configuration=Release -p:TargetPlatform="win-x64" -p:LauncherName="Dune2000" -p:LauncherIcon="../d2k.ico" -p:ModID="d2k" -p:DisplayName="Dune 2000" -p:FaqUrl="http://wiki.openra.net/FAQ"
- move /Y %APPVEYOR_BUILD_FOLDER%\bin\* %APPVEYOR_BUILD_FOLDER%
- if defined APPVEYOR_REPO_TAG_NAME set VERSION=%APPVEYOR_REPO_TAG_NAME%
- if not defined APPVEYOR_REPO_TAG_NAME set VERSION=%APPVEYOR_REPO_COMMIT:~0,7%
- '"C:\Program Files (x86)\NSIS\makensis.exe" /DSRCDIR="%APPVEYOR_BUILD_FOLDER%" /DTAG="git-%VERSION%" /DSUFFIX=" (dev)" /V3 /DOUTFILE="OpenRA-$(VERSION).exe" packaging/windows/OpenRA.nsi'
artifacts:
- path: OpenRA-$(VERSION).exe
name: Installer

View File

@@ -25,7 +25,7 @@ ShareAnonymizedIPs="${ShareAnonymizedIPs:-"True"}"
SupportDir="${SupportDir:-""}"
while true; do
mono --debug bin/OpenRA.Server.exe Engine.EngineDir=".." Game.Mod="$Mod" \
mono --debug bin/OpenRA.Server.dll Engine.EngineDir=".." Game.Mod="$Mod" \
Server.Name="$Name" \
Server.ListenPort="$ListenPort" \
Server.AdvertiseOnline="$AdvertiseOnline" \

View File

@@ -25,7 +25,7 @@ then
fi
# Launch the engine with the appropriate arguments
mono bin/OpenRA.exe Engine.EngineDir=".." Engine.LaunchPath="$MODLAUNCHER" $MODARG "$@"
mono --debug bin/OpenRA.dll Engine.EngineDir=".." Engine.LaunchPath="$MODLAUNCHER" $MODARG "$@"
# Show a crash dialog if something went wrong
if [ $? != 0 ] && [ $? != 1 ]; then

View File

@@ -10,7 +10,7 @@ function All-Command
return
}
dotnet build /p:Configuration=Release /nologo
dotnet build -c Release --nologo -p:TargetPlatform=win-x64
if ($lastexitcode -ne 0)
{
Write-Host "Build failed. If just the development tools failed to build, try installing Visual Studio. You may also still be able to run the game." -ForegroundColor Red
@@ -111,7 +111,7 @@ function Test-Command
function Check-Command
{
Write-Host "Compiling in debug configuration..." -ForegroundColor Cyan
dotnet build /p:Configuration=Debug /nologo
dotnet build -c Debug --nologo -p:TargetPlatform=win-x64
if ($lastexitcode -ne 0)
{
Write-Host "Build failed." -ForegroundColor Red

View File

@@ -366,23 +366,18 @@ scrollpanel-decorations:
Inherits: ^Chrome
Regions:
down: 836, 17, 16, 16
down-pressed: 836, 17, 16, 16
down-disabled: 853, 17, 16, 16
up: 870, 17, 16, 16
up-pressed: 870, 17, 16, 16
up-disabled: 887, 17, 16, 16
right: 904, 17, 16, 16
right-pressed: 904, 17, 16, 16
right-disabled: 921, 17, 16, 16
left: 938, 17, 16, 16
left-pressed: 938, 17, 16, 16
left-disabled: 955, 17, 16, 16
dropdown-decorations:
Inherits: ^Chrome
Regions:
marker: 836, 17, 16, 16
marker-pressed: 836, 17, 16, 16
marker-disabled: 853, 17, 16, 16
dropdown-separators:

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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