Compare commits

...

103 Commits

Author SHA1 Message Date
reaperrr
b733b10f27 Fix D2k DamagesConcrete overrides 2021-03-21 11:10:05 +00:00
reaperrr
ab3f3000fd Add support for pool-specific ammo pips decoration 2021-03-20 15:33:10 +01:00
Unrud
b278a63e11 Makefile: Fail target version when not a git repository 2021-03-13 15:58:11 +00:00
Paul Chote
7ba140e471 Fail Situation Critical if Volkov is killed before entering the world. 2021-03-13 15:55:39 +00:00
Paul Chote
df476dd96f Fix non-relative path handling in install logic. 2021-03-13 15:52:52 +00:00
Paul Chote
f813f2bd46 Fix lobby checkbox event rectangle overlapping with scrollbars. 2021-03-05 18:56:46 +01:00
Paul Chote
c9c24490e6 Fix rendering artifacts with RA's dialog5 background. 2021-03-05 18:36:08 +01:00
penev92
11d84041d9 Added the "missing" song to the D2k TestFiles
Since it's music it's optional, so the players still won't know anything happened, but if they do open the content manager they will have a chance for an automatic installation.
2021-02-28 12:29:27 +00:00
pizzaoverhead
7ec9bdf0d0 Fix TLS error on Win7. 2021-02-28 10:15:52 +00:00
penev92
ef59b07a2d Fixed D2k VQA videos crashing the game 2021-02-19 13:38:23 +01:00
Unrud
74e3ded764 AppStream Metadata: Add violence-worship: moderate 2021-02-14 14:17:22 +00:00
Unrud
a46fab4ece Update AppStream metadata for linux packaging
A few small improvements:
* The type `desktop` was renamed to `desktop-appliaction` (a long time ago)
* Add `launchable` to tell how the application can be launched
* Use `https` for homepage link
* Add link to the bugtracker
* Update `oars-1.0` to `oars-1.1`
* Remove all unnecessary `content_attribute` entries with value `none`
2021-02-14 14:17:16 +00:00
Smittytron
81a7c3446d Add clarifying objective to Monster Tank Madness 2021-02-12 02:35:42 +01:00
Orb
a3404fac66 APC Balance Tweak Commit 2021-02-09 23:08:55 +01:00
Paul Chote
bb2276498b Fix Tanya's prone firing animations. 2021-02-02 23:06:27 +01:00
Unrud
7367b546e8 Use version from VERSION file
The `VERSION` variable doesn't work with a release tarball, because it requires git to be set correctly.
Instead this reads the version from the `VERSION` file.
2021-02-01 23:14:12 +01:00
abcdefg30
becc180956 Increase the reload delay of the prison colt to match the normal colt 2021-01-31 22:36:16 +00:00
abcdefg30
656ce35330 Fix the gun sounds not playing in Allies05a 2021-01-31 22:36:13 +00:00
Smittytron
2575b41492 Fix crash in Allies 05a on SamBarrel kill 2021-01-31 18:53:02 +01:00
abcdefg30
acaf535b67 Remove the 'docs' target from the windows make file 2021-01-30 21:08:59 +00:00
Paul Chote
503a434a4b Fix Concrete placement on invalid terrain. 2021-01-30 14:48:35 +01:00
Smittytron
e27c093536 Remove SpawnActorOnDeath from civilian structures 2021-01-30 13:50:20 +01:00
Paul Chote
fed7daa03d Ignore invalid server entries instead of the entire list. 2021-01-30 13:46:22 +01:00
Paul Chote
3906c6efab Don't crash if a null uid is given to QueryRemoteMapDetails. 2021-01-30 13:46:20 +01:00
Paul Chote
ae018235d0 Prevent AttackPopupTurreted.TickIdle from running while disabled. 2021-01-30 13:16:45 +01:00
Paul Chote
a8f6256c04 Restore missing versioned source code package. 2021-01-30 13:05:54 +01:00
reaperrr
de6a9dc8da Fix RA inf death anims playing too fast 2021-01-30 01:24:03 +00:00
Paul Chote
f14e3669e3 Fix Tanya firing animation. 2021-01-29 15:03:37 +01:00
Paul Chote
21eb585a28 Support burst-specific infantry attack animations. 2021-01-29 15:03:24 +01:00
Paul Chote
f8dddf0502 Reduce Tanya firing rate. 2021-01-29 15:03:13 +01:00
Paul Chote
d3482c0f18 Suppress error messages from make.ps1 clean.
Also removed obsolete ./*/bin removal.
2021-01-29 14:54:15 +01:00
Paul Chote
d1c9b8d30f Keep Discord join button active for spectators. 2021-01-29 14:39:53 +01:00
Paul Chote
d2a6b7370e Remove first-client check from LobbySettingsNotification.
ClientWithIndex may rarely be null, causing a crash.
In any case we do want to report these changes to the first client, as
somebody else may have changed the settings and left.
2021-01-29 14:33:56 +01:00
Paul Chote
20b55ab5db Fix bot-controlled aircraft stalling above cloaked targets. 2021-01-29 14:30:00 +01:00
Paul Chote
c7ffc4d24f Increase map chooser dialog size to match the lobby. 2021-01-25 20:44:05 +01:00
Smittytron
2ce83bb51f Change hospital owner in Nod07b 2021-01-24 10:52:52 +00:00
teinarss
ea9f967acd Add Map and server name to discord details 2021-01-22 14:14:27 +01:00
Paul Chote
4a6927b513 Fix AutoTarget.ChooseTarget ignoring AttackBase.TargetFrozenActors. 2021-01-21 19:25:25 +01:00
Paul Chote
cab611b8dd Fix make check ignoring TARGETPLATFORM override. 2021-01-19 09:34:45 +01:00
Paul Chote
47bfa8bce8 Fix Sardarkaur not attacking while attack-moving. 2021-01-17 19:45:06 +01:00
Matthias Mailänder
40c452b07e Update path according to latest appstream specifications. 2021-01-16 20:04:15 +01:00
Matthias Mailänder
f1b238d7bf Document the target platform parameter during install
as it otherwise copies the system binaries.
2021-01-16 20:04:13 +01:00
Matthias Mailänder
0028a26193 Get rid of unnecessary Makefile variables. 2021-01-16 20:04:12 +01:00
Matthias Mailänder
bcdbd02f1c Don't contain the build root in the wrappers. 2021-01-16 20:04:10 +01:00
Matthias Mailänder
805fc5cade Create BIN_PATH first on shortcut installation. 2021-01-16 20:04:09 +01:00
VonNah
90ddbbc934 Fix vehicle explosion and missile impact sounds in Tiberian Dawn 2021-01-16 01:32:34 +00:00
VonNah
5f9126f274 Fixed an issue where RadarUp and DisablePower/EnablePower use the wrong sound effects.
Signed-off-by: VonNah <vonnahora@gmail.com>
2021-01-16 00:48:58 +00:00
Paul Chote
a1011efd98 Update apt metadata before installing dependencies. 2021-01-12 23:11:20 +01:00
Paul Chote
135ee781d4 Fix infantry freezing death animations in Top o' the World mission. 2021-01-11 11:32:50 +01:00
Paul Chote
56ef43145b Fix Grenadier death animation in Sarin Gas 3 mission. 2021-01-11 11:32:49 +01:00
Paul Chote
ff1bfe162b Add death types support to the Lua Kill() API. 2021-01-11 11:32:47 +01:00
Paul Chote
c642162d55 Cancel support power targeting if power cannot be activated. 2021-01-10 22:43:41 +01:00
Paul Chote
4bec1d1950 Add a per-player handicap option to the lobby.
Handicaps reduce unit health, firepower, and build speed.
2021-01-10 22:23:08 +01:00
Paul Chote
e769f51169 Increase lobby and server list width. 2021-01-10 22:22:56 +01:00
Paul Chote
ee19990749 Switch make.ps1 to downloading from our GeoIP data mirror. 2021-01-10 21:13:18 +01:00
Matthias Mailänder
0eefb0bc8c Fix syntax errors. 2021-01-10 21:10:21 +01:00
Matthias Mailänder
dd10b52955 Use the GitHub action variable for clickable links. 2021-01-10 21:10:19 +01:00
Matthias Mailänder
9a8b5da727 Update SDL 2 nuget package to match the platform. 2021-01-10 21:04:21 +01:00
Smittytron
53fe004c01 Add Aftermath mission Shock Therapy 2021-01-10 02:35:30 +01:00
Paul Chote
eea0ca1a4e Avoid BuildUnit crash if the item is invalidated before the task runs. 2021-01-10 01:34:26 +01:00
Paul Chote
ccbc78ea98 Add a setting to pause the shellmap. 2021-01-10 00:24:39 +01:00
Paul Chote
befcbe3677 Group bot AttackMove orders. 2021-01-09 23:56:18 +01:00
Orb
1f48359899 Playtest-Balance-Tweaks Commit 2021-01-09 23:23:21 +01:00
Matthias Mailänder
c4aed8af3f Fix error NU1101: Unable to find package. 2021-01-03 14:09:57 +01:00
Paul Chote
2d30913f06 Disable AttackPopupTurreted state changes when paused. 2021-01-03 12:47:03 +01:00
nvrnight
e3c96cf283 Issue #18936: The game no longer crashes when TextFieldWidget sends Enter key press and onSelect action is null. 2021-01-03 12:28:24 +01:00
Smittytron
325f414d45 Fix palettes in Fall of Greece 1 2021-01-02 22:46:19 +01:00
Smittytron
4371fb058b Add campaign-palettes to missions lacking it 2021-01-02 22:46:11 +01:00
Paul Chote
1d3461f5da Fix exit priority ordering. 2021-01-02 15:42:38 +01:00
Paul Chote
271f2ce539 Start rally point lines at the spawn position instead of the exit cell. 2021-01-02 15:29:37 +01:00
Paul Chote
8c29a44795 Prevent Health: 100 from being added to actors. 2021-01-02 15:11:56 +01:00
Paul Chote
226b96f9b0 Add a friendly type name for Nullable<T>. 2021-01-02 11:39:34 +01:00
abcdefg30
fa75891f62 Explicitly return on failure in make.ps1 2020-12-30 17:33:29 +01:00
Ivaylo Draganov
9c4dd4a08a Update launch.json to work with the new launcher 2020-12-30 17:32:23 +01:00
Nah
a75de2dd8b Update defaults.yaml
Fix the Building's SoundOnDamageTransition DamagedSounds with the correct one and add a second sound file to DestroyedSounds for more variation like in the original Tiberian Dawn.
2020-12-30 14:07:16 +00:00
abcdefg30
5249841ab7 Move myes1 and mrise1 from Select to Move Mechanic Voices 2020-12-30 02:01:21 +00:00
abcdefg30
01204eb414 Enable mechanic voices from Aftermath 2020-12-30 02:01:17 +00:00
Smittytron
58e86d1499 Remove out of bounds actors from Exodus 2020-12-30 01:51:55 +00:00
Smittytron
3b3c6a6647 Fix lag issue on Exodus by narrowing exit area 2020-12-30 01:51:48 +00:00
abcdefg30
31c6e3826e Correct Shock Trooper Move and Select voices 2020-12-29 21:15:42 +01:00
Orb
14f5c9d228 Fix-MT-Sound-Commit 2020-12-28 10:57:25 +01:00
Paul Chote
97d165ed94 Fix an incorrect comment in install_assemblies_mono 2020-12-24 12:11:20 +01:00
abcdefg30
d6c9bedd5a Add the 9th Dark Tournament map as "Oil Spill" 2020-12-21 21:14:17 +01:00
Paul Chote
00bc08e9a8 Fix api output directory. 2020-12-14 18:36:23 +01:00
Paul Chote
924cf4a885 Fix docs.openra.net repository reference. 2020-12-14 18:36:15 +01:00
Paul Chote
1f7eef8ffc Fix docs.openra.net documentation workflow. 2020-12-13 16:24:41 +00: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
211 changed files with 6996 additions and 1095 deletions

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

@@ -0,0 +1,49 @@
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
mono ~/.nuget/packages/nunit.consolerunner/3.11.1/tools/nunit3-console.exe --noresult bin/OpenRA.Test.dll
- name: Check Mods
run: |
sudo apt-get install lua5.1
make check-scripts
make test
windows:
name: Windows (Framework 4.7)
runs-on: windows-2019
steps:
- name: Clone Repository
uses: actions/checkout@v2
- name: Check Code
shell: powershell
run: |
dotnet nuget locals all --clear
.\make.ps1 check
Invoke-Expression "$home\.nuget\packages\nunit.consolerunner\3.11.1\tools\nunit3-console.exe --noresult bin/OpenRA.Test.dll"
- 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

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

@@ -0,0 +1,86 @@
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
run: |
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-${{ github.event.inputs.tag }}-x64.exe"
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-${{ github.event.inputs.tag }}-x64-winportable.zip" -O "OpenRA-${{ github.event.inputs.tag }}-x64-win-itch.zip"
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-${{ github.event.inputs.tag }}.dmg"
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-Dune-2000-x86_64.AppImage"
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-Red-Alert-x86_64.AppImage"
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-Tiberian-Dawn-x86_64.AppImage"
wget -q "https://raw.githubusercontent.com/${{ github.repository }}/${{ github.event.inputs.tag }}/packaging/.itch.toml"
zip -u "OpenRA-${{ github.event.inputs.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

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

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

20
.vscode/launch.json vendored
View File

@@ -11,9 +11,8 @@
"type": "mono"
},
"request": "launch",
"program": "${workspaceRoot}/OpenRA.Game.exe",
"cwd": "${workspaceRoot}",
"args": ["Game.Mod=cnc"],
"program": "${workspaceRoot}/bin/OpenRA.exe",
"args": ["Game.Mod=cnc", "Engine.EngineDir=.."],
"preLaunchTask": "build",
},
{
@@ -26,9 +25,8 @@
"type": "mono"
},
"request": "launch",
"program": "${workspaceRoot}/OpenRA.Game.exe",
"cwd": "${workspaceRoot}",
"args": ["Game.Mod=ra"],
"program": "${workspaceRoot}/bin/OpenRA.exe",
"args": ["Game.Mod=ra", "Engine.EngineDir=.."],
"preLaunchTask": "build",
},
{
@@ -41,9 +39,8 @@
"type": "mono"
},
"request": "launch",
"program": "${workspaceRoot}/OpenRA.Game.exe",
"cwd": "${workspaceRoot}",
"args": ["Game.Mod=d2k"],
"program": "${workspaceRoot}/bin/OpenRA.exe",
"args": ["Game.Mod=d2k", "Engine.EngineDir=.."],
"preLaunchTask": "build",
},
{
@@ -56,9 +53,8 @@
"type": "mono"
},
"request": "launch",
"program": "${workspaceRoot}/OpenRA.Game.exe",
"cwd": "${workspaceRoot}",
"args": ["Game.Mod=ts"],
"program": "${workspaceRoot}/bin/OpenRA.exe",
"args": ["Game.Mod=ts", "Engine.EngineDir=.."],
"preLaunchTask": "build",
},
]

View File

@@ -15,6 +15,10 @@
# to compile and install Red Alert, Tiberian Dawn, and Dune 2000, run:
# make [prefix=/foo] [bindir=/bar/bin] install
#
# to compile and install Red Alert, Tiberian Dawn, and Dune 2000
# using system libraries for native dependencies, run:
# make [prefix=/foo] [bindir=/bar/bin] TARGETPLATFORM=unix-generic install
#
# to install Linux startup scripts, desktop files, icons, and MIME metadata
# make install-linux-shortcuts
#
@@ -48,10 +52,6 @@ bindir ?= $(prefix)/bin
libdir ?= $(prefix)/lib
gameinstalldir ?= $(libdir)/openra
BIN_INSTALL_DIR = $(DESTDIR)$(bindir)
DATA_INSTALL_DIR = $(DESTDIR)$(datadir)
OPENRA_INSTALL_DIR = $(DESTDIR)$(gameinstalldir)
# Toolchain
CWD = $(shell pwd)
MSBUILD = msbuild -verbosity:m -nologo
@@ -61,7 +61,8 @@ RM_R = $(RM) -r
RM_F = $(RM) -f
RM_RF = $(RM) -rf
VERSION = $(shell git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || echo git-`git rev-parse --short HEAD`)
# Only for use in target version:
VERSION := $(shell git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || (c=$$(git rev-parse --short HEAD 2>/dev/null) && echo git-$$c))
# Detect target platform for dependencies if not given by the user
ifndef TARGETPLATFORM
@@ -98,7 +99,10 @@ clean:
check:
@echo
@echo "Compiling in debug mode..."
@$(MSBUILD) -t:build -restore -p:Configuration=Debug
@$(MSBUILD) -t:build -restore -p:Configuration=Debug -p:TargetPlatform=$(TARGETPLATFORM)
ifeq ($(TARGETPLATFORM), unix-generic)
@./configure-system-libraries.sh
endif
@echo
@echo "Checking runtime assemblies..."
@$(OPENRA_UTILITY) all --check-runtime-assemblies $(WHITELISTED_OPENRA_ASSEMBLIES) $(WHITELISTED_THIRDPARTY_ASSEMBLIES) $(WHITELISTED_CORE_ASSEMBLIES)
@@ -133,18 +137,21 @@ test: all
############# LOCAL INSTALLATION AND DOWNSTREAM PACKAGING ##############
#
version: VERSION mods/ra/mod.yaml mods/cnc/mod.yaml mods/d2k/mod.yaml mods/ts/mod.yaml mods/modcontent/mod.yaml mods/all/mod.yaml
@sh -c '. ./packaging/functions.sh; set_engine_version $(VERSION) .'
@sh -c '. ./packaging/functions.sh; set_mod_version $(VERSION) mods/ra/mod.yaml mods/cnc/mod.yaml mods/d2k/mod.yaml mods/ts/mod.yaml mods/modcontent/mod.yaml mods/all/mod.yaml'
ifeq ($(VERSION),)
$(error Unable to determine new version (requires git or override of variable VERSION))
endif
@sh -c '. ./packaging/functions.sh; set_engine_version "$(VERSION)" .'
@sh -c '. ./packaging/functions.sh; set_mod_version "$(VERSION)" mods/ra/mod.yaml mods/cnc/mod.yaml mods/d2k/mod.yaml mods/ts/mod.yaml mods/modcontent/mod.yaml mods/all/mod.yaml'
install:
@sh -c '. ./packaging/functions.sh; install_assemblies_mono $(CWD) $(OPENRA_INSTALL_DIR) $(TARGETPLATFORM) True True True'
@sh -c '. ./packaging/functions.sh; install_data $(CWD) $(OPENRA_INSTALL_DIR) cnc d2k ra'
@sh -c '. ./packaging/functions.sh; install_assemblies_mono $(CWD) $(DESTDIR)$(gameinstalldir) $(TARGETPLATFORM) True True True'
@sh -c '. ./packaging/functions.sh; install_data $(CWD) $(DESTDIR)$(gameinstalldir) cnc d2k ra'
install-linux-shortcuts:
@sh -c '. ./packaging/functions.sh; install_linux_shortcuts $(CWD) $(OPENRA_INSTALL_DIR) $(BIN_INSTALL_DIR) $(DATA_INSTALL_DIR) $(VERSION) cnc d2k ra'
@sh -c '. ./packaging/functions.sh; install_linux_shortcuts $(CWD) "$(DESTDIR)" "$(gameinstalldir)" "$(bindir)" "$(datadir)" "$(shell head -n1 VERSION)" cnc d2k ra'
install-linux-appdata:
@sh -c '. ./packaging/functions.sh; install_linux_appdata $(CWD) $(DATA_INSTALL_DIR) cnc d2k ra'
@sh -c '. ./packaging/functions.sh; install_linux_appdata $(CWD) "$(DESTDIR)" "$(datadir)" cnc d2k ra'
help:
@echo 'to compile, run:'
@@ -160,7 +167,11 @@ help:
@echo ' make test'
@echo
@echo 'to compile and install Red Alert, Tiberian Dawn, and Dune 2000 run:'
@echo ' make [prefix=/foo] install'
@echo ' make [prefix=/foo] [TARGETPLATFORM=unix-generic] install'
@echo
@echo 'to compile and install Red Alert, Tiberian Dawn, and Dune 2000'
@echo 'using system libraries for native dependencies, run:'
@echo ' make [prefix=/foo] [bindir=/bar/bin] TARGETPLATFORM=unix-generic install'
@echo
@echo 'to install Linux startup scripts, desktop files, icons, and MIME metadata'
@echo ' make install-linux-shortcuts'

View File

@@ -934,9 +934,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,8 +119,11 @@ 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,
Handicap = client.Handicap,
SpawnPoint = runtimePlayer.SpawnPoint,
IsRandomFaction = runtimePlayer.Faction.InternalName != client.Faction,
IsRandomSpawnPoint = runtimePlayer.DisplaySpawnPoint == 0,
@@ -157,9 +160,14 @@ 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;
public int Handicap;
/// <summary>True if the faction was chosen at random; otherwise, false.</summary>
public bool IsRandomFaction;

View File

@@ -166,6 +166,7 @@ namespace OpenRA
public void QueryRemoteMapDetails(string repositoryUrl, IEnumerable<string> uids, Action<MapPreview> mapDetailsReceived = null, Action queryFailed = null)
{
var maps = uids.Distinct()
.Where(uid => uid != null)
.Select(uid => previews[uid])
.Where(p => p.Status == MapStatus.Unavailable)
.ToDictionary(p => p.Uid, p => p);

View File

@@ -50,6 +50,9 @@ namespace OpenRA
public bool LockTeam = false;
public int Team = 0;
public bool LockHandicap = false;
public int Handicap = 0;
public string[] Allies = { };
public string[] Enemies = { };

View File

@@ -25,6 +25,7 @@ namespace OpenRA.Network
public readonly string Faction;
public readonly int SpawnPoint;
public readonly int Team;
public readonly int Handicap;
public readonly string Slot;
public readonly string Bot;
public readonly bool IsAdmin;
@@ -39,6 +40,7 @@ namespace OpenRA.Network
Faction = client.Faction;
SpawnPoint = client.SpawnPoint;
Team = client.Team;
Handicap = client.Handicap;
Slot = client.Slot;
Bot = client.Bot;
IsAdmin = client.IsAdmin;
@@ -53,6 +55,7 @@ namespace OpenRA.Network
client.Faction = Faction;
client.SpawnPoint = SpawnPoint;
client.Team = Team;
client.Handicap = Handicap;
client.Slot = Slot;
client.Bot = Bot;
client.IsAdmin = IsAdmin;

View File

@@ -144,6 +144,7 @@ namespace OpenRA.Network
public ClientState State = ClientState.Invalid;
public int Team;
public int Handicap;
public string Slot; // Slot ID, or null for observer
public string Bot; // Bot type, null for real clients
public int BotControllerClientIndex; // who added the bot to the slot
@@ -193,6 +194,7 @@ namespace OpenRA.Network
public bool LockFaction;
public bool LockColor;
public bool LockTeam;
public bool LockHandicap;
public bool LockSpawn;
public bool Required;

View File

@@ -58,13 +58,14 @@ namespace OpenRA
public readonly bool Playable = true;
public readonly int ClientIndex;
public readonly CPos HomeLocation;
public readonly int Handicap;
public readonly PlayerReference PlayerReference;
public readonly bool IsBot;
public readonly string BotType;
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 +81,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);
}
}
@@ -179,6 +181,8 @@ namespace OpenRA
HomeLocation = assignSpawnPoints?.AssignHomeLocation(world, client, playerRandom) ?? pr.HomeLocation;
SpawnPoint = assignSpawnPoints?.SpawnPointForPlayer(this) ?? client.SpawnPoint;
DisplaySpawnPoint = client.SpawnPoint;
Handicap = client.Handicap;
}
else
{
@@ -194,6 +198,7 @@ namespace OpenRA
DisplayFaction = ResolveDisplayFaction(world, pr.Faction);
HomeLocation = pr.HomeLocation;
SpawnPoint = DisplaySpawnPoint = 0;
Handicap = pr.Handicap;
}
if (!spectating)

View File

@@ -93,6 +93,8 @@ namespace OpenRA.Server
c.SpawnPoint = pr.Spawn;
if (pr.LockTeam)
c.Team = pr.Team;
if (pr.LockHandicap)
c.Team = pr.Handicap;
c.Color = pr.LockColor ? pr.Color : c.PreferredColor;
}
@@ -437,6 +439,7 @@ namespace OpenRA.Server
Faction = "Random",
SpawnPoint = 0,
Team = 0,
Handicap = 0,
State = Session.ClientState.Invalid,
};

View File

@@ -256,6 +256,8 @@ namespace OpenRA
public int IntroductionPromptVersion = 0;
public MPGameFilters MPGameFilters = MPGameFilters.Waiting | MPGameFilters.Empty | MPGameFilters.Protected | MPGameFilters.Started;
public bool PauseShellmap = false;
}
public class Settings

View File

@@ -32,6 +32,7 @@ namespace OpenRA
readonly List<IEffect> effects = new List<IEffect>();
readonly List<IEffect> unpartitionedEffects = new List<IEffect>();
readonly List<ISync> syncedEffects = new List<ISync>();
readonly GameSettings gameSettings;
readonly Queue<Action<World>> frameEndActions = new Queue<Action<World>>();
@@ -226,6 +227,7 @@ namespace OpenRA
};
RulesContainTemporaryBlocker = map.Rules.Actors.Any(a => a.Value.HasTraitInfo<ITemporaryBlockerInfo>());
gameSettings = Game.Settings.Game;
}
public void AddToMaps(Actor self, IOccupySpace ios)
@@ -424,7 +426,9 @@ namespace OpenRA
wasLoadingGameSave = false;
}
if (!Paused)
// Allow users to pause the shellmap via the settings menu
// Some traits initialize important state during the first tick, so we must allow it to tick at least once
if (!Paused && (Type != WorldType.Shellmap || !gameSettings.PauseShellmap || WorldTick == 0))
{
WorldTick++;

View File

@@ -82,6 +82,9 @@ namespace OpenRA.Mods.Cnc.Traits
protected override bool CanAttack(Actor self, in Target target)
{
if (IsTraitPaused)
return false;
if (state == PopupState.Closed)
{
state = PopupState.Transitioning;
@@ -103,6 +106,9 @@ namespace OpenRA.Mods.Cnc.Traits
void INotifyIdle.TickIdle(Actor self)
{
if (IsTraitDisabled || IsTraitPaused)
return;
if (state == PopupState.Open && idleTicks++ > info.CloseDelay)
{
var facingOffset = new WVec(0, -1024, 0).Rotate(WRot.FromYaw(info.DefaultFacing));

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

@@ -120,7 +120,7 @@ namespace OpenRA.Mods.Cnc.Traits
protected override void Tick(World world)
{
// Cancel the OG if we can't use the power
if (!manager.Powers.ContainsKey(order))
if (!manager.Powers.TryGetValue(order, out var p) || !p.Active || !p.Ready)
world.CancelInputMode();
}

View File

@@ -174,7 +174,7 @@ namespace OpenRA.Mods.Cnc.Traits
protected override void Tick(World world)
{
// Cancel the OG if we can't use the power
if (!manager.Powers.ContainsKey(order))
if (!manager.Powers.TryGetValue(order, out var p) || !p.Active || !p.Ready)
world.CancelInputMode();
}
@@ -274,7 +274,7 @@ namespace OpenRA.Mods.Cnc.Traits
protected override void Tick(World world)
{
// Cancel the OG if we can't use the power
if (!manager.Powers.ContainsKey(order))
if (!manager.Powers.TryGetValue(order, out var p) || !p.Active || !p.Ready)
world.CancelInputMode();
}

View File

@@ -142,6 +142,13 @@ namespace OpenRA.Mods.Common.Activities
// We don't know where the target actually is, so move to where we last saw it
if (useLastVisibleTarget)
{
// HACK: Bot players ignore the standard visibility checks in target.Recalculate,
// which means that targetIsHiddenActor is always false, allowing lastVisibleMaximumRange
// to be assigned zero range by attackAircraft.GetMaximumRangeVersusTarget for e.g. cloaked actors.
// Catch and cancel this edge case to avoid the aircraft stopping mid-air!
if (self.Owner.IsBot && lastVisibleMaximumRange == WDist.Zero)
return true;
// We've reached the assumed position but it is not there - give up
if (checkTarget.IsInRange(pos, lastVisibleMaximumRange))
return true;

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

@@ -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,22 +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;
}
}
targetLineNodes.Insert(0, exitPos);
var exit = building.NearestExitOrDefault(targetLineNodes[0]);
targetLineNodes.Insert(0, building.CenterPosition + (exit?.Info.SpawnOffset ?? WVec.Zero));
}
IEnumerable<IRenderable> IEffect.Render(WorldRenderer wr) { return SpriteRenderable.None; }

View File

@@ -319,7 +319,7 @@ namespace OpenRA.Mods.Common.FileFormats
// Annoyingly, the complete table is not applied until the frame
// *after* the one that contains the 8th chunk.
// Do we have a set of partial lookup tables ready to apply?
if (currentChunkBuffer == chunkBufferParts)
if (currentChunkBuffer == chunkBufferParts && chunkBufferParts != 0)
{
if (!cbpIsCompressed)
cbf = (byte[])cbp.Clone();

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

@@ -21,12 +21,14 @@ namespace OpenRA.Mods.Common.Scripting
{
readonly SpawnMapActors sma;
readonly World world;
readonly GameSettings gameSettings;
public MapGlobal(ScriptContext context)
: base(context)
{
sma = context.World.WorldActor.Trait<SpawnMapActors>();
world = context.World;
gameSettings = Game.Settings.Game;
// Register map actors as globals (yuck!)
foreach (var kv in sma.Actors)
@@ -109,6 +111,9 @@ namespace OpenRA.Mods.Common.Scripting
[Desc("Returns true if there is only one human player.")]
public bool IsSinglePlayer { get { return Context.World.LobbyInfo.NonBotPlayers.Count() == 1; } }
[Desc("Returns true if this is a shellmap and the player has paused animations.")]
public bool IsPausedShellmap { get { return Context.World.Type == WorldType.Shellmap && gameSettings.PauseShellmap; } }
[Desc("Returns the value of a `ScriptLobbyDropdown` selected in the game lobby.")]
public LuaValue LobbyOption(string id)
{

View File

@@ -9,6 +9,8 @@
*/
#endregion
using Eluant;
using OpenRA.Primitives;
using OpenRA.Scripting;
using OpenRA.Traits;
@@ -34,10 +36,18 @@ namespace OpenRA.Mods.Common.Scripting
[Desc("Maximum health of the actor.")]
public int MaxHealth { get { return health.MaxHP; } }
[Desc("Kill the actor.")]
public void Kill()
[Desc("Kill the actor. damageTypes may be omitted, specified as a string, or as table of strings.")]
public void Kill(object damageTypes = null)
{
health.InflictDamage(Self, Self, new Damage(health.MaxHP), true);
Damage damage;
if (damageTypes is string d)
damage = new Damage(health.MaxHP, new BitSet<DamageType>(new[] { d }));
else if (damageTypes is LuaTable t && t.TryGetClrValue(out string[] ds))
damage = new Damage(health.MaxHP, new BitSet<DamageType>(ds));
else
damage = new Damage(health.MaxHP);
health.InflictDamage(Self, Self, damage, true);
}
}
}

