Compare commits

...

42 Commits

Author SHA1 Message Date
Paul Chote
759dc34e03 Use ULFO format for non-compat disk images (requires macOS 10.11+). 2020-12-23 23:34:53 +00:00
Paul Chote
668ab78b1b Trim unused assemblies to reduce packaged size further. 2020-12-23 23:00:17 +00:00
Paul Chote
c754d6a892 Replace duplicate runtime files with hardlinks to reduce dmg size. 2020-12-23 20:05:48 +00:00
Paul Chote
e9108479b5 Switch macOS packages to .NET 5. 2020-12-23 20:05:48 +00:00
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
Paul Chote
a8d3d5c79a Fix Neutral crushing checks. 2020-12-08 20:17:11 +01:00
Paul Chote
7c852d90fb Ignore aircraft when searching for enemy targets. 2020-12-07 23:39:22 +01:00
Paul Chote
269ce9c406 Exclude carryalls from AI squads. 2020-12-07 23:39:22 +01:00
Paul Chote
53d98ec255 Abort squad states that are not able to move. 2020-12-07 23:39:22 +01:00
Paul Chote
7a7cd21578 Fix TD SAM Site facings being reset when damaged while closed. 2020-12-07 01:44:04 +01:00
180 changed files with 5425 additions and 779 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

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

@@ -0,0 +1,99 @@
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: 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}
- 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

@@ -18,6 +18,7 @@ namespace OpenRA.Primitives
static class LongBitSetAllocator<T> where T : class
{
static readonly Cache<string, long> Bits = new Cache<string, long>(Allocate);
static long allBits = 1;
static long nextBits = 1;
static long Allocate(string value)
@@ -25,6 +26,7 @@ namespace OpenRA.Primitives
lock (Bits)
{
var bits = nextBits;
allBits |= bits;
nextBits <<= 1;
if (nextBits == 0)
@@ -85,6 +87,8 @@ namespace OpenRA.Primitives
nextBits = 1;
}
}
public static long Mask { get { return allBits; } }
}
// Opitmized BitSet to be used only when guaranteed to be no more than 64 values.
@@ -114,6 +118,7 @@ namespace OpenRA.Primitives
public static bool operator ==(LongBitSet<T> me, LongBitSet<T> other) { return me.bits == other.bits; }
public static bool operator !=(LongBitSet<T> me, LongBitSet<T> other) { return !(me == other); }
public static LongBitSet<T> operator ~(LongBitSet<T> me) { return new LongBitSet<T>(me.bits ^ LongBitSetAllocator<T>.Mask); }
public bool Equals(LongBitSet<T> other) { return other == this; }
public override bool Equals(object obj) { return obj is LongBitSet<T> && Equals((LongBitSet<T>)obj); }

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>
@@ -62,4 +65,8 @@
<Analyzer Remove="@(Analyzer)" />
</ItemGroup>
</Target>
<ItemGroup>
<TrimmerRootAssembly Include="netstandard" />
<TrimmerRootAssembly Include="System.IO.Pipes" />
</ItemGroup>
</Project>

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

@@ -65,7 +65,7 @@ namespace OpenRA.Mods.Cnc.Traits
return self.World.NoPlayersMask;
// Friendly units should move around!
return info.BlockFriendly ? self.Owner.EnemyPlayersMask : self.World.AllPlayersMask;
return info.BlockFriendly ? ~self.Owner.AlliedPlayersMask : self.World.AllPlayersMask;
}
}

View File

