Compare commits

..

24 Commits

Author SHA1 Message Date
Paul Chote
24459c29ae Merge branch 'gh-actions' into devtest-integration 2020-12-09 22:06:34 +00:00
Paul Chote
6347049e1c 18892 (pchote edition) 2020-12-09 22:06:30 +00:00
Paul Chote
45bc278647 Merge remote-tracking branch 'upstream/pr/18913' into devtest-integration 2020-12-09 22:05:58 +00:00
Paul Chote
7ab35bf127 Merge remote-tracking branch 'upstream/pr/18912' into devtest-integration 2020-12-09 22:05:51 +00:00
Paul Chote
01044f12d1 Merge remote-tracking branch 'upstream/pr/18894' into devtest-integration 2020-12-09 22:05:42 +00:00
Paul Chote
d0ec33758b Merge remote-tracking branch 'upstream/pr/18695' into devtest-integration 2020-12-09 22:05:32 +00:00
Paul Chote
7dec826591 Merge remote-tracking branch 'upstream/pr/18678' into devtest-integration 2020-12-09 22:05:24 +00:00
Paul Chote
c6eec76af9 Merge remote-tracking branch 'upstream/pr/18430' into devtest-integration 2020-12-09 22:05:17 +00:00
Paul Chote
2e1c1f1305 Merge remote-tracking branch 'upstream/pr/18874' into devtest-integration 2020-12-09 22:05:10 +00:00
Paul Chote
7abde1cc5f Merge remote-tracking branch 'upstream/pr/18902' into devtest-integration 2020-12-09 22:04:59 +00:00
Paul Chote
2f67fc64de Fix infantry move animations overriding attack animations in the same tick. 2020-12-09 21:56:17 +00:00
Paul Chote
880a584bd5 Revert "Fix WithInfantryBody wrongly overwriting attack animations"
This reverts commit 1a63cc4a41.
2020-12-09 21:53:39 +00:00
Paul Chote
756188b0d0 Prevent Civilians from wandering onto Tiberium. 2020-12-09 21:49:57 +00:00
Paul Chote
f1b0ef20c7 Add AvoidTerrainTypes to ScaredyCat. 2020-12-09 19:43:22 +00:00
Paul Chote
e014795c25 Add AvoidTerrainTypes to Wanders. 2020-12-09 19:42:14 +00:00
Paul Chote
baed329f21 Fix restart black screen race condition. 2020-12-06 21:42:42 +00:00
Paul Chote
4c113fd7b6 Migrate CI and packaging from Travis CI to GitHub Actions. 2020-12-06 16:37:05 +00:00
Paul Chote
b690157c5b Fix rally point target line exit display. 2020-12-05 13:30:52 +00:00
Paul Chote
e29d52d9dc Add DisplayFaction details to the replay metadata. 2020-11-29 18:00:15 +00:00
yuantse
b4e6b3fd6f Add Allies 09a 2020-11-15 11:44:52 +08:00
Smittytron
7903f7bd4b Add Counterstrike mission Sarin Gas 3: Controlled Burn 2020-11-14 12:56:15 -06:00
abcdefg30
ccc639ad22 Add an update rule 2020-11-14 13:11:29 +01:00
abcdefg30
6b563c8170 Update the rules of the default mods 2020-11-14 13:11:26 +01:00
abcdefg30
07fdcbb140 Rename Stances to Relationships in the yaml api 2020-11-14 13:10:09 +01:00
177 changed files with 1282 additions and 2539 deletions

View File

@@ -2,13 +2,14 @@ name: Continuous Integration
on:
push:
workflow_dispatch:
pull_request:
branches: [ bleed ]
jobs:
linux-mono:
name: Linux (mono)
runs-on: ubuntu-20.04
runs-on: ubuntu-16.04
steps:
- name: Clone Repository
@@ -18,6 +19,7 @@ jobs:
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: |
@@ -26,26 +28,18 @@ jobs:
make test
windows:
name: Windows (Net 5.0)
name: Windows (Framework 4.7)
runs-on: windows-2019
steps:
- name: Clone Repository
uses: actions/checkout@v2
- name: Install .NET 5
uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'
- name: Check Code
shell: powershell
run: |
# Work around runtime failures on the GH Actions runner
dotnet nuget locals all --clear
.\make.ps1 check
dotnet build OpenRA.Test\OpenRA.Test.csproj -c Debug --nologo -p:TargetPlatform=win-x64
dotnet test bin\OpenRA.Test.dll --test-adapter-path:.
Invoke-Expression "$home\.nuget\packages\nunit.consolerunner\3.11.1\tools\nunit3-console.exe --noresult bin/OpenRA.Test.dll"
- name: Check Mods
run: |

View File

@@ -1,107 +0,0 @@
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

View File

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

View File