View File

@@ -52,6 +52,16 @@ namespace OpenRA.Mods.Common.Scripting
}
}
[Desc("The player's handicap level.")]
public int Handicap
{
get
{
var c = Player.World.LobbyInfo.Clients.FirstOrDefault(i => i.Index == Player.ClientIndex);
return c?.Handicap ?? 0;
}
}
[Desc("Returns true if the player is a bot.")]
public bool IsBot { get { return Player.IsBot; } }

View File

@@ -43,6 +43,7 @@ namespace OpenRA.Mods.Common.Server
{ "name", Name },
{ "faction", Faction },
{ "team", Team },
{ "handicap", Handicap },
{ "spawn", Spawn },
{ "clear_spawn", ClearPlayerSpawn },
{ "color", PlayerColor },
@@ -245,6 +246,7 @@ namespace OpenRA.Mods.Common.Server
client.Slot = null;
client.SpawnPoint = 0;
client.Team = 0;
client.Handicap = 0;
client.Color = Color.White;
server.SyncLobbyClients();
CheckAutoStart(server);
@@ -377,6 +379,7 @@ namespace OpenRA.Mods.Common.Server
Faction = "Random",
SpawnPoint = 0,
Team = 0,
Handicap = 0,
State = Session.ClientState.NotReady,
BotControllerClientIndex = controllerClientIndex
};
@@ -736,6 +739,7 @@ namespace OpenRA.Mods.Common.Server
targetClient.Slot = null;
targetClient.SpawnPoint = 0;
targetClient.Team = 0;
targetClient.Handicap = 0;
targetClient.Color = Color.White;
targetClient.State = Session.ClientState.NotReady;
server.SendMessage("{0} moved {1} to spectators.".F(client.Name, targetClient.Name));
@@ -824,6 +828,42 @@ namespace OpenRA.Mods.Common.Server
}
}
static bool Handicap(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
{
var parts = s.Split(' ');
var targetClient = server.LobbyInfo.ClientWithIndex(Exts.ParseIntegerInvariant(parts[0]));
// Only the host can change other client's info
if (targetClient.Index != client.Index && !client.IsAdmin)
return true;
// Map has disabled handicap changes
if (server.LobbyInfo.Slots[targetClient.Slot].LockHandicap)
return true;
if (!Exts.TryParseIntegerInvariant(parts[1], out var handicap))
{
Log.Write("server", "Invalid handicap: {0}", s);
return false;
}
// Handicaps may be set between 0 - 95% in steps of 5%
var options = Enumerable.Range(0, 20).Select(i => 5 * i);
if (!options.Contains(handicap))
{
Log.Write("server", "Invalid handicap: {0}", s);
return false;
}
targetClient.Handicap = handicap;
server.SyncLobbyClients();
return true;
}
}
static bool ClearPlayerSpawn(S server, Connection conn, Session.Client client, string s)
{
var spawnPoint = Exts.ParseIntegerInvariant(s);
@@ -1003,6 +1043,7 @@ namespace OpenRA.Mods.Common.Server
LockFaction = pr.LockFaction,
LockColor = pr.LockColor,
LockTeam = pr.LockTeam,
LockHandicap = pr.LockHandicap,
LockSpawn = pr.LockSpawn,
Required = pr.Required,
};