@@ -23,6 +23,9 @@ namespace OpenRA.Mods.Cnc.Traits.Render
[Desc("This actor has turret art with facings baked into the sprite.")]
public class WithEmbeddedTurretSpriteBodyInfo : WithSpriteBodyInfo, Requires<TurretedInfo>, Requires<BodyOrientationInfo>
{
[Desc("Number of facings for gameplay calculations. -1 indicates auto-detection from the sequence.")]
public readonly int QuantizedFacings = -1;
public override object Create(ActorInitializer init) { return new WithEmbeddedTurretSpriteBody(init, this); }
public override IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
@@ -43,6 +46,7 @@ namespace OpenRA.Mods.Cnc.Traits.Render
public class WithEmbeddedTurretSpriteBody : WithSpriteBody
{
readonly WithEmbeddedTurretSpriteBodyInfo info;
readonly Turreted turreted;
static Func<WAngle> MakeTurretFacingFunc(Actor self)
@@ -52,22 +56,23 @@ namespace OpenRA.Mods.Cnc.Traits.Render
return () => turreted.WorldOrientation.Yaw;
}
public WithEmbeddedTurretSpriteBody(ActorInitializer init, WithSpriteBodyInfo info)
public WithEmbeddedTurretSpriteBody(ActorInitializer init, WithEmbeddedTurretSpriteBodyInfo info)
: base(init, info, MakeTurretFacingFunc(init.Self))
{
this.info = info;
turreted = init.Self.TraitsImplementing<Turreted>().FirstOrDefault();
}
protected override void TraitEnabled(Actor self)
{
base.TraitEnabled(self);
turreted.QuantizedFacings = DefaultAnimation.CurrentSequence.Facings;
turreted.QuantizedFacings = info.QuantizedFacings >= 0 ? info.QuantizedFacings : DefaultAnimation.CurrentSequence.Facings;
}
protected override void DamageStateChanged(Actor self)
{
base.DamageStateChanged(self);
turreted.QuantizedFacings = DefaultAnimation.CurrentSequence.Facings;
turreted.QuantizedFacings = info.QuantizedFacings >= 0 ? info.QuantizedFacings : DefaultAnimation.CurrentSequence.Facings;
}
}
}

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

@@ -132,7 +132,7 @@ namespace OpenRA.Mods.Common.Traits
// Use for proactive targeting.
public bool IsPreferredEnemyUnit(Actor a)
{
if (a == null || a.IsDead || Player.RelationshipWith(a.Owner) != PlayerRelationship.Enemy || a.Info.HasTraitInfo<HuskInfo>())
if (a == null || a.IsDead || Player.RelationshipWith(a.Owner) != PlayerRelationship.Enemy || a.Info.HasTraitInfo<HuskInfo>() || a.Info.HasTraitInfo<AircraftInfo>())
return false;
var targetTypes = a.GetEnabledTargetTypes();

View File

@@ -68,6 +68,10 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
class GroundUnitsAttackMoveState : GroundStateBase, IState
{
int lastUpdatedTick;
CPos? lastLeaderLocation;
Actor lastTarget;
public void Activate(Squad owner) { }
public void Tick(Squad owner)
@@ -91,6 +95,27 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
if (leader == null)
return;
if (leader.Location != lastLeaderLocation)
{
lastLeaderLocation = leader.Location;
lastUpdatedTick = owner.World.WorldTick;
}
if (owner.TargetActor != lastTarget)
{
lastTarget = owner.TargetActor;
lastUpdatedTick = owner.World.WorldTick;
}
// HACK: Drop back to the idle state if we haven't moved in 2.5 seconds
// This works around the squad being stuck trying to attack-move to a location
// that they cannot path to, generating expensive pathfinding calls each tick.
if (owner.World.WorldTick > lastUpdatedTick + 63)
{
owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsIdleState(), true);
return;
}
var ownUnits = owner.World.FindActorsInCircle(leader.CenterPosition, WDist.FromCells(owner.Units.Count) / 3)
.Where(a => a.Owner == owner.Units.First().Owner && owner.Units.Contains(a)).ToHashSet();
@@ -126,6 +151,10 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
class GroundUnitsAttackState : GroundStateBase, IState
{
int lastUpdatedTick;
CPos? lastLeaderLocation;
Actor lastTarget;
public void Activate(Squad owner) { }
public void Tick(Squad owner)
@@ -145,6 +174,28 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
}
}
var leader = owner.Units.ClosestTo(owner.TargetActor.CenterPosition);
if (leader.Location != lastLeaderLocation)
{
lastLeaderLocation = leader.Location;
lastUpdatedTick = owner.World.WorldTick;
}
if (owner.TargetActor != lastTarget)
{
lastTarget = owner.TargetActor;
lastUpdatedTick = owner.World.WorldTick;
}
// HACK: Drop back to the idle state if we haven't moved in 2.5 seconds
// This works around the squad being stuck trying to attack-move to a location
// that they cannot path to, generating expensive pathfinding calls each tick.
if (owner.World.WorldTick > lastUpdatedTick + 63)
{
owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsIdleState(), true);
return;
}
foreach (var a in owner.Units)
if (!BusyAttack(a))
owner.Bot.QueueOrder(new Order("Attack", a, Target.FromActor(owner.TargetActor), false));