@@ -10,7 +10,7 @@ on:
jobs:
linux:
name: Linux AppImages
runs-on: ubuntu-20.04
runs-on: ubuntu-16.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
@@ -39,11 +39,6 @@ jobs:
- name: Clone Repository
uses: actions/checkout@v2
- name: Install .NET 5
uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'
- name: Prepare Environment
run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
@@ -69,20 +64,18 @@ jobs:
windows:
name: Windows Installers
runs-on: ubuntu-20.04
runs-on: ubuntu-16.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
- name: Install .NET 5
uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'
- name: Prepare Environment
run: |
echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
sudo apt install nsis wine64
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
- name: Package Installers
run: |
@@ -97,3 +90,155 @@ jobs:
overwrite: true
file_glob: true
file: build/windows/*
itch:
name: Deploy to itch.io
runs-on: ubuntu-16.04
if: github.repository == 'openra/openra' && startsWith(github.ref, 'refs/tags/release-')
needs: [linux, macos, windows]
steps:
- name: Prepare Environment
run: |
echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
- name: Download Packages
run: |
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-${GIT_TAG}-x64.exe"
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-${GIT_TAG}-x64-winportable.zip" -O "OpenRA-${GIT_TAG}-x64-win-itch.zip"
wget -q "https://github.com${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-${GIT_TAG}.dmg"
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-Dune-2000-x86_64.AppImage"
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-Red-Alert-x86_64.AppImage"
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-Tiberian-Dawn-x86_64.AppImage"
wget -q "https://raw.githubusercontent.com/${{ github.repository }}/${GIT_TAG}/packaging/.itch.toml"
zip -u "OpenRA-${GIT_TAG}-x64-win-itch.zip" .itch.toml
- name: Publish Windows Installer
uses: josephbmanley/butler-publish-itchio-action@master
env:
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
CHANNEL: win
ITCH_GAME: openra
ITCH_USER: openra-developers
VERSION: ${{ env.GIT_TAG }}
PACKAGE: OpenRA-${{ env.GIT_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: ${{ env.GIT_TAG }}
PACKAGE: OpenRA-${{ env.GIT_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: ${{ env.GIT_TAG }}
PACKAGE: OpenRA-${{ env.GIT_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: ${{ env.GIT_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: ${{ env.GIT_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: ${{ env.GIT_TAG }}
PACKAGE: OpenRA-Dune-2000-x86_64.AppImage
docs:
name: Update Docs
if: github.repository == 'openra/openra' && (startsWith(github.ref, 'refs/tags/playtest-') || startsWith(github.ref, 'refs/tags/release-'))
runs-on: ubuntu-16.04
needs: [linux, macos, windows]
steps:
- name: Clone Repository
uses: actions/checkout@v2
- name: Prepare Environment
run: |
echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
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.ref, 'refs/tags/playtest-')
run: |
./utility.sh all --settings-docs "${GIT_TAG}" > "wiki/Settings (playtest).md"
- name: Update Wiki (Release)
if: startsWith(github.ref, 'refs/tags/release-')
run: |
./utility.sh all --settings-docs "${GIT_TAG}" > "wiki/Settings.md"
- name: Push Wiki
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
- name: Clone docs.openra.net
uses: actions/checkout@v2
with:
repository: openra/docs.git
token: ${{ secrets.DOCS_TOKEN }}
path: docs
- name: Update docs.openra.net (Playtest)
if: startsWith(github.ref, 'refs/tags/playtest-')
run: |
./utility.sh all --docs "${GIT_TAG}" > "docs/playtest/traits.md"
./utility.sh all --weapon-docs "${GIT_TAG}" > "docs/playtest/weapons.md"
./utility.sh all --lua-docs "${GIT_TAG}" > "docs/playtest/lua.md"
- name: Update docs.openra.net (Release)
if: startsWith(github.ref, 'refs/tags/release-')
run: |
./utility.sh all --docs "${GIT_TAG}" > "docs/release/traits.md"
./utility.sh all --weapon-docs "${GIT_TAG}" > "docs/release/weapons.md"
./utility.sh all --lua-docs "${GIT_TAG}" > "docs/release/lua.md"
- name: Push docs.openra.net
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

20
.vscode/launch.json vendored
View File

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

View File

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

View File

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

View File

@@ -74,7 +74,7 @@ namespace OpenRA.Activities
// drop valid activities queued after it. Walk the queue until we find a valid activity or
// (more likely) run out of activities.
while (first != null && first.State == ActivityState.Done)
first = first.nextActivity;
first = first.NextActivity;
return first;
}
@@ -120,8 +120,7 @@ namespace OpenRA.Activities
lastRun = Tick(self);
// Avoid a single tick delay if the childactivity was just queued.
var ca = ChildActivity;
if (ca != null && ca.State == ActivityState.Queued)
if (ChildActivity != null && ChildActivity.State == ActivityState.Queued)
{
if (ChildHasPriority)
lastRun = TickChild(self) && finishing;
@@ -207,18 +206,18 @@ namespace OpenRA.Activities
public void Queue(Activity activity)
{
var it = this;
while (it.nextActivity != null)
it = it.nextActivity;
it.nextActivity = activity;
if (NextActivity != null)
NextActivity.Queue(activity);
else
NextActivity = activity;
}
public void QueueChild(Activity activity)
{
if (childActivity != null)
childActivity.Queue(activity);
if (ChildActivity != null)
ChildActivity.Queue(activity);
else
childActivity = activity;
ChildActivity = activity;
}
/// <summary>
@@ -270,21 +269,15 @@ namespace OpenRA.Activities
public IEnumerable<T> ActivitiesImplementing<T>(bool includeChildren = true) where T : IActivityInterface
{
// Skips Done child and next activities
if (includeChildren)
{
var ca = ChildActivity;
if (ca != null)
foreach (var a in ca.ActivitiesImplementing<T>())
yield return a;
}
if (includeChildren && ChildActivity != null)
foreach (var a in ChildActivity.ActivitiesImplementing<T>())
yield return a;
if (this is T)
yield return (T)(object)this;
var na = NextActivity;
if (na != null)
foreach (var a in na.ActivitiesImplementing<T>())
if (NextActivity != null)
foreach (var a in NextActivity.ActivitiesImplementing<T>())
yield return a;
}
}

View File

@@ -66,11 +66,17 @@ namespace OpenRA
static readonly ConcurrentCache<Type, FieldLoadInfo[]> TypeLoadInfo =
new ConcurrentCache<Type, FieldLoadInfo[]>(BuildTypeLoadInfo);
static readonly ConcurrentCache<MemberInfo, bool> MemberHasTranslateAttribute =
new ConcurrentCache<MemberInfo, bool>(member => member.HasAttribute<TranslateAttribute>());
static readonly ConcurrentCache<string, BooleanExpression> BooleanExpressionCache =
new ConcurrentCache<string, BooleanExpression>(expression => new BooleanExpression(expression));
static readonly ConcurrentCache<string, IntegerExpression> IntegerExpressionCache =
new ConcurrentCache<string, IntegerExpression>(expression => new IntegerExpression(expression));
static readonly object TranslationsLock = new object();
static Dictionary<string, string> translations;
public static void Load(object self, MiniYaml my)
{
var loadInfo = TypeLoadInfo[self.GetType()];
@@ -207,7 +213,11 @@ namespace OpenRA
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(string))
{
if (field != null && MemberHasTranslateAttribute[field] && value != null)
return Regex.Replace(value, "@[^@]+@", m => Translate(m.Value.Substring(1, m.Value.Length - 2)), RegexOptions.Compiled);
return value;
}
else if (fieldType == typeof(Color))
{
if (value != null && Color.TryParse(value, out var color))
@@ -684,8 +694,34 @@ namespace OpenRA
return null;
}
}
public static string Translate(string key)
{
if (string.IsNullOrEmpty(key))
return key;
lock (TranslationsLock)
{
if (translations == null)
return key;
if (!translations.TryGetValue(key, out var value))
return key;
return value;
}
}
public static void SetTranslations(IDictionary<string, string> translations)
{
lock (TranslationsLock)
FieldLoader.translations = new Dictionary<string, string>(translations);
}
}
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class TranslateAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Field)]
public sealed class FieldFromYamlKeyAttribute : FieldLoader.SerializeAttribute
{

View File

@@ -17,7 +17,6 @@ using System.Net;
using System.Text;
using ICSharpCode.SharpZipLib.Checksum;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.FileFormats
@@ -26,15 +25,12 @@ namespace OpenRA.FileFormats
{
static readonly byte[] Signature = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
public int Width { get; private set; }
public int Height { get; private set; }
public Color[] Palette { get; private set; }
public byte[] Data { get; private set; }
public SpriteFrameType Type { get; private set; }
public int Width { get; set; }
public int Height { get; set; }
public Color[] Palette { get; set; }
public byte[] Data { get; set; }
public Dictionary<string, string> EmbeddedData = new Dictionary<string, string>();
public int PixelStride { get { return Type == SpriteFrameType.Indexed8 ? 1 : Type == SpriteFrameType.Rgb24 ? 3 : 4; } }
public Png(Stream s)
{
if (!Verify(s))
@@ -42,8 +38,9 @@ namespace OpenRA.FileFormats
s.Position += 8;
var headerParsed = false;
var isPaletted = false;
var is24Bit = false;
var data = new List<byte>();
Type = SpriteFrameType.Rgba32;
while (true)
{
@@ -68,12 +65,14 @@ namespace OpenRA.FileFormats
var bitDepth = ms.ReadUInt8();
var colorType = (PngColorType)ms.ReadByte();
if (IsPaletted(bitDepth, colorType))
Type = SpriteFrameType.Indexed8;
else if (colorType == PngColorType.Color)
Type = SpriteFrameType.Rgb24;
isPaletted = IsPaletted(bitDepth, colorType);
is24Bit = colorType == PngColorType.Color;
Data = new byte[Width * Height * PixelStride];
var dataLength = Width * Height;
if (!isPaletted)
dataLength *= 4;
Data = new byte[dataLength];
var compression = ms.ReadByte();
/*var filter = */ms.ReadByte();
@@ -134,28 +133,39 @@ namespace OpenRA.FileFormats
{
using (var ds = new InflaterInputStream(ns))
{
var pxStride = PixelStride;
var rowStride = Width * pxStride;
var pxStride = isPaletted ? 1 : is24Bit ? 3 : 4;
var srcStride = Width * pxStride;
var destStride = Width * (isPaletted ? 1 : 4);
var prevLine = new byte[rowStride];
var prevLine = new byte[srcStride];
for (var y = 0; y < Height; y++)
{
var filter = (PngFilter)ds.ReadByte();
var line = ds.ReadBytes(rowStride);
var line = ds.ReadBytes(srcStride);
for (var i = 0; i < rowStride; i++)
for (var i = 0; i < srcStride; i++)
line[i] = i < pxStride
? UnapplyFilter(filter, line[i], 0, prevLine[i], 0)
: UnapplyFilter(filter, line[i], line[i - pxStride], prevLine[i], prevLine[i - pxStride]);
Array.Copy(line, 0, Data, y * rowStride, rowStride);
if (is24Bit)
{
// Fold alpha channel into RGB data
for (var i = 0; i < line.Length / 3; i++)
{
Array.Copy(line, 3 * i, Data, y * destStride + 4 * i, 3);
Data[y * destStride + 4 * i + 3] = 255;
}
}
else
Array.Copy(line, 0, Data, y * destStride, line.Length);
prevLine = line;
}
}
}
if (Type == SpriteFrameType.Indexed8 && Palette == null)
if (isPaletted && Palette == null)
throw new InvalidDataException("Non-Palette indexed PNG are not supported.");
return;
@@ -165,7 +175,7 @@ namespace OpenRA.FileFormats
}
}
public Png(byte[] data, SpriteFrameType type, int width, int height, Color[] palette = null,
public Png(byte[] data, int width, int height, Color[] palette = null,
Dictionary<string, string> embeddedData = null)
{
var expectLength = width * height;
@@ -175,46 +185,11 @@ namespace OpenRA.FileFormats
if (data.Length != expectLength)
throw new InvalidDataException("Input data does not match expected length");
Type = type;
Width = width;
Height = height;
switch (type)
{
case SpriteFrameType.Indexed8:
case SpriteFrameType.Rgba32:
case SpriteFrameType.Rgb24:
{
// Data is already in a compatible format
Data = data;
if (type == SpriteFrameType.Indexed8)
Palette = palette;
break;
}
case SpriteFrameType.Bgra32:
case SpriteFrameType.Bgr24:
{
// Convert to big endian
Data = new byte[data.Length];
var stride = PixelStride;
for (var i = 0; i < width * height; i++)
{
Data[stride * i] = data[stride * i + 2];
Data[stride * i + 1] = data[stride * i + 1];
Data[stride * i + 2] = data[stride * i + 0];
if (type == SpriteFrameType.Bgra32)
Data[stride * i + 3] = data[stride * i + 3];
}
break;
}
default:
throw new InvalidDataException("Unhandled SpriteFrameType {0}".F(type));
}
Palette = palette;
Data = data;
if (embeddedData != null)
EmbeddedData = embeddedData;
@@ -299,8 +274,9 @@ namespace OpenRA.FileFormats
header.Write(IPAddress.HostToNetworkOrder(Height));
header.WriteByte(8); // Bit depth
var colorType = Type == SpriteFrameType.Indexed8 ? PngColorType.Indexed | PngColorType.Color :
Type == SpriteFrameType.Rgb24 ? PngColorType.Color : PngColorType.Color | PngColorType.Alpha;
var colorType = Palette != null
? PngColorType.Indexed | PngColorType.Color
: PngColorType.Color | PngColorType.Alpha;
header.WriteByte((byte)colorType);
header.WriteByte(0); // Compression
@@ -310,7 +286,7 @@ namespace OpenRA.FileFormats
WritePngChunk(output, "IHDR", header);
}
var alphaPalette = false;
bool alphaPalette = false;
if (Palette != null)
{
using (var palette = new MemoryStream())
@@ -342,12 +318,12 @@ namespace OpenRA.FileFormats
{
using (var compressed = new DeflaterOutputStream(data))
{
var rowStride = Width * PixelStride;
var stride = Width * (Palette != null ? 1 : 4);
for (var y = 0; y < Height; y++)
{
// Write uncompressed scanlines for simplicity
compressed.WriteByte(0);
compressed.Write(Data, y * rowStride, rowStride);
compressed.Write(Data, y * stride, stride);
}
compressed.Flush();

View File

@@ -297,7 +297,6 @@ namespace OpenRA
EngineVersion = "Unknown";
Console.WriteLine("Engine version is {0}", EngineVersion);
Console.WriteLine("Runtime: {0}", Platform.RuntimeVersion);
// Special case handling of Game.Mod argument: if it matches a real filesystem path
// then we use this to override the mod search path, and replace it with the mod id
@@ -330,16 +329,9 @@ namespace OpenRA
try
{
var rendererPath = Path.Combine(Platform.BinDir, "OpenRA.Platforms." + p + ".dll");
#if !MONO
var loader = new AssemblyLoader(rendererPath);
var platformType = loader.LoadDefaultAssembly().GetTypes().SingleOrDefault(t => typeof(IPlatform).IsAssignableFrom(t));
#else
var assembly = Assembly.LoadFile(rendererPath);
var platformType = assembly.GetTypes().SingleOrDefault(t => typeof(IPlatform).IsAssignableFrom(t));
#endif
var platformType = assembly.GetTypes().SingleOrDefault(t => typeof(IPlatform).IsAssignableFrom(t));
if (platformType == null)
throw new InvalidOperationException("Platform dll must include exactly one IPlatform implementation.");
@@ -484,8 +476,6 @@ namespace OpenRA
ChromeMetrics.TryGet("ChatMessageColor", out chatMessageColor);
ChromeMetrics.TryGet("SystemMessageColor", out systemMessageColor);
if (!ChromeMetrics.TryGet("SystemMessageLabel", out systemMessageLabel))
systemMessageLabel = "Battlefield Control";
ModData.LoadScreen.StartGame(args);
}
@@ -555,8 +545,6 @@ namespace OpenRA
static volatile ActionQueue delayedActions = new ActionQueue();
static Color systemMessageColor = Color.White;
static Color chatMessageColor = Color.White;
static string systemMessageLabel;
public static void RunAfterTick(Action a) { delayedActions.Add(a, RunTime); }
public static void RunAfterDelay(int delayMilliseconds, Action a) { delayedActions.Add(a, RunTime + delayMilliseconds); }
@@ -888,7 +876,7 @@ namespace OpenRA
public static void AddSystemLine(string text)
{
AddSystemLine(systemMessageLabel, text);
AddSystemLine("Battlefield Control", text);
}
public static void AddSystemLine(string name, string text)

View File

@@ -15,6 +15,7 @@ namespace OpenRA
{
public class GameSpeed
{
[Translate]
public readonly string Name = "Default";
public readonly int Timestep = 40;
public readonly int OrderLatency = 3;

View File

@@ -69,16 +69,9 @@ namespace OpenRA.Graphics
// Hotspot is specified relative to the center of the frame
var hotspot = f.Offset.ToInt2() - kv.Value.Hotspot - new int2(f.Size) / 2;
// Resolve indexed data to real colours
var data = f.Data;
var type = f.Type;
if (type == SpriteFrameType.Indexed8)
{
data = ConvertIndexedToBgra(kv.Key, f, palette);
type = SpriteFrameType.Bgra32;
}
c.Sprites[c.Length++] = sheetBuilder.Add(data, type, f.Size, 0, hotspot);
// SheetBuilder expects data in BGRA
var data = FrameToBGRA(kv.Key, f, palette);
c.Sprites[c.Length++] = sheetBuilder.Add(data, f.Size, 0, hotspot);
// Bounds relative to the hotspot
c.Bounds = Rectangle.Union(c.Bounds, new Rectangle(hotspot, f.Size));
@@ -224,27 +217,33 @@ namespace OpenRA.Graphics
Update();
}
public static byte[] ConvertIndexedToBgra(string name, ISpriteFrame frame, ImmutablePalette palette)
public static byte[] FrameToBGRA(string name, ISpriteFrame frame, ImmutablePalette palette)
{
if (frame.Type != SpriteFrameType.Indexed8)
throw new ArgumentException("ConvertIndexedToBgra requires input frames to be indexed.", nameof(frame));
// Data is already in BGRA format
if (frame.Type == SpriteFrameType.BGRA)
return frame.Data;
// Cursors may be either native BGRA or Indexed.
// Indexed sprites are converted to BGRA using the referenced palette.
// All palettes must be explicitly referenced, even if they are embedded in the sprite.
if (palette == null)
if (frame.Type == SpriteFrameType.Indexed && palette == null)
throw new InvalidOperationException("Cursor sequence `{0}` attempted to load an indexed sprite but does not define Palette".F(name));
var width = frame.Size.Width;
var height = frame.Size.Height;
var data = new byte[4 * width * height];
unsafe
for (var j = 0; j < height; j++)
{
// Cast the data to an int array so we can copy the src data directly
fixed (byte* bd = &data[0])
for (var i = 0; i < width; i++)
{
var rgba = (uint*)bd;
for (var j = 0; j < height; j++)
for (var i = 0; i < width; i++)
rgba[j * width + i] = palette[frame.Data[j * width + i]];
var rgba = palette[frame.Data[j * width + i]];
var k = 4 * (j * width + i);
// Convert RGBA to BGRA
data[k] = (byte)(rgba >> 16);
data[k + 1] = (byte)(rgba >> 8);
data[k + 2] = (byte)(rgba >> 0);
data[k + 3] = (byte)(rgba >> 24);
}
}

View File

@@ -45,7 +45,6 @@ namespace OpenRA.Graphics
public interface IModelCache : IDisposable
{
IModel GetModel(string model);
IModel GetModelSequence(string model, string sequence);
bool HasModelSequence(string model, string sequence);
IVertexBuffer<Vertex> VertexBuffer { get; }
@@ -67,11 +66,6 @@ namespace OpenRA.Graphics
public void Dispose() { }
public IModel GetModel(string model)
{
throw new NotImplementedException();
}
public IModel GetModelSequence(string model, string sequence)
{
throw new NotImplementedException();

View File

@@ -79,17 +79,21 @@ namespace OpenRA.Graphics
public Png AsPng()
{
if (Type == SheetType.Indexed)
throw new InvalidOperationException("AsPng() cannot be called on Indexed sheets.");
var data = GetData();
return new Png(GetData(), SpriteFrameType.Bgra32, Size.Width, Size.Height);
// Convert BGRA to RGBA
for (var i = 0; i < Size.Width * Size.Height; i++)
{
var temp = data[i * 4];
data[i * 4] = data[i * 4 + 2];
data[i * 4 + 2] = temp;
}
return new Png(data, Size.Width, Size.Height);
}
public Png AsPng(TextureChannel channel, IPalette pal)
{
if (Type != SheetType.Indexed)
throw new InvalidOperationException("AsPng(TextureChannel, IPalette) can only be called on Indexed sheets.");
var d = GetData();
var plane = new byte[Size.Width * Size.Height];
var dataStride = 4 * Size.Width;
@@ -103,7 +107,7 @@ namespace OpenRA.Graphics
for (var i = 0; i < Palette.Size; i++)
palColors[i] = pal.GetColor(i);
return new Png(plane, SpriteFrameType.Bgra32, Size.Width, Size.Height, palColors);
return new Png(plane, Size.Width, Size.Height, palColors);
}
public void CreateBuffer()

View File

@@ -52,15 +52,8 @@ namespace OpenRA.Graphics
{
switch (t)
{
case SpriteFrameType.Indexed8:
return SheetType.Indexed;
// Util.FastCopyIntoChannel will automatically convert these to BGRA
case SpriteFrameType.Bgra32:
case SpriteFrameType.Bgr24:
case SpriteFrameType.Rgba32:
case SpriteFrameType.Rgb24:
return SheetType.BGRA;
case SpriteFrameType.Indexed: return SheetType.Indexed;
case SpriteFrameType.BGRA: return SheetType.BGRA;
default: throw new NotImplementedException("Unknown SpriteFrameType {0}".F(t));
}
}
@@ -81,16 +74,16 @@ namespace OpenRA.Graphics
this.margin = margin;
}
public Sprite Add(ISpriteFrame frame) { return Add(frame.Data, frame.Type, frame.Size, 0, frame.Offset); }
public Sprite Add(byte[] src, SpriteFrameType type, Size size) { return Add(src, type, size, 0, float3.Zero); }
public Sprite Add(byte[] src, SpriteFrameType type, Size size, float zRamp, in float3 spriteOffset)
public Sprite Add(ISpriteFrame frame) { return Add(frame.Data, frame.Size, 0, frame.Offset); }
public Sprite Add(byte[] src, Size size) { return Add(src, size, 0, float3.Zero); }
public Sprite Add(byte[] src, Size size, float zRamp, in float3 spriteOffset)
{
// Don't bother allocating empty sprites
if (size.Width == 0 || size.Height == 0)
return new Sprite(current, Rectangle.Empty, 0, spriteOffset, channel, BlendMode.Alpha);
var rect = Allocate(size, zRamp, spriteOffset);
Util.FastCopyIntoChannel(rect, src, type);
Util.FastCopyIntoChannel(rect, src);
current.CommitBufferedData();
return rect;
}
@@ -103,6 +96,15 @@ namespace OpenRA.Graphics
return rect;
}
public Sprite Add(Size size, byte paletteIndex)
{
var data = new byte[size.Width * size.Height];
for (var i = 0; i < data.Length; i++)
data[i] = paletteIndex;
return Add(data, size);
}
TextureChannel? NextChannel(TextureChannel t)
{
var nextChannel = (int)t + (int)Type;

View File

@@ -18,29 +18,7 @@ using OpenRA.Primitives;
namespace OpenRA.Graphics
{
/// <summary>
/// Describes the format of the pixel data in a ISpriteFrame.
/// Note that the channel order is defined for little-endian bytes, so BGRA corresponds
/// to a 32bit ARGB value, such as that returned by Color.ToArgb()!
/// </summary>
public enum SpriteFrameType
{
// 8 bit index into an external palette
Indexed8,
// 32 bit color such as returned by Color.ToArgb() or the bmp file format
// (remember that little-endian systems place the little bits in the first byte!)
Bgra32,
// Like BGRA, but without an alpha channel
Bgr24,
// 32 bit color in big-endian format, like png
Rgba32,
// Like RGBA, but without an alpha channel
Rgb24
}
public enum SpriteFrameType { Indexed, BGRA }
public interface ISpriteLoader
{
@@ -69,7 +47,7 @@ namespace OpenRA.Graphics
public class SpriteCache
{
public readonly Cache<SheetType, SheetBuilder> SheetBuilders;
public readonly Cache<SpriteFrameType, SheetBuilder> SheetBuilders;
readonly ISpriteLoader[] loaders;
readonly IReadOnlyFileSystem fileSystem;
@@ -79,7 +57,7 @@ namespace OpenRA.Graphics
public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders)
{
SheetBuilders = new Cache<SheetType, SheetBuilder>(t => new SheetBuilder(t));
SheetBuilders = new Cache<SpriteFrameType, SheetBuilder>(t => new SheetBuilder(SheetBuilder.FrameTypeToSheetType(t)));
this.fileSystem = fileSystem;
this.loaders = loaders;
@@ -125,7 +103,7 @@ namespace OpenRA.Graphics
{
if (unloaded[i] != null)
{
sprite[i] = SheetBuilders[SheetBuilder.FrameTypeToSheetType(unloaded[i].Type)].Add(unloaded[i]);
sprite[i] = SheetBuilders[unloaded[i].Type].Add(unloaded[i]);
unloaded[i] = null;
}
}

View File

@@ -107,13 +107,12 @@ namespace OpenRA.Graphics
throw new YamlException("Sprite type mismatch. Terrain sprites must all be either Indexed or RGBA.");
var s = sheetBuilder.Allocate(f.Size, zRamp, offset);
Util.FastCopyIntoChannel(s, f.Data, f.Type);
Util.FastCopyIntoChannel(s, f.Data);
if (tileset.EnableDepth)
{
var ss = sheetBuilder.Allocate(f.Size, zRamp, offset);
var depthFrame = allFrames[j + frameCount];
Util.FastCopyIntoChannel(ss, depthFrame.Data, depthFrame.Type);
Util.FastCopyIntoChannel(ss, allFrames[j + frameCount].Data);
// s and ss are guaranteed to use the same sheet
// because of the custom terrain sheet allocation
@@ -137,10 +136,7 @@ namespace OpenRA.Graphics
}
// 1x1px transparent tile
if (sheetBuilder.Type == SheetType.BGRA)
missingTile = sheetBuilder.Add(new byte[4], SpriteFrameType.Bgra32, new Size(1, 1));
else
missingTile = sheetBuilder.Add(new byte[1], SpriteFrameType.Indexed8, new Size(1, 1));
missingTile = sheetBuilder.Add(new byte[sheetBuilder.Type == SheetType.BGRA ? 4 : 1], new Size(1, 1));
Sheet.ReleaseBuffer();
}
@@ -172,7 +168,10 @@ namespace OpenRA.Graphics
for (var x = 0; x < template.Size.X; x++)
{
var tile = new TerrainTile(template.Id, (byte)(i++));
if (!tileset.TryGetTileInfo(tile, out var tileInfo))
var tileInfo = tileset.GetTileInfo(tile);
// Empty tile
if (tileInfo == null)
continue;
var sprite = TileSprite(tile);

View File

@@ -62,7 +62,7 @@ namespace OpenRA.Graphics
vertices[nv + 5] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC, tint);
}
public static void FastCopyIntoChannel(Sprite dest, byte[] src, SpriteFrameType srcType)
public static void FastCopyIntoChannel(Sprite dest, byte[] src)
{
var destData = dest.Sheet.GetData();
var width = dest.Bounds.Width;
@@ -85,34 +85,12 @@ namespace OpenRA.Graphics
{
for (var i = 0; i < width; i++)
{
byte r, g, b, a;
switch (srcType)
{
case SpriteFrameType.Bgra32:
case SpriteFrameType.Bgr24:
{
b = src[k++];
g = src[k++];
r = src[k++];
a = srcType == SpriteFrameType.Bgra32 ? src[k++] : (byte)255;
break;
}
case SpriteFrameType.Rgba32:
case SpriteFrameType.Rgb24:
{
r = src[k++];
g = src[k++];
b = src[k++];
a = srcType == SpriteFrameType.Rgba32 ? src[k++] : (byte)255;
break;
}
default:
throw new InvalidOperationException("Unknown SpriteFrameType {0}".F(srcType));
}
var r = src[k++];
var g = src[k++];
var b = src[k++];
var a = src[k++];
var cc = Color.FromArgb(a, r, g, b);
data[(y + j) * destStride + x + i] = PremultiplyAlpha(cc).ToArgb();
}
}
@@ -161,29 +139,16 @@ namespace OpenRA.Graphics
for (var i = 0; i < width; i++)
{
Color cc;
switch (src.Type)
if (src.Palette == null)
{
case SpriteFrameType.Indexed8:
{
cc = src.Palette[src.Data[k++]];
break;
}
case SpriteFrameType.Rgba32:
case SpriteFrameType.Rgb24:
{
var r = src.Data[k++];
var g = src.Data[k++];
var b = src.Data[k++];
var a = src.Type == SpriteFrameType.Rgba32 ? src.Data[k++] : (byte)255;
cc = Color.FromArgb(a, r, g, b);
break;
}
// Pngs don't support BGR[A], so no need to include them here
default:
throw new InvalidOperationException("Unknown SpriteFrameType {0}".F(src.Type));
var r = src.Data[k++];
var g = src.Data[k++];
var b = src.Data[k++];
var a = src.Data[k++];
cc = Color.FromArgb(a, r, g, b);
}
else
cc = src.Palette[src.Data[k++]];
data[(y + j) * destStride + x + i] = PremultiplyAlpha(cc).ToArgb();
}

View File

@@ -60,7 +60,7 @@ namespace OpenRA
public readonly string[]
Rules, ServerTraits,
Sequences, ModelSequences, Cursors, Chrome, Assemblies, ChromeLayout,
Weapons, Voices, Notifications, Music, TileSets,
Weapons, Voices, Notifications, Music, Translations, TileSets,
ChromeMetrics, MapCompatibility, Missions, Hotkeys;
public readonly IReadOnlyDictionary<string, string> Packages;
@@ -128,6 +128,7 @@ namespace OpenRA
Voices = YamlList(yaml, "Voices");
Notifications = YamlList(yaml, "Notifications");
Music = YamlList(yaml, "Music");
Translations = YamlList(yaml, "Translations");
TileSets = YamlList(yaml, "TileSets");
ChromeMetrics = YamlList(yaml, "ChromeMetrics");
Missions = YamlList(yaml, "Missions");

View File

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

View File

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

View File

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

View File

@@ -138,6 +138,42 @@ namespace OpenRA
public IEnumerable<string> Languages { get; private set; }
void LoadTranslations(Map map)
{
var selectedTranslations = new Dictionary<string, string>();
var defaultTranslations = new Dictionary<string, string>();
if (!Manifest.Translations.Any())
{
Languages = new string[0];
return;
}
var yaml = MiniYaml.Load(map, Manifest.Translations, map.TranslationDefinitions);
Languages = yaml.Select(t => t.Key).ToArray();
foreach (var y in yaml)
{
if (y.Key == Game.Settings.Graphics.Language)
selectedTranslations = y.Value.ToDictionary(my => my.Value ?? "");
else if (y.Key == Game.Settings.Graphics.DefaultLanguage)
defaultTranslations = y.Value.ToDictionary(my => my.Value ?? "");
}
var translations = new Dictionary<string, string>();
foreach (var tkv in defaultTranslations.Concat(selectedTranslations))
{
if (translations.ContainsKey(tkv.Key))
continue;
if (selectedTranslations.ContainsKey(tkv.Key))
translations.Add(tkv.Key, selectedTranslations[tkv.Key]);
else
translations.Add(tkv.Key, tkv.Value);
}
FieldLoader.SetTranslations(translations);
}
public Map PrepareMap(string uid)
{
LoadScreen?.Display();
@@ -149,6 +185,8 @@ namespace OpenRA
using (new Support.PerfTimer("Map"))
map = new Map(this, MapCache[uid].Package);
LoadTranslations(map);
// Reinitialize all our assets
InitializeLoaders(map);

View File

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

View File

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

View File

@@ -80,7 +80,6 @@ namespace OpenRA
{
get
{
// Players in mission maps must not leave the player view
return !inMissionMap && (spectating || WinState != WinState.Undefined);
}
}

View File

@@ -431,15 +431,24 @@ namespace OpenRA
var srcWidth = screenSprite.Sheet.Size.Width;
var destWidth = screenSprite.Bounds.Width;
var destHeight = -screenSprite.Bounds.Height;
var channelOrder = new[] { 2, 1, 0, 3 };
ThreadPool.QueueUserWorkItem(_ =>
{
// Extract the screen rect from the (larger) backing surface
// Convert BGRA to RGBA
var dest = new byte[4 * destWidth * destHeight];
for (var y = 0; y < destHeight; y++)
Array.Copy(src, 4 * y * srcWidth, dest, 4 * y * destWidth, 4 * destWidth);
{
for (var x = 0; x < destWidth; x++)
{
var destOffset = 4 * (y * destWidth + x);
var srcOffset = 4 * (y * srcWidth + x);
for (var i = 0; i < 4; i++)
dest[destOffset + i] = src[srcOffset + channelOrder[i]];
}
}
new Png(dest, SpriteFrameType.Bgra32, destWidth, destHeight).Save(path);
new Png(dest, destWidth, destHeight).Save(path);
});
}

View File

@@ -660,28 +660,16 @@ namespace OpenRA.Server
}
}
byte[] CreateFrame(int client, int frame, byte[] data)
void DispatchOrdersToClient(Connection c, int client, int frame, byte[] data)
{
using (var ms = new MemoryStream(data.Length + 12))
try
{
var ms = new MemoryStream(data.Length + 12);
ms.WriteArray(BitConverter.GetBytes(data.Length + 4));
ms.WriteArray(BitConverter.GetBytes(client));
ms.WriteArray(BitConverter.GetBytes(frame));
ms.WriteArray(data);
return ms.GetBuffer();
}
}
void DispatchOrdersToClient(Connection c, int client, int frame, byte[] data)
{
DispatchFrameToClient(c, client, CreateFrame(client, frame, data));
}
void DispatchFrameToClient(Connection c, int client, byte[] frameData)
{
try
{
SendData(c.Socket, frameData);
SendData(c.Socket, ms.ToArray());
}
catch (Exception e)
{
@@ -789,9 +777,8 @@ namespace OpenRA.Server
public void DispatchOrdersToClients(Connection conn, int frame, byte[] data)
{
var from = conn != null ? conn.PlayerIndex : 0;
var frameData = CreateFrame(from, frame, data);
foreach (var c in Conns.Except(conn).ToList())
DispatchFrameToClient(c, from, frameData);
DispatchOrdersToClient(c, from, frame, data);
if (recorder != null)
{

View File

@@ -190,6 +190,9 @@ namespace OpenRA
public int BatchSize = 8192;
public int SheetSize = 2048;
public string Language = "english";
public string DefaultLanguage = "english";
}
public class SoundSettings

View File

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

View File

@@ -17,9 +17,11 @@ namespace OpenRA.Traits
[Desc("Required for shroud and fog visibility checks. Add this to the player actor.")]
public class ShroudInfo : TraitInfo, ILobbyOptions
{
[Translate]
[Desc("Descriptive label for the fog checkbox in the lobby.")]
public readonly string FogCheckboxLabel = "Fog of War";
[Translate]
[Desc("Tooltip description for the fog checkbox in the lobby.")]
public readonly string FogCheckboxDescription = "Line of sight is required to view enemy forces";
@@ -35,9 +37,11 @@ namespace OpenRA.Traits
[Desc("Display order for the fog checkbox in the lobby.")]
public readonly int FogCheckboxDisplayOrder = 0;
[Translate]
[Desc("Descriptive label for the explored map checkbox in the lobby.")]
public readonly string ExploredMapCheckboxLabel = "Explored Map";
[Translate]
[Desc("Tooltip description for the explored map checkbox in the lobby.")]
public readonly string ExploredMapCheckboxDescription = "Initial map shroud is revealed";

View File

@@ -28,6 +28,7 @@ namespace OpenRA.Traits
[Desc("The side that the faction belongs to. For example, England belongs to the 'Allies' side.")]
public readonly string Side = null;
[Translate]
public readonly string Description = null;
public readonly bool Selectable = true;

View File

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

View File

@@ -20,11 +20,15 @@ namespace OpenRA.Mods.Cnc.AudioLoaders
bool IsAud(Stream s)
{
var start = s.Position;
s.Position += 11;
s.Position += 10;
var readFlag = s.ReadByte();
var readFormat = s.ReadByte();
s.Position = start;
return readFormat == (int)SoundFormat.ImaAdpcm;
if (!Enum.IsDefined(typeof(SoundFlags), readFlag))
return false;
return Enum.IsDefined(typeof(SoundFormat), readFormat);
}
bool ISoundLoader.TryParseSound(Stream stream, out ISoundFormat sound)
@@ -49,8 +53,8 @@ namespace OpenRA.Mods.Cnc.AudioLoaders
public sealed class AudFormat : ISoundFormat
{
public int Channels { get { return channels; } }
public int SampleBits { get { return sampleBits; } }
public int Channels { get { return 1; } }
public int SampleBits { get { return 16; } }
public int SampleRate { get { return sampleRate; } }
public float LengthInSeconds { get { return AudReader.SoundLength(sourceStream); } }
public Stream GetPCMInputStream() { return audStreamFactory(); }
@@ -58,15 +62,13 @@ namespace OpenRA.Mods.Cnc.AudioLoaders
readonly Stream sourceStream;
readonly Func<Stream> audStreamFactory;
readonly int channels;
readonly int sampleBits;
readonly int sampleRate;
public AudFormat(Stream stream)
{
sourceStream = stream;
if (!AudReader.LoadSound(stream, out audStreamFactory, out sampleRate, out sampleBits, out channels))
if (!AudReader.LoadSound(stream, out audStreamFactory, out sampleRate))
throw new InvalidDataException();
}
}

View File

@@ -32,6 +32,11 @@ namespace OpenRA.Mods.Cnc.FileFormats
public static class AudReader
{
public static byte[] LoadSound(byte[] raw, ref int index)
{
return ImaAdpcmReader.LoadImaAdpcmSound(raw, ref index);
}
public static float SoundLength(Stream s)
{
var sampleRate = s.ReadUInt16();
@@ -49,7 +54,7 @@ namespace OpenRA.Mods.Cnc.FileFormats
return (float)samples / sampleRate;
}
public static bool LoadSound(Stream s, out Func<Stream> result, out int sampleRate, out int sampleBits, out int channels)
public static bool LoadSound(Stream s, out Func<Stream> result, out int sampleRate)
{
result = null;
var startPosition = s.Position;
@@ -58,17 +63,15 @@ namespace OpenRA.Mods.Cnc.FileFormats
sampleRate = s.ReadUInt16();
var dataSize = s.ReadInt32();
var outputSize = s.ReadInt32();
var readFlag = s.ReadByte();
sampleBits = (readFlag & (int)SoundFlags._16Bit) == 0 ? 8 : 16;
channels = (readFlag & (int)SoundFlags.Stereo) == 0 ? 1 : 2;
if (!Enum.IsDefined(typeof(SoundFlags), readFlag))
return false;
var readFormat = s.ReadByte();
if (!Enum.IsDefined(typeof(SoundFormat), readFormat))
return false;
if (readFormat == (int)SoundFormat.WestwoodCompressed)
throw new NotImplementedException();
var offsetPosition = s.Position;
result = () =>

View File

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

View File

@@ -35,10 +35,10 @@ namespace OpenRA.Mods.Cnc.Graphics
uint IModel.Frames { get { return frames; } }
uint IModel.Sections { get { return limbs; } }
public Voxel(VoxelLoader loader, VxlReader vxl, HvaReader hva, (string Vxl, string Hva) files)
public Voxel(VoxelLoader loader, VxlReader vxl, HvaReader hva)
{
if (vxl.LimbCount != hva.LimbCount)
throw new InvalidOperationException("{0}.vxl and {1}.hva limb counts don't match.".F(files.Vxl, files.Hva));
throw new InvalidOperationException("Voxel and hva limb counts don't match");
transforms = hva.Transforms;
frames = hva.FrameCount;

View File

@@ -77,8 +77,8 @@ namespace OpenRA.Mods.Cnc.Graphics
var size = new Size(su, sv);
var s = sheetBuilder.Allocate(size);
var t = sheetBuilder.Allocate(size);
OpenRA.Graphics.Util.FastCopyIntoChannel(s, colors, SpriteFrameType.Indexed8);
OpenRA.Graphics.Util.FastCopyIntoChannel(t, normals, SpriteFrameType.Indexed8);
OpenRA.Graphics.Util.FastCopyIntoChannel(s, colors);
OpenRA.Graphics.Util.FastCopyIntoChannel(t, normals);
// s and t are guaranteed to use the same sheet because
// of the custom voxel sheet allocation implementation
@@ -216,10 +216,9 @@ namespace OpenRA.Mods.Cnc.Graphics
HvaReader hva;
using (var s = fileSystem.Open(files.Vxl + ".vxl"))
vxl = new VxlReader(s);
using (var s = fileSystem.Open(files.Hva + ".hva"))
hva = new HvaReader(s, files.Hva + ".hva");
return new Voxel(this, vxl, hva, files);
return new Voxel(this, vxl, hva);
}
public Voxel Load(string vxl, string hva)

View File

@@ -86,11 +86,6 @@ namespace OpenRA.Mods.Cnc.Graphics
return loader.Load(vxl, hva);
}
public IModel GetModel(string model)
{
return loader.Load(model, model);
}
public IModel GetModelSequence(string model, string sequence)
{
try { return models[model][sequence]; }

View File

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

View File

@@ -30,7 +30,7 @@ namespace OpenRA.Mods.Cnc.SpriteLoaders
class ShpD2Frame : ISpriteFrame
{
public SpriteFrameType Type { get { return SpriteFrameType.Indexed8; } }
public SpriteFrameType Type { get { return SpriteFrameType.Indexed; } }
public Size Size { get; private set; }
public Size FrameSize { get { return Size; } }
public float2 Offset { get { return float2.Zero; } }

View File

@@ -77,7 +77,7 @@ namespace OpenRA.Mods.Cnc.SpriteLoaders
class ImageHeader : ISpriteFrame
{
public SpriteFrameType Type { get { return SpriteFrameType.Indexed8; } }
public SpriteFrameType Type { get { return SpriteFrameType.Indexed; } }
public Size Size { get { return reader.Size; } }
public Size FrameSize { get { return reader.Size; } }
public float2 Offset { get { return float2.Zero; } }

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Cnc.SpriteLoaders
{
class TmpRAFrame : ISpriteFrame
{
public SpriteFrameType Type { get { return SpriteFrameType.Indexed8; } }
public SpriteFrameType Type { get { return SpriteFrameType.Indexed; } }
public Size Size { get; private set; }
public Size FrameSize { get; private set; }
public float2 Offset { get { return float2.Zero; } }

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Cnc.SpriteLoaders
{
class TmpTDFrame : ISpriteFrame
{
public SpriteFrameType Type { get { return SpriteFrameType.Indexed8; } }
public SpriteFrameType Type { get { return SpriteFrameType.Indexed; } }
public Size Size { get; private set; }
public Size FrameSize { get; private set; }
public float2 Offset { get { return float2.Zero; } }

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.Cnc.SpriteLoaders
{
readonly TmpTSFrame parent;
public SpriteFrameType Type { get { return SpriteFrameType.Indexed8; } }
public SpriteFrameType Type { get { return SpriteFrameType.Indexed; } }
public Size Size { get { return parent.Size; } }
public Size FrameSize { get { return Size; } }
public float2 Offset { get { return parent.Offset; } }
@@ -36,7 +36,7 @@ namespace OpenRA.Mods.Cnc.SpriteLoaders
class TmpTSFrame : ISpriteFrame
{
public SpriteFrameType Type { get { return SpriteFrameType.Indexed8; } }
public SpriteFrameType Type { get { return SpriteFrameType.Indexed; } }
public Size Size { get; private set; }
public Size FrameSize { get { return Size; } }
public float2 Offset { get; private set; }

View File

@@ -26,9 +26,11 @@ namespace OpenRA.Mods.Cnc.Traits
[Desc("The prerequisite type that this provides.")]
public readonly string Prerequisite = null;
[Translate]
[Desc("Label to display over the support power icon and in its tooltip while the power is active.")]
public readonly string ActiveText = "ACTIVE";
[Translate]
[Desc("Label to display over the support power icon and in its tooltip while the power is available but not active.")]
public readonly string AvailableText = "READY";

View File

@@ -14,7 +14,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.Cnc.SpriteLoaders;
using OpenRA.Primitives;
@@ -36,7 +35,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
var dest = inputFiles[0].Split('-').First() + ".shp";
var frames = inputFiles.Select(a => new Png(File.OpenRead(a))).ToList();
if (frames.Any(f => f.Type != SpriteFrameType.Indexed8))
if (frames.Any(f => f.Palette == null))
throw new InvalidOperationException("All frames must be paletted");
var size = new Size(frames[0].Width, frames[0].Height);

View File

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

View File

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

View File

@@ -62,7 +62,7 @@ namespace OpenRA.Mods.Common.Lint
if (!string.IsNullOrWhiteSpace(player.Faction) && !factions.Contains(player.Faction))
emitError("Invalid faction {0} chosen for player {1}.".F(player.Faction, player.Name));
if (worldActor.HasTraitInfo<MapStartingLocationsInfo>())
if (worldActor.HasTraitInfo<MPStartLocationsInfo>())
{
var playerCount = players.Count(p => p.Value.Playable);
var spawns = new List<CPos>();

View File

@@ -24,8 +24,6 @@ namespace OpenRA.Mods.Common.LoadScreens
public LaunchArguments Launch;
protected ModData ModData { get; private set; }
bool initialized;
public virtual void Init(ModData modData, Dictionary<string, string> info)
{
ModData = modData;
@@ -33,15 +31,12 @@ namespace OpenRA.Mods.Common.LoadScreens
public virtual void Display()
{
if (Game.Renderer == null || initialized)
if (Game.Renderer == null)
return;
// Draw a black screen
Game.Renderer.BeginUI();
Game.Renderer.EndFrame(new NullInputHandler());
// PERF: draw the screen only once
initialized = true;
}
public virtual void StartGame(Arguments args)

View File

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

View File

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

View File

@@ -61,22 +61,24 @@ namespace OpenRA.Mods.Common.SpriteLoaders
RegionsFromSlices(png, out frameRegions, out frameOffsets);
frames = new ISpriteFrame[frameRegions.Count];
var stride = png.PixelStride;
for (var i = 0; i < frames.Length; i++)
{
var frameStart = frameRegions[i].X + frameRegions[i].Y * png.Width;
var frameSize = new Size(frameRegions[i].Width, frameRegions[i].Height);
var pixelLength = png.Palette == null ? 4 : 1;
frames[i] = new PngSheetFrame()
{
Size = frameSize,
FrameSize = frameSize,
Offset = frameOffsets[i],
Data = new byte[frameRegions[i].Width * frameRegions[i].Height * stride],
Type = png.Type
Data = new byte[frameRegions[i].Width * frameRegions[i].Height * pixelLength],
Type = png.Palette == null ? SpriteFrameType.BGRA : SpriteFrameType.Indexed
};
for (var y = 0; y < frames[i].Size.Height; y++)
Array.Copy(png.Data, (frameStart + y * png.Width) * stride, frames[i].Data, y * frames[i].Size.Width * stride, frames[i].Size.Width * stride);
Array.Copy(png.Data, (frameStart + y * png.Width) * pixelLength, frames[i].Data, y * frames[i].Size.Width * pixelLength, frames[i].Size.Width * pixelLength);
}
metadata = new TypeDictionary

View File

@@ -20,7 +20,7 @@ namespace OpenRA.Mods.Common.SpriteLoaders
{
class ShpTSFrame : ISpriteFrame
{
public SpriteFrameType Type { get { return SpriteFrameType.Indexed8; } }
public SpriteFrameType Type { get { return SpriteFrameType.Indexed; } }
public Size Size { get; private set; }
public Size FrameSize { get; private set; }
public float2 Offset { get; private set; }

View File

@@ -54,6 +54,7 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Sort order for the production palette. Smaller numbers are presented earlier.")]
public readonly int BuildPaletteOrder = 9999;
[Translate]
[Desc("Text shown in the production tooltip.")]
public readonly string Description = "";

View File

@@ -30,7 +30,7 @@ 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.")]
[Desc("The terrain types that this actor should avoid running on to while panicing.")]
public readonly HashSet<string> AvoidTerrainTypes = new HashSet<string>();
[SequenceReference(prefix: true)]

View File

@@ -21,6 +21,7 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Delay for the end game notification in milliseconds.")]
public readonly int NotificationDelay = 1500;
[Translate]
[Desc("Description of the objective.")]
public readonly string Objective = "Destroy all opposition!";

View File

@@ -19,9 +19,11 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Attach this to the player actor.")]
public class DeveloperModeInfo : TraitInfo, ILobbyOptions
{
[Translate]
[Desc("Descriptive label for the developer mode checkbox in the lobby.")]
public readonly string CheckboxLabel = "Debug Menu";
[Translate]
[Desc("Tooltip description for the developer mode checkbox in the lobby.")]
public readonly string CheckboxDescription = "Enables cheats and developer commands";

View File

@@ -12,7 +12,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
@@ -59,19 +58,15 @@ namespace OpenRA.Mods.Common.Traits
[NotificationReference("Speech")]
public readonly string LeaveNotification = null;
public override object Create(ActorInitializer init) { return new MissionObjectives(init.Self.Owner, this); }
public override object Create(ActorInitializer init) { return new MissionObjectives(init.World, this); }
}
public class MissionObjectives : INotifyWinStateChanged, ISync, IResolveOrder, IWorldLoaded
public class MissionObjectives : INotifyWinStateChanged, ISync, IResolveOrder
{
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
{
@@ -88,21 +83,12 @@ 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(Player player, MissionObjectivesInfo info)
public MissionObjectives(World world, 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;
@@ -154,9 +140,10 @@ namespace OpenRA.Mods.Common.Traits
void CheckIfGameIsOver(Player player)
{
var gameOver = player.World.Players.All(p => p.NonCombatant || p.WinState != WinState.Undefined || !p.HasObjectives);
var players = player.World.Players.Where(p => !p.NonCombatant);
var gameOver = players.All(p => p.WinState != WinState.Undefined || !p.HasObjectives);
if (gameOver)
{
Game.RunAfterDelay(Info.GameOverDelay, () =>
{
if (!Game.IsCurrentWorld(player.World))
@@ -166,14 +153,17 @@ 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))
{
@@ -203,9 +193,13 @@ 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

@@ -18,9 +18,11 @@ namespace OpenRA.Mods.Common.Traits
{
public class PlayerResourcesInfo : TraitInfo, ILobbyOptions
{
[Translate]
[Desc("Descriptive label for the starting cash option in the lobby.")]
public readonly string DefaultCashDropdownLabel = "Starting Cash";
[Translate]
[Desc("Tooltip description for the starting cash option in the lobby.")]
public readonly string DefaultCashDropdownDescription = "Change the amount of cash that players start with";

View File

@@ -19,6 +19,7 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Internal id for this tech level.")]
public readonly string Id;
[Translate]
[Desc("Name shown in the lobby options.")]
public readonly string Name;

View File

@@ -36,6 +36,7 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Delay for the end game notification in milliseconds.")]
public readonly int NotificationDelay = 1500;
[Translate]
[Desc("Description of the objective")]
public readonly string Objective = "Hold all the strategic positions!";

View File

@@ -21,6 +21,7 @@ namespace OpenRA.Mods.Common.Traits.Render
[Desc("Displays a text overlay relative to the selection box.")]
public class WithTextDecorationInfo : WithDecorationBaseInfo
{
[Translate]
[FieldLoader.Require]
public readonly string Text = null;

View File

@@ -15,6 +15,7 @@ namespace OpenRA.Mods.Common.Traits
{
public abstract class TooltipInfoBase : ConditionalTraitInfo, Requires<IMouseBoundsInfo>
{
[Translate]
public readonly string Name = "";
}
@@ -27,6 +28,7 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Shown in the build palette widget.")]
public class TooltipInfo : TooltipInfoBase, ITooltipInfo
{
[Translate]
[Desc("An optional generic name (i.e. \"Soldier\" or \"Structure\")" +
"to be shown to chosen players.")]
public readonly string GenericName = null;
@@ -34,12 +36,15 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Prefix generic tooltip name with 'Ally/Neutral/EnemyPrefix'.")]
public readonly bool GenericStancePrefix = true;
[Translate]
[Desc("Prefix to display in the tooltip for allied units.")]
public readonly string AllyPrefix = "Allied";
[Translate]
[Desc("Prefix to display in the tooltip for neutral units.")]
public readonly string NeutralPrefix = null;
[Translate]
[Desc("Prefix to display in the tooltip for enemy units.")]
public readonly string EnemyPrefix = "Enemy";

View File

@@ -17,6 +17,7 @@ namespace OpenRA.Mods.Common.Traits
public class TooltipDescriptionInfo : ConditionalTraitInfo
{
[Desc("Text shown in tooltip.")]
[Translate]
public readonly string Description = "";
[Desc("Player relationships who can view the description.")]

View File

@@ -100,8 +100,7 @@ namespace OpenRA.Mods.Common.Traits
if (++ticksIdle % info.ReduceMoveRadiusDelay == 0)
effectiveMoveRadius--;
// 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;
return null; // We'll be back the next tick; better to sit idle for a few seconds than prolong this tick indefinitely with a loop
}
if (info.AvoidTerrainTypes.Count > 0)

View File

@@ -11,7 +11,6 @@
using System.Collections.Generic;
using System.Linq;
using OpenRA.Mods.Common;
using OpenRA.Primitives;
using OpenRA.Traits;
@@ -26,8 +25,8 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Maximum number of actors.")]
public readonly int Maximum = 4;
[Desc("Time (in ticks) between actor spawn. Supports 1 or 2 values.\nIf 2 values are provided they are used as a range from which a value is randomly selected.")]
public readonly int[] SpawnInterval = { 6000 };
[Desc("Time (in ticks) between actor spawn.")]
public readonly int SpawnInterval = 6000;
[FieldLoader.Require]
[ActorReference]
@@ -39,20 +38,6 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Type of ActorSpawner with which it connects.")]
public readonly HashSet<string> Types = new HashSet<string>() { };
public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
{
base.RulesetLoaded(rules, ai);
if (SpawnInterval.Length == 0 || SpawnInterval.Length > 2)
throw new YamlException("{0}.{1} must be either 1 or 2 values".F(nameof(ActorSpawnManager), nameof(SpawnInterval)));
if (SpawnInterval.Length == 2 && SpawnInterval[0] >= SpawnInterval[1])
throw new YamlException("{0}.{1}'s first value must be less than the second value".F(nameof(ActorSpawnManager), nameof(SpawnInterval)));
if (SpawnInterval.Any(it => it < 0))
throw new YamlException("{0}.{1}'s value(s) must not be less than 0".F(nameof(ActorSpawnManager), nameof(SpawnInterval)));
}
public override object Create(ActorInitializer init) { return new ActorSpawnManager(init.Self, this); }
}
@@ -92,7 +77,7 @@ namespace OpenRA.Mods.Common.Traits
if (spawnPoint == null)
return;
spawnCountdown = Util.RandomDelay(self.World, info.SpawnInterval);
spawnCountdown = info.SpawnInterval;
do
{

View File

@@ -20,9 +20,11 @@ namespace OpenRA.Mods.Common.Traits
{
public class CrateSpawnerInfo : TraitInfo, ILobbyOptions
{
[Translate]
[Desc("Descriptive label for the crates checkbox in the lobby.")]
public readonly string CheckboxLabel = "Crates";
[Translate]
[Desc("Tooltip description for the crates checkbox in the lobby.")]
public readonly string CheckboxDescription = "Collect crates with units to receive random bonuses or penalties";

View File

@@ -19,7 +19,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Attach this to the world actor.")]
public class CreateMapPlayersInfo : TraitInfo<CreateMapPlayers>, ICreatePlayersInfo
public class CreateMPPlayersInfo : TraitInfo<CreateMPPlayers>, ICreatePlayersInfo
{
/// <summary>
/// Returns a list of GameInformation.Players that matches the indexing of ICreatePlayers.CreatePlayers.
@@ -80,7 +80,7 @@ namespace OpenRA.Mods.Common.Traits
}
}
public class CreateMapPlayers : ICreatePlayers
public class CreateMPPlayers : ICreatePlayers
{
void ICreatePlayers.CreatePlayers(World w, MersenneTwister playerRandom)
{

View File

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

View File

@@ -20,13 +20,15 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Allows the map to have working spawnpoints. Also controls the 'Separate Team Spawns' checkbox in the lobby options.")]
public class MapStartingLocationsInfo : TraitInfo, ILobbyOptions, IAssignSpawnPointsInfo
public class MPStartLocationsInfo : TraitInfo, ILobbyOptions, IAssignSpawnPointsInfo
{
public readonly WDist InitialExploreRange = WDist.FromCells(5);
[Translate]
[Desc("Descriptive label for the spawn positions checkbox in the lobby.")]
public readonly string SeparateTeamSpawnsCheckboxLabel = "Separate Team Spawns";
[Translate]
[Desc("Tooltip description for the spawn positions checkbox in the lobby.")]
public readonly string SeparateTeamSpawnsCheckboxDescription = "Players without assigned spawn points will start as far as possible from enemy players";
@@ -42,7 +44,7 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Display order for the spawn positions checkbox in the lobby.")]
public readonly int SeparateTeamSpawnsCheckboxDisplayOrder = 0;
public override object Create(ActorInitializer init) { return new MapStartingLocations(this); }
public override object Create(ActorInitializer init) { return new MPStartLocations(this); }
IEnumerable<LobbyOption> ILobbyOptions.LobbyOptions(Ruleset rules)
{
@@ -103,15 +105,15 @@ namespace OpenRA.Mods.Common.Traits
}
}
public class MapStartingLocations : IWorldLoaded, INotifyCreated, IAssignSpawnPoints
public class MPStartLocations : IWorldLoaded, INotifyCreated, IAssignSpawnPoints
{
readonly MapStartingLocationsInfo info;
readonly MPStartLocationsInfo info;
readonly Dictionary<int, Session.Client> occupiedSpawnPoints = new Dictionary<int, Session.Client>();
bool separateTeamSpawns;
CPos[] spawnLocations;
List<int> availableSpawnPoints;
public MapStartingLocations(MapStartingLocationsInfo info)
public MPStartLocations(MPStartLocationsInfo info)
{
this.info = info;
}

View File

@@ -14,8 +14,8 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Used by SpawnStartingUnits. Attach these to the world actor. You can have multiple variants by adding @suffixes.")]
public class StartingUnitsInfo : TraitInfo<StartingUnits>
[Desc("Used by SpawnMPUnits. Attach these to the world actor. You can have multiple variants by adding @suffixes.")]
public class MPStartUnitsInfo : TraitInfo<MPStartUnits>
{
[Desc("Internal class ID.")]
public readonly string Class = "none";
@@ -50,5 +50,5 @@ namespace OpenRA.Mods.Common.Traits
public readonly WAngle? SupportActorsFacing = null;
}
public class StartingUnits { }
public class MPStartUnits { }
}

View File

@@ -17,9 +17,11 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Controls the build radius checkboxes in the lobby options.")]
public class MapBuildRadiusInfo : TraitInfo, ILobbyOptions
{
[Translate]
[Desc("Descriptive label for the ally build radius checkbox in the lobby.")]
public readonly string AllyBuildRadiusCheckboxLabel = "Build off Allies";
[Translate]
[Desc("Tooltip description for the ally build radius checkbox in the lobby.")]
public readonly string AllyBuildRadiusCheckboxDescription = "Allow allies to place structures inside your build area";
@@ -35,9 +37,11 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Display order for the ally build radius checkbox in the lobby.")]
public readonly int AllyBuildRadiusCheckboxDisplayOrder = 0;
[Translate]
[Desc("Tooltip description for the build radius checkbox in the lobby.")]
public readonly string BuildRadiusCheckboxLabel = "Limit Build Area";
[Translate]
[Desc("Tooltip description for the build radius checkbox in the lobby.")]
public readonly string BuildRadiusCheckboxDescription = "Limits structure placement to areas around Construction Yards";

View File

@@ -17,9 +17,11 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Controls the 'Creeps' checkbox in the lobby options.")]
public class MapCreepsInfo : TraitInfo, ILobbyOptions
{
[Translate]
[Desc("Descriptive label for the creeps checkbox in the lobby.")]
public readonly string CheckboxLabel = "Creep Actors";
[Translate]
[Desc("Tooltip description for the creeps checkbox in the lobby.")]
public readonly string CheckboxDescription = "Hostile forces spawn on the battlefield";

View File

@@ -18,9 +18,11 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Controls the game speed, tech level, and short game lobby options.")]
public class MapOptionsInfo : TraitInfo, ILobbyOptions, IRulesetLoaded
{
[Translate]
[Desc("Descriptive label for the short game checkbox in the lobby.")]
public readonly string ShortGameCheckboxLabel = "Short Game";
[Translate]
[Desc("Tooltip description for the short game checkbox in the lobby.")]
public readonly string ShortGameCheckboxDescription = "Players are defeated when their bases are destroyed";
@@ -36,9 +38,11 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Display order for the short game checkbox in the lobby.")]
public readonly int ShortGameCheckboxDisplayOrder = 0;
[Translate]
[Desc("Descriptive label for the tech level option in the lobby.")]
public readonly string TechLevelDropdownLabel = "Tech Level";
[Translate]
[Desc("Tooltip description for the tech level option in the lobby.")]
public readonly string TechLevelDropdownDescription = "Change the units and abilities at your disposal";
@@ -54,9 +58,11 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Display order for the tech level option in the lobby.")]
public readonly int TechLevelDropdownDisplayOrder = 0;
[Translate]
[Desc("Tooltip description for the game speed option in the lobby.")]
public readonly string GameSpeedDropdownLabel = "Game Speed";
[Translate]
[Desc("Description of the game speed option in the lobby.")]
public readonly string GameSpeedDropdownDescription = "Change the rate at which time passes";

View File

@@ -21,10 +21,12 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Internal id for this option.")]
public readonly string ID = null;
[Translate]
[FieldLoader.Require]
[Desc("Descriptive label for this option.")]
public readonly string Label = null;
[Translate]
[Desc("Tooltip description for this option.")]
public readonly string Description = null;

View File

@@ -19,13 +19,15 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Spawn base actor at the spawnpoint and support units in an annulus around the base actor. Both are defined at MPStartUnits. Attach this to the world actor.")]
public class SpawnStartingUnitsInfo : TraitInfo, Requires<StartingUnitsInfo>, ILobbyOptions
public class SpawnMPUnitsInfo : TraitInfo, Requires<MPStartUnitsInfo>, ILobbyOptions
{
public readonly string StartingUnitsClass = "none";
[Translate]
[Desc("Descriptive label for the starting units option in the lobby.")]
public readonly string DropdownLabel = "Starting Units";
[Translate]
[Desc("Tooltip description for the starting units option in the lobby.")]
public readonly string DropdownDescription = "Change the units that you start the game with";
@@ -43,7 +45,7 @@ namespace OpenRA.Mods.Common.Traits
var startingUnits = new Dictionary<string, string>();
// Duplicate classes are defined for different race variants
foreach (var t in rules.Actors["world"].TraitInfos<StartingUnitsInfo>())
foreach (var t in rules.Actors["world"].TraitInfos<MPStartUnitsInfo>())
startingUnits[t.Class] = t.ClassName;
if (startingUnits.Any())
@@ -51,14 +53,14 @@ namespace OpenRA.Mods.Common.Traits
new ReadOnlyDictionary<string, string>(startingUnits), StartingUnitsClass, DropdownLocked);
}
public override object Create(ActorInitializer init) { return new SpawnStartingUnits(this); }
public override object Create(ActorInitializer init) { return new SpawnMPUnits(this); }
}
public class SpawnStartingUnits : IWorldLoaded
public class SpawnMPUnits : IWorldLoaded
{
readonly SpawnStartingUnitsInfo info;
readonly SpawnMPUnitsInfo info;
public SpawnStartingUnits(SpawnStartingUnitsInfo info)
public SpawnMPUnits(SpawnMPUnitsInfo info)
{
this.info = info;
}
@@ -75,7 +77,7 @@ namespace OpenRA.Mods.Common.Traits
var spawnClass = p.PlayerReference.StartingUnitsClass ?? w.LobbyInfo.GlobalSettings
.OptionOrDefault("startingunits", info.StartingUnitsClass);
var unitGroup = w.Map.Rules.Actors["world"].TraitInfos<StartingUnitsInfo>()
var unitGroup = w.Map.Rules.Actors["world"].TraitInfos<MPStartUnitsInfo>()
.Where(g => g.Class == spawnClass && g.Factions != null && g.Factions.Contains(p.Faction.InternalName))
.RandomOrDefault(w.SharedRandom);

View File

@@ -1,38 +0,0 @@
#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 RenameMPTraits : UpdateRule
{
public override string Name { get { return "Several traits spawning map actors and players have been renamed."; } }
public override string Description
{
get
{
return "'SpawnMPUnits' was renamed to 'SpawnStartingUnits', 'MPStartUnits' to 'StartingUnits', 'MPStartLocations' to " +
"'MapStartingLocations', and 'CreateMPPlayers' to 'CreateMapPlayers'.";
}
}
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
{
actorNode.RenameChildrenMatching("SpawnMPUnits", "SpawnStartingUnits");
actorNode.RenameChildrenMatching("MPStartUnits", "StartingUnits");
actorNode.RenameChildrenMatching("MPStartLocations", "MapStartingLocations");
actorNode.RenameChildrenMatching("CreateMPPlayers", "CreateMapPlayers");
yield break;
}
}
}

View File

@@ -53,9 +53,9 @@ namespace OpenRA.Mods.Common.UpdateRules
new RenameRallyPointPath(),
}),
new UpdatePath("release-20200503", "playtest-20201213", new UpdateRule[]
new UpdatePath("release-20200503", new UpdateRule[]
{
// Prep only changes here
// Bleed only changes here
new AddPipDecorationTraits(),
new ModernizeDecorationTraits(),
new RenameHealCrateAction(),
@@ -79,12 +79,6 @@ namespace OpenRA.Mods.Common.UpdateRules
new RenameCircleContrast(),
new SplitDamagedByTerrain(),
new RemoveLaysTerrain(),
}),
new UpdatePath("playtest-20201213", new UpdateRule[]
{
// Bleed only changes here
new RenameMPTraits(),
})
};

View File

@@ -0,0 +1,58 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace OpenRA.Mods.Common.UtilityCommands
{
public class CheckRuntimeAssembliesCommand : IUtilityCommand
{
string IUtilityCommand.Name { get { return "--check-runtime-assemblies"; } }
bool IUtilityCommand.ValidateArguments(string[] args)
{
return true;
}
[Desc("ASSEMBLY [ASSEMBLY ...]", "Check the runtime dependencies of the mod against a given whitelist of " +
"assembly (dll and exe) names and generate an error if any unlisted files are required.")]
void IUtilityCommand.Run(Utility utility, string[] args)
{
var whitelist = args
.Skip(1)
.Select(a => Path.GetFileName(a))
.ToArray();
// Load the renderer assembly so we can check its dependencies
Assembly.LoadFile(Path.Combine(Platform.BinDir, "OpenRA.Platforms.Default.dll"));
var missing = new List<string>();
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
{
var assemblyName = Path.GetFileName(a.Location);
if (!whitelist.Contains(assemblyName))
missing.Add(assemblyName);
}
if (missing.Any())
{
Console.WriteLine("error: The following assemblies are referenced but not whitelisted:");
foreach (var m in missing)
Console.WriteLine(" " + m);
Environment.Exit(1);
}
}
}
}

View File

@@ -78,8 +78,16 @@ namespace OpenRA.Mods.Common.UtilityCommands
frame.Size.Width);
}
var png = new Png(pngData, SpriteFrameType.Indexed8, frameSize.Width, frameSize.Height, palColors);
png.Save("{0}-{1:D4}.png".F(prefix, count++));
if (frame.Type == SpriteFrameType.BGRA)
{
var png = new Png(pngData, frameSize.Width, frameSize.Height);
png.Save("{0}-{1:D4}.png".F(prefix, count++));
}
else
{
var png = new Png(pngData, frameSize.Width, frameSize.Height, palColors);
png.Save("{0}-{1:D4}.png".F(prefix, count++));
}
}
Console.WriteLine("Saved {0}-[0..{1}].png", prefix, count - 1);

View File

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

View File

@@ -45,7 +45,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
var count = 0;
var sb = sequences.SpriteCache.SheetBuilders[SheetType.Indexed];
var sb = sequences.SpriteCache.SheetBuilders[SpriteFrameType.Indexed];
foreach (var s in sb.AllSheets)
{
var max = s == sb.Current ? (int)sb.CurrentChannel + 1 : 4;
@@ -53,7 +53,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
s.AsPng((TextureChannel)ChannelMasks[i], palette).Save("{0}.png".F(count++));
}
sb = sequences.SpriteCache.SheetBuilders[SheetType.BGRA];
sb = sequences.SpriteCache.SheetBuilders[SpriteFrameType.BGRA];
foreach (var s in sb.AllSheets)
s.AsPng().Save("{0}.png".F(count++));

View File

@@ -0,0 +1,79 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace OpenRA.Mods.Common.UtilityCommands
{
class ExtractLanguageStringsCommand : IUtilityCommand
{
string IUtilityCommand.Name { get { return "--extract-language-strings"; } }
bool IUtilityCommand.ValidateArguments(string[] args)
{
return true;
}
[Desc("Extract translatable strings that are not yet localized and update chrome layout.")]
void IUtilityCommand.Run(Utility utility, string[] args)
{
// HACK: The engine code assumes that Game.modData is set.
var modData = Game.ModData = utility.ModData;
var types = modData.ObjectCreator.GetTypes();
var translatableFields = types.SelectMany(t => t.GetFields())
.Where(f => f.HasAttribute<TranslateAttribute>()).Distinct();
foreach (var filename in modData.Manifest.ChromeLayout)
{
modData.ModFiles.TryGetPackageContaining(filename, out var package, out var name);
name = package.Name + "/" + name;
Console.WriteLine("# {0}:", filename);
var yaml = MiniYaml.FromFile(name, false);
FromChromeLayout(ref yaml, null,
translatableFields.Select(t => t.Name).Distinct(), null);
using (var file = new StreamWriter(name))
file.WriteLine(yaml.WriteToString());
}
// TODO: Properties can also be translated.
}
internal static void FromChromeLayout(ref List<MiniYamlNode> nodes, MiniYamlNode parent, IEnumerable<string> translatables, string container)
{
var parentNode = parent != null && parent.Key != null ? parent.Key.Split('@') : null;
var parentType = parent != null && parent.Key != null ? parentNode.First() : null;
var parentLabel = parent != null && parent.Key != null ? parentNode.Last() : null;
if ((parentType == "Background" || parentType == "Container") && parentLabel.IsUppercase())
container = parentLabel;
foreach (var node in nodes)
{
var alreadyTranslated = node.Value.Value != null && node.Value.Value.Contains('@');
if (translatables.Contains(node.Key) && !alreadyTranslated && parentLabel != null)
{
var translationKey = "{0}-{1}".F(parentLabel.Replace('_', '-'), node.Key.ToUpper());
if (container != null)
translationKey = "{0}-".F(container.Replace('_', '-')) + translationKey;
Console.WriteLine("\t{0}: {1}", translationKey, node.Value.Value);
node.Value.Value = "@{0}@".F(translationKey);
}
FromChromeLayout(ref node.Value.Nodes, node, translatables, container);
}
}
}
}

View File

@@ -26,6 +26,7 @@ namespace OpenRA.Mods.Common.Widgets
public bool DisableKeyRepeat = false;
public bool DisableKeySound = false;
[Translate]
public string Text = "";
public TextAlign Align = TextAlign.Center;
public int LeftMargin = 5;
@@ -57,9 +58,11 @@ namespace OpenRA.Mods.Common.Widgets
protected Lazy<TooltipContainerWidget> tooltipContainer;
[Translate]
public string TooltipText;
public Func<string> GetTooltipText;
[Translate]
public string TooltipDesc;
public Func<string> GetTooltipDesc;

View File

@@ -26,6 +26,7 @@ namespace OpenRA.Mods.Common.Widgets
public Func<string> GetImageName;
public Func<string> GetImageCollection;
[Translate]
public string TooltipText;
Lazy<TooltipContainerWidget> tooltipContainer;

View File

@@ -21,6 +21,7 @@ namespace OpenRA.Mods.Common.Widgets
public class LabelWidget : Widget
{
[Translate]
public string Text = null;
public TextAlign Align = TextAlign.Left;
public TextVAlign VAlign = TextVAlign.Middle;

View File

@@ -43,12 +43,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
string currentFilename;
IReadOnlyPackage currentPackage;
Sprite[] currentSprites;
IModel currentVoxel;
VqaPlayerWidget player = null;
bool isVideoLoaded = false;
bool isLoadError = false;
int currentFrame;
WRot modelOrientation;
[ObjectCreator.UseCtor]
public AssetBrowserLogic(Widget widget, Action onExit, ModData modData, World world, Dictionary<string, MiniYaml> logicArgs)
@@ -81,24 +79,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
spriteWidget.GetSprite = () => currentSprites != null ? currentSprites[currentFrame] : null;
currentPalette = spriteWidget.Palette;
spriteWidget.GetPalette = () => currentPalette;
spriteWidget.IsVisible = () => !isVideoLoaded && !isLoadError && currentSprites != null;
spriteWidget.IsVisible = () => !isVideoLoaded && !isLoadError;
}
var playerWidget = panel.GetOrNull<VqaPlayerWidget>("PLAYER");
if (playerWidget != null)
playerWidget.IsVisible = () => isVideoLoaded && !isLoadError;
var modelWidget = panel.GetOrNull<ModelWidget>("VOXEL");
if (modelWidget != null)
{
modelWidget.GetVoxel = () => currentVoxel;
currentPalette = modelWidget.Palette;
modelWidget.GetPalette = () => currentPalette;
modelWidget.GetPlayerPalette = () => currentPalette;
modelWidget.GetRotation = () => modelOrientation;
modelWidget.IsVisible = () => !isVideoLoaded && !isLoadError && currentVoxel != null;
}
var errorLabelWidget = panel.GetOrNull("ERROR");
if (errorLabelWidget != null)
errorLabelWidget.IsVisible = () => isLoadError;
@@ -223,46 +210,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
prevButton.IsVisible = () => !isVideoLoaded;
}
var voxelContainer = panel.GetOrNull("VOXEL_SELECTOR");
if (voxelContainer != null)
voxelContainer.IsVisible = () => currentVoxel != null;
var rollSlider = panel.GetOrNull<SliderWidget>("ROLL_SLIDER");
if (rollSlider != null)
{
rollSlider.OnChange += x =>
{
var roll = (int)x;
modelOrientation = modelOrientation.WithRoll(new WAngle(roll));
};
rollSlider.GetValue = () => modelOrientation.Roll.Angle;
}
var pitchSlider = panel.GetOrNull<SliderWidget>("PITCH_SLIDER");
if (pitchSlider != null)
{
pitchSlider.OnChange += x =>
{
var pitch = (int)x;
modelOrientation = modelOrientation.WithPitch(new WAngle(pitch));
};
pitchSlider.GetValue = () => modelOrientation.Pitch.Angle;
}
var yawSlider = panel.GetOrNull<SliderWidget>("YAW_SLIDER");
if (yawSlider != null)
{
yawSlider.OnChange += x =>
{
var yaw = (int)x;
modelOrientation = modelOrientation.WithYaw(new WAngle(yaw));
};
yawSlider.GetValue = () => modelOrientation.Yaw.Angle;
}
var assetBrowserModData = modData.Manifest.Get<AssetBrowser>();
allowedExtensions = assetBrowserModData.SupportedExtensions;
@@ -395,24 +342,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return true;
}
if (Path.GetExtension(filename.ToLowerInvariant()) == ".vxl")
currentSprites = world.Map.Rules.Sequences.SpriteCache[prefix + filename];
currentFrame = 0;
if (frameSlider != null)
{
var voxelName = Path.GetFileNameWithoutExtension(filename);
currentVoxel = world.ModelCache.GetModel(voxelName);
currentSprites = null;
}
else
{
currentSprites = world.Map.Rules.Sequences.SpriteCache[prefix + filename];
currentFrame = 0;
if (frameSlider != null)
{
frameSlider.MaximumValue = (float)currentSprites.Length - 1;
frameSlider.Ticks = currentSprites.Length;
}
currentVoxel = null;
frameSlider.MaximumValue = (float)currentSprites.Length - 1;
frameSlider.Ticks = currentSprites.Length;
}
}
catch (Exception ex)

View File

@@ -101,8 +101,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (player == null || player.RelationshipWith(pp) == PlayerRelationship.Ally || player.WinState != WinState.Undefined)
{
flag.GetImageName = () => pp.Faction.InternalName;
var factionName = pp.Faction.Name != pp.DisplayFaction.Name ? "{0} ({1})".F(pp.DisplayFaction.Name, pp.Faction.Name) : pp.Faction.Name;
item.Get<LabelWidget>("FACTION").GetText = () => factionName;
item.Get<LabelWidget>("FACTION").GetText = () => pp.Faction.Name;
}
else
{

View File

@@ -247,6 +247,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
BindIntSliderPref(panel, "FRAME_LIMIT_SLIDER", ds, "MaxFramerate");
BindCheckboxPref(panel, "PLAYER_STANCE_COLORS_CHECKBOX", gs, "UsePlayerStanceColors");
var languageDropDownButton = panel.GetOrNull<DropDownButtonWidget>("LANGUAGE_DROPDOWNBUTTON");
if (languageDropDownButton != null)
{
languageDropDownButton.OnMouseDown = _ => ShowLanguageDropdown(languageDropDownButton, modData.Languages);
languageDropDownButton.GetText = () => FieldLoader.Translate(ds.Language);
}
var windowModeDropdown = panel.Get<DropDownButtonWidget>("MODE_DROPDOWN");
windowModeDropdown.OnMouseDown = _ => ShowWindowModeDropdown(windowModeDropdown, ds);
windowModeDropdown.GetText = () => ds.Mode == WindowMode.Windowed ?
@@ -381,6 +388,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{
ds.CapFramerate = dds.CapFramerate;
ds.MaxFramerate = dds.MaxFramerate;
ds.Language = dds.Language;
ds.GLProfile = dds.GLProfile;
ds.Mode = dds.Mode;
ds.VideoDisplay = dds.VideoDisplay;
@@ -820,6 +828,21 @@ namespace OpenRA.Mods.Common.Widgets.Logic
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options.Keys, setupItem);
}
static void ShowLanguageDropdown(DropDownButtonWidget dropdown, IEnumerable<string> languages)
{
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (o, itemTemplate) =>
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => Game.Settings.Graphics.Language == o,
() => Game.Settings.Graphics.Language = o);
item.Get<LabelWidget>("LABEL").GetText = () => FieldLoader.Translate(o);
return item;
};
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, languages, setupItem);
}
static void ShowStatusBarsDropdown(DropDownButtonWidget dropdown, GameSettings s)
{
var options = new Dictionary<string, StatusBarsType>()

View File

@@ -1,217 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public class ModelWidget : Widget
{
public string Palette = "terrain";
public string PlayerPalette = "player";
public string NormalsPalette = "normals";
public string ShadowPalette = "shadow";
public float Scale = 12f;
public int LightPitch = 142;
public int LightYaw = 682;
public float[] LightAmbientColor = new float[] { 0.6f, 0.6f, 0.6f };
public float[] LightDiffuseColor = new float[] { 0.4f, 0.4f, 0.4f };
public WRot Rotation = WRot.None;
public WAngle CameraAngle = WAngle.FromDegrees(40);
public Func<string> GetPalette;
public Func<string> GetPlayerPalette;
public Func<string> GetNormalsPalette;
public Func<string> GetShadowPalette;
public Func<float[]> GetLightAmbientColor;
public Func<float[]> GetLightDiffuseColor;
public Func<float> GetScale;
public Func<int> GetLightPitch;
public Func<int> GetLightYaw;
public Func<IModel> GetVoxel;
public Func<WRot> GetRotation;
public Func<WAngle> GetCameraAngle;
public int2 IdealPreviewSize { get; private set; }
protected readonly WorldRenderer WorldRenderer;
IFinalizedRenderable renderable;
[ObjectCreator.UseCtor]
public ModelWidget(WorldRenderer worldRenderer)
{
GetPalette = () => Palette;
GetPlayerPalette = () => PlayerPalette;
GetNormalsPalette = () => NormalsPalette;
GetShadowPalette = () => ShadowPalette;
GetLightAmbientColor = () => LightAmbientColor;
GetLightDiffuseColor = () => LightDiffuseColor;
GetScale = () => Scale;
GetRotation = () => Rotation;
GetLightPitch = () => LightPitch;
GetLightYaw = () => LightYaw;
GetCameraAngle = () => CameraAngle;
WorldRenderer = worldRenderer;
}
protected ModelWidget(ModelWidget other)
: base(other)
{
Palette = other.Palette;
GetPalette = other.GetPalette;
GetVoxel = other.GetVoxel;
WorldRenderer = other.WorldRenderer;
}
public override Widget Clone()
{
return new ModelWidget(this);
}
IModel cachedVoxel;
string cachedPalette;
string cachedPlayerPalette;
string cachedNormalsPalette;
string cachedShadowPalette;
float cachedScale;
WRot cachedRotation;
float[] cachedLightAmbientColor = new float[] { 0, 0, 0 };
float[] cachedLightDiffuseColor = new float[] { 0, 0, 0 };
int cachedLightPitch;
int cachedLightYaw;
WAngle cachedCameraAngle;
PaletteReference paletteReference;
PaletteReference paletteReferencePlayer;
PaletteReference paletteReferenceNormals;
PaletteReference paletteReferenceShadow;
public override void Draw()
{
if (renderable == null)
return;
renderable.Render(WorldRenderer);
}
public override void PrepareRenderables()
{
var voxel = GetVoxel();
var palette = GetPalette();
var playerPalette = GetPlayerPalette();
var normalsPalette = GetNormalsPalette();
var shadowPalette = GetShadowPalette();
var scale = GetScale();
var rotation = GetRotation();
var lightAmbientColor = GetLightAmbientColor();
var lightDiffuseColor = GetLightDiffuseColor();
var lightPitch = GetLightPitch();
var lightYaw = GetLightYaw();
var cameraAngle = GetCameraAngle();
if (voxel == null || palette == null)
return;
if (voxel != cachedVoxel)
cachedVoxel = voxel;
if (palette != cachedPalette)
{
if (string.IsNullOrEmpty(palette) && string.IsNullOrEmpty(playerPalette))
return;
var paletteName = string.IsNullOrEmpty(palette) ? playerPalette : palette;
paletteReference = WorldRenderer.Palette(paletteName);
cachedPalette = paletteName;
}
if (playerPalette != cachedPlayerPalette)
{
paletteReferencePlayer = WorldRenderer.Palette(playerPalette);
cachedPlayerPalette = playerPalette;
}
if (normalsPalette != cachedNormalsPalette)
{
paletteReferenceNormals = WorldRenderer.Palette(normalsPalette);
cachedNormalsPalette = normalsPalette;
}
if (shadowPalette != cachedShadowPalette)
{
paletteReferenceShadow = WorldRenderer.Palette(shadowPalette);
cachedShadowPalette = shadowPalette;
}
if (scale != cachedScale)
cachedScale = scale;
if (rotation != cachedRotation)
cachedRotation = rotation;
if (lightPitch != cachedLightPitch)
cachedLightPitch = lightPitch;
if (lightYaw != cachedLightYaw)
cachedLightYaw = lightYaw;
if (cachedLightAmbientColor[0] != lightAmbientColor[0] || cachedLightAmbientColor[1] != lightAmbientColor[1] || cachedLightAmbientColor[2] != lightAmbientColor[2])
cachedLightAmbientColor = lightAmbientColor;
if (cachedLightDiffuseColor[0] != lightDiffuseColor[0] || cachedLightDiffuseColor[1] != lightDiffuseColor[1] || cachedLightDiffuseColor[2] != lightDiffuseColor[2])
cachedLightDiffuseColor = lightDiffuseColor;
if (cameraAngle != cachedCameraAngle)
cachedCameraAngle = cameraAngle;
if (cachedVoxel == null)
return;
var animation = new ModelAnimation(
cachedVoxel,
() => WVec.Zero,
() => cachedRotation,
() => false,
() => 0,
true);
var animations = new ModelAnimation[] { animation };
ModelPreview preview = new ModelPreview(
new ModelAnimation[] { animation }, WVec.Zero, 0,
cachedScale,
new WAngle(cachedLightPitch),
new WAngle(cachedLightYaw),
cachedLightAmbientColor,
cachedLightDiffuseColor,
cachedCameraAngle,
paletteReference,
paletteReferenceNormals,
paletteReferenceShadow);
var screenBounds = animation.ScreenBounds(WPos.Zero, WorldRenderer, scale);
IdealPreviewSize = new int2(screenBounds.Width, screenBounds.Height);
var origin = RenderOrigin + new int2(RenderBounds.Size.Width / 2, RenderBounds.Size.Height / 2);
var camera = new WRot(WAngle.Zero, cachedCameraAngle - new WAngle(256), new WAngle(256));
var modelRenderable = new UIModelRenderable(
animations, WPos.Zero, origin, 0, camera, scale,
WRot.None, cachedLightAmbientColor, cachedLightDiffuseColor,
paletteReferencePlayer, paletteReferenceNormals, paletteReferenceShadow);
renderable = modelRenderable.PrepareRender(WorldRenderer);
}
}
}

View File

@@ -70,10 +70,13 @@ namespace OpenRA.Mods.Common.Widgets
public readonly bool DrawTime = true;
[Translate]
public readonly string ReadyText = "";
[Translate]
public readonly string HoldText = "";
[Translate]
public readonly string InfiniteSymbol = "\u221E";
public int DisplayedIconCount { get; private set; }

View File

@@ -22,8 +22,10 @@ namespace OpenRA.Mods.Common.Widgets
{
public class SupportPowersWidget : Widget
{
[Translate]
public readonly string ReadyText = "";
[Translate]
public readonly string HoldText = "";
public readonly string OverlayFont = "TinyBold";

View File

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

View File

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

View File

@@ -22,7 +22,7 @@ namespace OpenRA.Mods.D2k.SpriteLoaders
{
class R8Frame : ISpriteFrame
{
public SpriteFrameType Type { get { return SpriteFrameType.Indexed8; } }
public SpriteFrameType Type { get; set; }
public Size Size { get; private set; }
public Size FrameSize { get; private set; }
public float2 Offset { get; private set; }

View File

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

View File

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

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