View File

@@ -22,9 +22,6 @@ namespace OpenRA.Mods.Common.Server
{
lock (server.LobbyInfo)
{
if (server.LobbyInfo.ClientWithIndex(conn.PlayerIndex).IsAdmin)
return;
var defaults = new Session.Global();
LobbyCommands.LoadMapSettings(server, defaults, server.Map.Rules);

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
@@ -348,8 +348,11 @@ namespace OpenRA.Mods.Common.Traits
return chosenTarget;
var targetsInRange = self.World.FindActorsInCircle(self.CenterPosition, scanRange)
.Select(Target.FromActor)
.Concat(self.Owner.FrozenActorLayer.FrozenActorsInCircle(self.World, self.CenterPosition, scanRange)
.Select(Target.FromActor);
if (allowMove || ab.Info.TargetFrozenActors)
targetsInRange = targetsInRange
.Concat(self.Owner.FrozenActorLayer.FrozenActorsInCircle(self.World, self.CenterPosition, scanRange)
.Select(Target.FromFrozenActor));
foreach (var target in targetsInRange)
@@ -391,7 +394,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

@@ -53,8 +53,7 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
if (AttackOrFleeFuzzy.Default.CanAttack(owner.Units, enemyUnits))
{
foreach (var u in owner.Units)
owner.Bot.QueueOrder(new Order("AttackMove", u, Target.FromCell(owner.World, owner.TargetActor.Location), false));
owner.Bot.QueueOrder(new Order("AttackMove", null, Target.FromCell(owner.World, owner.TargetActor.Location), false, groupedActors: owner.Units.ToArray()));
// We have gathered sufficient units. Attack the nearest enemy unit.
owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsAttackMoveState(), true);
@@ -124,8 +123,9 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
// Since units have different movement speeds, they get separated while approaching the target.
// Let them regroup into tighter formation.
owner.Bot.QueueOrder(new Order("Stop", leader, false));
foreach (var unit in owner.Units.Where(a => !ownUnits.Contains(a)))
owner.Bot.QueueOrder(new Order("AttackMove", unit, Target.FromCell(owner.World, leader.Location), false));
var units = owner.Units.Where(a => !ownUnits.Contains(a)).ToArray();
owner.Bot.QueueOrder(new Order("AttackMove", null, Target.FromCell(owner.World, leader.Location), false, groupedActors: units));
}
else
{
@@ -138,8 +138,7 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsAttackState(), true);
}
else
foreach (var a in owner.Units)
owner.Bot.QueueOrder(new Order("AttackMove", a, Target.FromCell(owner.World, owner.TargetActor.Location), false));
owner.Bot.QueueOrder(new Order("AttackMove", null, Target.FromCell(owner.World, owner.TargetActor.Location), false, groupedActors: owner.Units.ToArray()));
}
if (ShouldFlee(owner))