View File

@@ -93,6 +93,10 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
class NavyUnitsAttackMoveState : NavyStateBase, IState
{
int lastUpdatedTick;
CPos? lastLeaderLocation;
Actor lastTarget;
public void Activate(Squad owner) { }
public void Tick(Squad owner)
@@ -116,6 +120,27 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
if (leader == null)
return;
if (leader.Location != lastLeaderLocation)
{
lastLeaderLocation = leader.Location;
lastUpdatedTick = owner.World.WorldTick;
}
if (owner.TargetActor != lastTarget)
{
lastTarget = owner.TargetActor;
lastUpdatedTick = owner.World.WorldTick;
}
// HACK: Drop back to the idle state if we haven't moved in 2.5 seconds
// This works around the squad being stuck trying to attack-move to a location
// that they cannot path to, generating expensive pathfinding calls each tick.
if (owner.World.WorldTick > lastUpdatedTick + 63)
{
owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsIdleState(), true);
return;
}
var ownUnits = owner.World.FindActorsInCircle(leader.CenterPosition, WDist.FromCells(owner.Units.Count) / 3)
.Where(a => a.Owner == owner.Units.First().Owner && owner.Units.Contains(a)).ToHashSet();
@@ -151,6 +176,10 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
class NavyUnitsAttackState : NavyStateBase, IState
{
int lastUpdatedTick;
CPos? lastLeaderLocation;
Actor lastTarget;
public void Activate(Squad owner) { }
public void Tick(Squad owner)
@@ -170,6 +199,28 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
}
}
var leader = owner.Units.ClosestTo(owner.TargetActor.CenterPosition);
if (leader.Location != lastLeaderLocation)
{
lastLeaderLocation = leader.Location;
lastUpdatedTick = owner.World.WorldTick;
}
if (owner.TargetActor != lastTarget)
{
lastTarget = owner.TargetActor;
lastUpdatedTick = owner.World.WorldTick;
}
// HACK: Drop back to the idle state if we haven't moved in 2.5 seconds
// This works around the squad being stuck trying to attack-move to a location
// that they cannot path to, generating expensive pathfinding calls each tick.
if (owner.World.WorldTick > lastUpdatedTick + 63)
{
owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsIdleState(), true);
return;
}
foreach (var a in owner.Units)
if (!BusyAttack(a))
owner.Bot.QueueOrder(new Order("Attack", a, Target.FromActor(owner.TargetActor), false));

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

@@ -70,7 +70,7 @@ namespace OpenRA.Mods.Common.Traits
if (IsTraitDisabled || !self.IsAtGroundLevel() || !Info.CrushClasses.Overlaps(crushClasses))
return self.World.NoPlayersMask;
return Info.CrushedByFriendlies ? self.World.AllPlayersMask : self.Owner.EnemyPlayersMask;
return Info.CrushedByFriendlies ? self.World.AllPlayersMask : ~self.Owner.AlliedPlayersMask;
}
bool CrushableInner(BitSet<CrushClass> crushClasses, Player crushOwner)

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>
@@ -39,4 +40,8 @@
<Analyzer Remove="@(Analyzer)" />
</ItemGroup>
</Target>
<ItemGroup>
<TrimmerRootAssembly Include="netstandard" />
<TrimmerRootAssembly Include="System.IO.Pipes" />
</ItemGroup>
</Project>

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>
@@ -39,4 +40,8 @@
<Analyzer Remove="@(Analyzer)" />
</ItemGroup>
</Target>
<ItemGroup>
<TrimmerRootAssembly Include="netstandard" />
<TrimmerRootAssembly Include="System.IO.Pipes" />
</ItemGroup>
</Project>

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 -->
@@ -58,4 +58,8 @@
<Analyzer Remove="@(Analyzer)" />
</ItemGroup>
</Target>
<ItemGroup>
<TrimmerRootAssembly Include="netstandard" />
<TrimmerRootAssembly Include="System.IO.Pipes" />
</ItemGroup>
</Project>

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" \

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