View File

@@ -78,8 +78,7 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
if (AttackOrFleeFuzzy.Default.CanAttack(owner.Units, enemyUnits))
{
foreach (var u in owner.Units)
owner.Bot.QueueOrder(new Order("AttackMove", u, Target.FromCell(owner.World, owner.TargetActor.Location), false));
owner.Bot.QueueOrder(new Order("AttackMove", null, Target.FromCell(owner.World, owner.TargetActor.Location), false, groupedActors: owner.Units.ToArray()));
// We have gathered sufficient units. Attack the nearest enemy unit.
owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsAttackMoveState(), true);
@@ -149,8 +148,9 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
// Since units have different movement speeds, they get separated while approaching the target.
// Let them regroup into tighter formation.
owner.Bot.QueueOrder(new Order("Stop", leader, false));
foreach (var unit in owner.Units.Where(a => !ownUnits.Contains(a)))
owner.Bot.QueueOrder(new Order("AttackMove", unit, Target.FromCell(owner.World, leader.Location), false));
var units = owner.Units.Where(a => !ownUnits.Contains(a)).ToArray();
owner.Bot.QueueOrder(new Order("AttackMove", null, Target.FromCell(owner.World, leader.Location), false, groupedActors: units));
}
else
{
@@ -163,8 +163,7 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsAttackState(), true);
}
else
foreach (var a in owner.Units)
owner.Bot.QueueOrder(new Order("AttackMove", a, Target.FromCell(owner.World, owner.TargetActor.Location), false));
owner.Bot.QueueOrder(new Order("AttackMove", null, Target.FromCell(owner.World, owner.TargetActor.Location), false, groupedActors: owner.Units.ToArray()));
}
if (ShouldFlee(owner))

View File

@@ -55,10 +55,7 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
Backoff--;
}
else
{
foreach (var a in owner.Units)
owner.Bot.QueueOrder(new Order("AttackMove", a, Target.FromCell(owner.World, owner.TargetActor.Location), false));
}
owner.Bot.QueueOrder(new Order("AttackMove", null, Target.FromCell(owner.World, owner.TargetActor.Location), false, groupedActors: owner.Units.ToArray()));
}
public void Deactivate(Squad owner) { }

View File

@@ -53,7 +53,7 @@ namespace OpenRA.Mods.Common.Traits
// This is important because p may have side-effects that trigger a desync if not
// called on the same exits in the same order!
var all = Exits(actor, productionType)
.OrderBy(e => e.Info.Priority)
.OrderByDescending(e => e.Info.Priority)
.ThenBy(e => (actor.World.Map.CenterOfCell(actor.Location + e.Info.ExitCell) - pos).LengthSquared)
.ToList();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,40 @@
#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 OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Modifies the damage applied to this actor based on the owner's handicap.")]
public class HandicapDamageMultiplierInfo : TraitInfo
{
public override object Create(ActorInitializer init) { return new HandicapDamageMultiplier(init.Self); }
}
public class HandicapDamageMultiplier : IDamageModifier
{
readonly Actor self;
public HandicapDamageMultiplier(Actor self)
{
this.self = self;
}
int IDamageModifier.GetDamageModifier(Actor attacker, Damage damage)
{
// Equivalent to the health handicap from C&C3:
// 5% handicap = 95% health = 105% damage
// 50% handicap = 50% health = 200% damage
// 95% handicap = 5% health = 2000% damage
return 10000 / (100 - self.Owner.Handicap);
}
}
}

View File

@@ -0,0 +1,40 @@
#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 OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Modifies the damage applied by this actor based on the owner's handicap.")]
public class HandicapFirepowerMultiplierInfo : TraitInfo
{
public override object Create(ActorInitializer init) { return new HandicapFirepowerMultiplier(init.Self); }
}
public class HandicapFirepowerMultiplier : IFirepowerModifier
{
readonly Actor self;
public HandicapFirepowerMultiplier(Actor self)
{
this.self = self;
}
int IFirepowerModifier.GetFirepowerModifier()
{
// Equivalent to the firepower handicap from C&C3:
// 5% handicap = 95% firepower
// 50% handicap = 50% firepower
// 95% handicap = 5% firepower
return 100 - self.Owner.Handicap;
}
}
}

View File

@@ -0,0 +1,32 @@
#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;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Modifies the production time of this actor based on the producer's handicap.")]
public class HandicapProductionTimeMultiplierInfo : TraitInfo<HandicapProductionTimeMultiplier>, IProductionTimeModifierInfo
{
int IProductionTimeModifierInfo.GetProductionTimeModifier(TechTree techTree, string queue)
{
// Equivalent to the build speed handicap from C&C3:
// 5% handicap = 105% build time
// 50% handicap = 150% build time
// 95% handicap = 195% build time
return 100 + techTree.Owner.Handicap;
}
}
public class HandicapProductionTimeMultiplier { }
}

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

@@ -412,8 +412,11 @@ namespace OpenRA.Mods.Common.Traits
var hasPlayedSound = false;
BeginProduction(new ProductionItem(this, order.TargetString, cost, playerPower, () => self.World.AddFrameEndTask(_ =>
{
var isBuilding = unit.HasTraitInfo<BuildingInfo>();
// Make sure the item hasn't been invalidated between the ProductionItem ticking and this FrameEndTask running
if (!Queue.Any(i => i.Done && i.Item == unit.Name))
return;
var isBuilding = unit.HasTraitInfo<BuildingInfo>();
if (isBuilding && !hasPlayedSound)
hasPlayedSound = Game.Sound.PlayNotification(rules, self.Owner, "Speech", Info.ReadyAudio, self.Owner.Faction.InternalName);
else if (!isBuilding)

View File

@@ -106,6 +106,8 @@ namespace OpenRA.Mods.Common.Traits
return ret;
}
public Player Owner { get { return player; } }
class Watcher
{
public readonly string Key;

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

@@ -38,6 +38,9 @@ namespace OpenRA.Mods.Common.Traits.Render
[PaletteReference]
public readonly string Palette = "chrome";
[Desc("Name(s) of AmmoPool(s) that use this decoration. Leave empty to include all pools.")]
public readonly string[] AmmoPools = { };
public override object Create(ActorInitializer init) { return new WithAmmoPipsDecoration(init.Self, this); }
}
@@ -49,7 +52,13 @@ namespace OpenRA.Mods.Common.Traits.Render
public WithAmmoPipsDecoration(Actor self, WithAmmoPipsDecorationInfo info)
: base(self, info)
{
ammo = self.TraitsImplementing<AmmoPool>().ToArray();
if (info.AmmoPools.Any())
ammo = self.TraitsImplementing<AmmoPool>()
.Where(ap => info.AmmoPools.Contains(ap.Info.Name))
.ToArray();
else
ammo = self.TraitsImplementing<AmmoPool>().ToArray();
pips = new Animation(self.World, info.Image);
}

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

@@ -29,8 +29,10 @@ namespace OpenRA.Mods.Common.Traits.Render
public readonly string DefaultAttackSequence = null;
[SequenceReference(dictionaryReference: LintDictionaryReference.Values)]
[Desc("Attack sequence to use for each armament.")]
public readonly Dictionary<string, string> AttackSequences = new Dictionary<string, string>();
[Desc("Attack sequence to use for each armament.",
"A dictionary of [armament name]: [sequence name(s)].",
"Multiple sequence names can be defined to specify per-burst animations.")]
public readonly Dictionary<string, string[]> AttackSequences = new Dictionary<string, string[]>();
[SequenceReference]
public readonly string[] IdleSequences = { };
@@ -133,11 +135,21 @@ namespace OpenRA.Mods.Common.Traits.Render
}
}
public void Attacking(Actor self, in Target target, Armament a)
void Attacking(Actor self, Armament a, Barrel barrel)
{
var info = GetDisplayInfo();
if (!info.AttackSequences.TryGetValue(a.Info.Name, out var sequence))
sequence = info.DefaultAttackSequence;
var sequence = info.DefaultAttackSequence;
if (info.AttackSequences.TryGetValue(a.Info.Name, out var sequences) && sequences.Length > 0)
{
sequence = sequences[0];
// Find the sequence corresponding to this barrel/burst.
if (barrel != null && sequences.Length > 1)
for (var i = 0; i < sequences.Length; i++)
if (a.Barrels[i] == barrel)
sequence = sequences[i];
}
if (!string.IsNullOrEmpty(sequence) && DefaultAnimation.HasSequence(NormalizeInfantrySequence(self, sequence)))
{
@@ -148,7 +160,9 @@ namespace OpenRA.Mods.Common.Traits.Render
void INotifyAttack.PreparingAttack(Actor self, in Target target, Armament a, Barrel barrel)
{
Attacking(self, target, a);
// 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, a, barrel));
}
void INotifyAttack.Attacking(Actor self, in Target target, Armament a, Barrel barrel) { }
@@ -160,10 +174,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>()
@@ -137,7 +137,7 @@ namespace OpenRA.Mods.Common.Traits
protected override void Tick(World world)
{
// Cancel the OG if we can't use the power
if (!manager.Powers.ContainsKey(order))
if (!manager.Powers.TryGetValue(order, out var p) || !p.Active || !p.Ready)
world.CancelInputMode();
}

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

@@ -106,7 +106,7 @@ namespace OpenRA.Mods.Common.Traits
void IOrderGenerator.Tick(World world)
{
// Cancel the OG if we can't use the power
if (!manager.Powers.ContainsKey(order))
if (!manager.Powers.TryGetValue(order, out var p) || !p.Active || !p.Ready)
world.CancelInputMode();
}

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

@@ -309,7 +309,7 @@ namespace OpenRA.Mods.Common.Traits
protected override void Tick(World world)
{
// Cancel the OG if we can't use the power
if (!manager.Powers.ContainsKey(order))
if (!manager.Powers.TryGetValue(order, out var p) || !p.Active || !p.Ready)
world.CancelInputMode();
}

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,8 +60,11 @@ 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,
Handicap = client.Handicap,
SpawnPoint = resolvedSpawnPoint,
IsRandomFaction = clientFaction.RandomFactionMembers.Any(),
IsRandomSpawnPoint = client.SpawnPoint == 0,

View File

@@ -214,6 +214,10 @@ namespace OpenRA.Mods.Common.Traits
if (factionInit != null && factionInit.Value == Owner.Faction)
return false;
var healthInit = init as HealthInit;
if (healthInit != null && healthInit.Value == 100)
return false;
// TODO: Other default values will need to be filtered
// here after we have built a properties panel
return true;

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

@@ -218,6 +218,9 @@ namespace OpenRA.Mods.Common
if (t.IsGenericType && t.GetGenericTypeDefinition().GetInterfaces().Any(e => e.IsGenericType && e.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
return "Collection of {0}".F(FriendlyTypeName(t.GetGenericArguments().First()));
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
return "{0} (optional)".F(t.GetGenericArguments().Select(FriendlyTypeName).First());
if (t == typeof(int) || t == typeof(uint))
return "Integer";

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

@@ -268,11 +268,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
case "delete":
{
var sourcePath = Path.Combine(path, i.Value.Value);
// Try as an absolute path
if (!File.Exists(sourcePath))
sourcePath = Platform.ResolvePath(i.Value.Value);
// Yaml path may be specified relative to a named directory (e.g. ^SupportDir) or the detected disc path
var sourcePath = i.Value.Value.StartsWith("^") ? Platform.ResolvePath(i.Value.Value) : Path.Combine(path, i.Value.Value);
Log.Write("debug", "Deleting {0}", sourcePath);
File.Delete(sourcePath);
@@ -325,11 +322,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
static void ExtractFromPackage(ExtractionType type, string path, MiniYaml actionYaml, List<string> extractedFiles, Action<string> updateMessage)
{
var sourcePath = Path.Combine(path, actionYaml.Value);
// Try as an absolute path
if (!File.Exists(sourcePath))
sourcePath = Platform.ResolvePath(actionYaml.Value);
// Yaml path may be specified relative to a named directory (e.g. ^SupportDir) or the detected disc path
var sourcePath = actionYaml.Value.StartsWith("^") ? Platform.ResolvePath(actionYaml.Value) : Path.Combine(path, actionYaml.Value);
using (var source = File.OpenRead(sourcePath))
{
@@ -384,11 +378,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
static void ExtractFromMSCab(string path, MiniYaml actionYaml, List<string> extractedFiles, Action<string> updateMessage)
{
var sourcePath = Path.Combine(path, actionYaml.Value);
// Try as an absolute path
if (!File.Exists(sourcePath))
sourcePath = Platform.ResolvePath(actionYaml.Value);
// Yaml path may be specified relative to a named directory (e.g. ^SupportDir) or the detected disc path
var sourcePath = actionYaml.Value.StartsWith("^") ? Platform.ResolvePath(actionYaml.Value) : Path.Combine(path, actionYaml.Value);
using (var source = File.OpenRead(sourcePath))
{
@@ -418,11 +409,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
static void ExtractFromISCab(string path, MiniYaml actionYaml, List<string> extractedFiles, Action<string> updateMessage)
{
var sourcePath = Path.Combine(path, actionYaml.Value);
// Try as an absolute path
if (!File.Exists(sourcePath))
sourcePath = Platform.ResolvePath(actionYaml.Value);
// Yaml path may be specified relative to a named directory (e.g. ^SupportDir) or the detected disc path
var sourcePath = actionYaml.Value.StartsWith("^") ? Platform.ResolvePath(actionYaml.Value) : Path.Combine(path, actionYaml.Value);
var volumeNode = actionYaml.Nodes.FirstOrDefault(n => n.Key == "Volumes");
if (volumeNode == null)

View File

@@ -624,6 +624,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
LobbyUtils.SetupEditableColorWidget(template, slot, client, orderManager, shellmapWorld, colorPreview);
LobbyUtils.SetupEditableFactionWidget(template, slot, client, orderManager, factions);
LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, map);
LobbyUtils.SetupEditableHandicapWidget(template, slot, client, orderManager, map);
LobbyUtils.SetupEditableSpawnWidget(template, slot, client, orderManager, map);
LobbyUtils.SetupEditableReadyWidget(template, slot, client, orderManager, map);
}
@@ -640,6 +641,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (isHost)
{
LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, map);
LobbyUtils.SetupEditableHandicapWidget(template, slot, client, orderManager, map);
LobbyUtils.SetupEditableSpawnWidget(template, slot, client, orderManager, map);
LobbyUtils.SetupPlayerActionWidget(template, slot, client, orderManager, worldRenderer,
lobby, () => panel = PanelType.Kick, () => panel = PanelType.Players);
@@ -648,6 +650,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{
LobbyUtils.SetupNameWidget(template, slot, client, orderManager, worldRenderer);
LobbyUtils.SetupTeamWidget(template, slot, client);
LobbyUtils.SetupHandicapWidget(template, slot, client);
LobbyUtils.SetupSpawnWidget(template, slot, client);
}
@@ -752,7 +755,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
void UpdateDiscordStatus()
{
var mapTitle = map.Title;
var numberOfPlayers = 0;
var slots = 0;
@@ -771,6 +773,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic
}
}
// Add extra slots to keep the join button active for spectators
if (numberOfPlayers == slots && orderManager.LobbyInfo.GlobalSettings.AllowSpectators)
slots = numberOfPlayers + 1;
var details = map.Title + " - " + orderManager.LobbyInfo.GlobalSettings.ServerName;
if (updateDiscordStatus)
{
string secret = null;
@@ -781,8 +788,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
}
var state = skirmishMode ? DiscordState.InSkirmishLobby : DiscordState.InMultiplayerLobby;
DiscordService.UpdateStatus(state, mapTitle, secret, numberOfPlayers, slots);
DiscordService.UpdateStatus(state, details, secret, numberOfPlayers, slots);
updateDiscordStatus = false;
}
else
@@ -790,7 +797,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (!skirmishMode)
DiscordService.UpdatePlayers(numberOfPlayers, slots);
DiscordService.UpdateDetails(mapTitle);
DiscordService.UpdateDetails(details);
}
}
@@ -806,7 +813,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Ui.CloseWindow();
var state = skirmishMode ? DiscordState.PlayingSkirmish : DiscordState.PlayingMultiplayer;
DiscordService.UpdateStatus(state);
var details = map.Title + " - " + orderManager.LobbyInfo.GlobalSettings.ServerName;
DiscordService.UpdateStatus(state, details);
onStart();
}

View File

@@ -146,6 +146,25 @@ namespace OpenRA.Mods.Common.Widgets.Logic
dropdown.ShowDropDown("TEAM_DROPDOWN_TEMPLATE", 150, options, setupItem);
}
public static void ShowHandicapDropDown(DropDownButtonWidget dropdown, Session.Client client,
OrderManager orderManager)
{
Func<int, ScrollItemWidget, ScrollItemWidget> setupItem = (ii, itemTemplate) =>
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => client.Handicap == ii,
() => orderManager.IssueOrder(Order.Command("handicap {0} {1}".F(client.Index, ii))));
var label = "{0}%".F(ii);
item.Get<LabelWidget>("LABEL").GetText = () => label;
return item;
};
// Handicaps may be set between 0 - 95% in steps of 5%
var options = Enumerable.Range(0, 20).Select(i => 5 * i);
dropdown.ShowDropDown("TEAM_DROPDOWN_TEMPLATE", 150, options, setupItem);
}
public static void ShowSpawnDropDown(DropDownButtonWidget dropdown, Session.Client client,
OrderManager orderManager, IEnumerable<int> spawnPoints)
{
@@ -562,6 +581,29 @@ namespace OpenRA.Mods.Common.Widgets.Logic
HideChildWidget(parent, "TEAM_DROPDOWN");
}
public static void SetupEditableHandicapWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, MapPreview map)
{
var dropdown = parent.Get<DropDownButtonWidget>("HANDICAP_DROPDOWN");
dropdown.IsVisible = () => true;
dropdown.IsDisabled = () => s.LockTeam || orderManager.LocalClient.IsReady;
dropdown.OnMouseDown = _ => ShowHandicapDropDown(dropdown, c, orderManager);
var handicapLabel = new CachedTransform<int, string>(h => "{0}%".F(h));
dropdown.GetText = () => handicapLabel.Update(c.Handicap);
HideChildWidget(parent, "HANDICAP");
}
public static void SetupHandicapWidget(Widget parent, Session.Slot s, Session.Client c)
{
var team = parent.Get<LabelWidget>("HANDICAP");
team.IsVisible = () => true;
var handicapLabel = new CachedTransform<int, string>(h => "{0}%".F(h));
team.GetText = () => handicapLabel.Update(c.Handicap);
HideChildWidget(parent, "HANDICAP_DROPDOWN");
}
public static void SetupEditableSpawnWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, MapPreview map)
{
var dropdown = parent.Get<DropDownButtonWidget>("SPAWN_DROPDOWN");

View File

@@ -43,7 +43,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
this.modData = modData;
this.onSelect = onSelect;
var approving = new Action(() => { Ui.CloseWindow(); onSelect(selectedUid); });
var approving = new Action(() =>
{
Ui.CloseWindow();
onSelect?.Invoke(selectedUid);
});
var canceling = new Action(() => { Ui.CloseWindow(); onExit(); });
var okButton = widget.Get<ButtonWidget>("BUTTON_OK");

View File

@@ -334,14 +334,24 @@ namespace OpenRA.Mods.Common.Widgets.Logic
List<GameServer> games = null;
if (i.Error == null)
{
games = new List<GameServer>();
try
{
var data = Encoding.UTF8.GetString(i.Result);
var yaml = MiniYaml.FromString(data);
games = yaml.Select(a => new GameServer(a.Value))
.Where(gs => gs.Address != null)
.ToList();
foreach (var node in yaml)
{
try
{
var gs = new GameServer(node.Value);
if (gs.Address != null)
games.Add(gs);
}
catch
{
// Ignore any invalid games advertised.
}
}
}
catch
{

View File

@@ -246,6 +246,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
BindCheckboxPref(panel, "FRAME_LIMIT_CHECKBOX", ds, "CapFramerate");
BindIntSliderPref(panel, "FRAME_LIMIT_SLIDER", ds, "MaxFramerate");
BindCheckboxPref(panel, "PLAYER_STANCE_COLORS_CHECKBOX", gs, "UsePlayerStanceColors");
if (panel.GetOrNull<CheckboxWidget>("PAUSE_SHELLMAP_CHECKBOX") != null)
BindCheckboxPref(panel, "PAUSE_SHELLMAP_CHECKBOX", gs, "PauseShellmap");
var languageDropDownButton = panel.GetOrNull<DropDownButtonWidget>("LANGUAGE_DROPDOWNBUTTON");
if (languageDropDownButton != null)

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