Compare commits
342 Commits
geoip-cach
...
devtest-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86a714a588 | ||
|
|
c01c39954a | ||
|
|
c48eb572e3 | ||
|
|
b03ab1212f | ||
|
|
8a9b5e7e01 | ||
|
|
9b90e4f25a | ||
|
|
8a4401bdcb | ||
|
|
d52e90cf23 | ||
|
|
14ef0a7740 | ||
|
|
c1f79b348a | ||
|
|
62166a50d9 | ||
|
|
283b330403 | ||
|
|
2cf6b74295 | ||
|
|
1a9f707d18 | ||
|
|
06a1c88e86 | ||
|
|
f358b566b1 | ||
|
|
15fc27d142 | ||
|
|
ad20597d74 | ||
|
|
6d409a7c97 | ||
|
|
36d5ae5421 | ||
|
|
db9744ea7f | ||
|
|
5e62fe86fc | ||
|
|
6cfa27c33b | ||
|
|
a405969199 | ||
|
|
9a6f3b4c05 | ||
|
|
7be059a79b | ||
|
|
29b55de042 | ||
|
|
235fb19aa8 | ||
|
|
c0f54fa4fc | ||
|
|
cdc216aca0 | ||
|
|
4505053618 | ||
|
|
e0d53126d6 | ||
|
|
12ff1dd14c | ||
|
|
50db3152f6 | ||
|
|
9c4fd0e3d3 | ||
|
|
8d27d22100 | ||
|
|
27f1a7ab27 | ||
|
|
d52e4793fe | ||
|
|
544ac6cb33 | ||
|
|
06fbc1a6cf | ||
|
|
edab10e6a6 | ||
|
|
dd99fc93e4 | ||
|
|
dbe824d4e5 | ||
|
|
6e73d7f5c2 | ||
|
|
960056d300 | ||
|
|
3efac3287e | ||
|
|
c4b4a8c8a5 | ||
|
|
b833f033bf | ||
|
|
ad75e2be89 | ||
|
|
7ee4fbeb0d | ||
|
|
9886f0ca9a | ||
|
|
46cf56d6ff | ||
|
|
e7af295b5e | ||
|
|
9d179d9a1a | ||
|
|
15010f9567 | ||
|
|
a7f4f6c1cf | ||
|
|
3eeb677f14 | ||
|
|
ef69a3de66 | ||
|
|
0aa5e07252 | ||
|
|
07d58337f1 | ||
|
|
b5e3f25418 | ||
|
|
19b02875c7 | ||
|
|
8a74f6ea18 | ||
|
|
a847f3eafa | ||
|
|
a751f074e7 | ||
|
|
75cb5c2166 | ||
|
|
70a86bed7a | ||
|
|
f67f8ed05e | ||
|
|
1ae53220d6 | ||
|
|
c546cb552e | ||
|
|
10f8836d7b | ||
|
|
7ecd4124ce | ||
|
|
54cd77be8e | ||
|
|
43388cb7fc | ||
|
|
4cc5104fde | ||
|
|
d519cabae3 | ||
|
|
9852e29835 | ||
|
|
3a427c3630 | ||
|
|
336656e8f7 | ||
|
|
06ad9666e8 | ||
|
|
728e0c6600 | ||
|
|
657e690bdd | ||
|
|
2d36d0a659 | ||
|
|
c42fd5d2e2 | ||
|
|
10bf97eff6 | ||
|
|
ea9992247d | ||
|
|
b90fecff76 | ||
|
|
a4fc9fea3b | ||
|
|
b7c7eff2a2 | ||
|
|
8c10dc406a | ||
|
|
05c3861426 | ||
|
|
1ef5db8896 | ||
|
|
006a87692a | ||
|
|
e019b70420 | ||
|
|
70ec5b0344 | ||
|
|
5401ace540 | ||
|
|
ab9081c852 | ||
|
|
3a9b35980c | ||
|
|
150439d215 | ||
|
|
b01a534a98 | ||
|
|
758b0b08d0 | ||
|
|
f87ba1d8a4 | ||
|
|
67fa7bdcc9 | ||
|
|
0b03aca104 | ||
|
|
3bf61f1043 | ||
|
|
ac975f4139 | ||
|
|
6d12301f88 | ||
|
|
914950c4a5 | ||
|
|
b417b267dd | ||
|
|
aae497eff1 | ||
|
|
3c9db4c2ac | ||
|
|
8c3793e7ea | ||
|
|
60a7f53491 | ||
|
|
04bfd62f2f | ||
|
|
117b8b3653 | ||
|
|
70cb0d2924 | ||
|
|
c5ea496c45 | ||
|
|
01e955ca37 | ||
|
|
fdb66c769c | ||
|
|
38b3fbbdbe | ||
|
|
3bc5d2d02c | ||
|
|
ac7eda8ca2 | ||
|
|
baf58f53b3 | ||
|
|
8513a83331 | ||
|
|
bf7fecff10 | ||
|
|
6e1f2f636c | ||
|
|
8b3db6f3d6 | ||
|
|
39d0abe982 | ||
|
|
c2026dc254 | ||
|
|
cae6c28754 | ||
|
|
3b99924799 | ||
|
|
b2b548b103 | ||
|
|
c2e3806a77 | ||
|
|
25500a7dda | ||
|
|
8c394a4cb5 | ||
|
|
2e7bd4de4b | ||
|
|
7261322e41 | ||
|
|
cada396733 | ||
|
|
9f9709f058 | ||
|
|
26fc65209d | ||
|
|
f642cead44 | ||
|
|
616d9421d6 | ||
|
|
20e5219cf4 | ||
|
|
9edda21b06 | ||
|
|
6d6822ca15 | ||
|
|
a2269e7ee7 | ||
|
|
e8f443f4a9 | ||
|
|
67f8452178 | ||
|
|
47f6e407d9 | ||
|
|
889e2152a4 | ||
|
|
173aae1f81 | ||
|
|
baed80983b | ||
|
|
b00423dc76 | ||
|
|
376ed15079 | ||
|
|
a6d8d6cd8e | ||
|
|
b3ee8b447e | ||
|
|
5f588561b6 | ||
|
|
83c53e17e0 | ||
|
|
c028488894 | ||
|
|
b066005f7e | ||
|
|
87e33a75c6 | ||
|
|
4961b0943b | ||
|
|
eda1e9c266 | ||
|
|
bc7bf174d8 | ||
|
|
2e06d5790b | ||
|
|
ab8790e8f1 | ||
|
|
7d630e63e7 | ||
|
|
77899191f3 | ||
|
|
6871873e93 | ||
|
|
fc4bd131cd | ||
|
|
d866286f82 | ||
|
|
76dfda164e | ||
|
|
134d47e48c | ||
|
|
a2dbd5e013 | ||
|
|
c27412c83a | ||
|
|
4143aba595 | ||
|
|
39ccac4022 | ||
|
|
7943f4deb6 | ||
|
|
2dda2d7689 | ||
|
|
b8f2a14ea0 | ||
|
|
595809f090 | ||
|
|
477db9cd4a | ||
|
|
67ff292d62 | ||
|
|
ae882b85a9 | ||
|
|
5e92915095 | ||
|
|
95809db03c | ||
|
|
6d5a5121bc | ||
|
|
86992751c7 | ||
|
|
2eba8b6c37 | ||
|
|
e1523e939d | ||
|
|
15a92f443d | ||
|
|
b57c68e392 | ||
|
|
e95fcb6bc0 | ||
|
|
6581fcb6a7 | ||
|
|
6551337bd8 | ||
|
|
137df52fdd | ||
|
|
b79aa7eb6a | ||
|
|
1c8c49dc8e | ||
|
|
f0c808d2fc | ||
|
|
26d9ae88df | ||
|
|
3c7f119bb1 | ||
|
|
fea35923f0 | ||
|
|
9627776318 | ||
|
|
6dcde3af72 | ||
|
|
43717a89b5 | ||
|
|
c7ba359688 | ||
|
|
dc3dbf6d85 | ||
|
|
71664c85ff | ||
|
|
cb41be113a | ||
|
|
4fe7daa85e | ||
|
|
f75afc6ee4 | ||
|
|
67fd71ab92 | ||
|
|
385e70552e | ||
|
|
1e2c67bfca | ||
|
|
56739f87fb | ||
|
|
571eb7614f | ||
|
|
c6c3a8c60d | ||
|
|
ae7cfa56b7 | ||
|
|
b856613194 | ||
|
|
86305879cb | ||
|
|
e5a1a8a706 | ||
|
|
27602a4a97 | ||
|
|
a98e460257 | ||
|
|
0349435650 | ||
|
|
c3fbdca18f | ||
|
|
acb5245a28 | ||
|
|
23561cd76b | ||
|
|
d3ab3d7d78 | ||
|
|
5b870be83f | ||
|
|
6130d5622c | ||
|
|
318c4e3456 | ||
|
|
ab701449e2 | ||
|
|
9a3447d863 | ||
|
|
5280637adf | ||
|
|
3bce55ac44 | ||
|
|
a3f79503ed | ||
|
|
02d462a82c | ||
|
|
3d17328d0d | ||
|
|
0e81abc21b | ||
|
|
803b930405 | ||
|
|
6adf45bcb4 | ||
|
|
01417c88c5 | ||
|
|
60bbbe0d93 | ||
|
|
888dfd3654 | ||
|
|
12de56ff62 | ||
|
|
b7cee41c54 | ||
|
|
10aac03f75 | ||
|
|
0eb0041f90 | ||
|
|
b38018af9c | ||
|
|
4df5ac0385 | ||
|
|
a7476bc303 | ||
|
|
cc4b3cb361 | ||
|
|
31a965b29a | ||
|
|
7a213338a2 | ||
|
|
fb27a25e52 | ||
|
|
534b09ae4a | ||
|
|
341a9f370c | ||
|
|
607d9b2d5c | ||
|
|
507ce40ad2 | ||
|
|
f58c3aed32 | ||
|
|
55e85bd9ca | ||
|
|
4bf614c5cd | ||
|
|
96b06c75d1 | ||
|
|
d261648ab0 | ||
|
|
7b81b9e806 | ||
|
|
a93aea3e4e | ||
|
|
2cfacc2c7d | ||
|
|
6d6b21a0eb | ||
|
|
7a78c37851 | ||
|
|
b8a9f41892 | ||
|
|
227567dfe1 | ||
|
|
d5ff5c672b | ||
|
|
393f6eca3a | ||
|
|
d193ef856e | ||
|
|
44d3691fa1 | ||
|
|
27d0465891 | ||
|
|
52a9fcef3c | ||
|
|
7386816f52 | ||
|
|
9c0075b233 | ||
|
|
e4c5700baf | ||
|
|
e1b7df8b6a | ||
|
|
c999b2d778 | ||
|
|
bfb6c671fb | ||
|
|
7c6ec577dc | ||
|
|
626b40f31b | ||
|
|
5af12440ba | ||
|
|
4614f6febe | ||
|
|
1354ffc32e | ||
|
|
3723939c99 | ||
|
|
e099739e13 | ||
|
|
21a48cc41d | ||
|
|
4740266308 | ||
|
|
78139413d7 | ||
|
|
f0578a75f4 | ||
|
|
7a0e55a02a | ||
|
|
f132bac80d | ||
|
|
109ea4fe5b | ||
|
|
f33feafd0e | ||
|
|
9195356e3a | ||
|
|
eff91108f4 | ||
|
|
e40c0516e6 | ||
|
|
c580a94ab7 | ||
|
|
6a545bb942 | ||
|
|
672bd2d9fe | ||
|
|
3ab4a584ab | ||
|
|
150d02ac0d | ||
|
|
6d26f60904 | ||
|
|
b42276953f | ||
|
|
7c290b9f76 | ||
|
|
51fe1d6629 | ||
|
|
8f558d2b47 | ||
|
|
3f5fadf2e9 | ||
|
|
07c16cee1d | ||
|
|
24130dfcdc | ||
|
|
15a2341a91 | ||
|
|
86f61298e6 | ||
|
|
3cd7ec3878 | ||
|
|
8b13d3e4c7 | ||
|
|
327d451abc | ||
|
|
2dac16ee02 | ||
|
|
d4b92a19d7 | ||
|
|
e0357596f5 | ||
|
|
1ef27d18c1 | ||
|
|
1d2d8ed107 | ||
|
|
1bf01bc214 | ||
|
|
0015deca47 | ||
|
|
ddfdc6e90f | ||
|
|
5db2ad54f2 | ||
|
|
b8a5750529 | ||
|
|
98d5b8c7cc | ||
|
|
3f34154a1e | ||
|
|
3119f831b3 | ||
|
|
8a07b762a2 | ||
|
|
551ab2fc59 | ||
|
|
99957e57b9 | ||
|
|
be2c59bc6e | ||
|
|
57f9a49b66 | ||
|
|
54bd0eb99d | ||
|
|
485faac294 | ||
|
|
d531d6f3ef | ||
|
|
3a9fdb82f5 | ||
|
|
5024ae1156 |
@@ -6,13 +6,101 @@ charset=utf-8
|
||||
[*]
|
||||
end_of_line = LF
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
; 4-column tab indentation
|
||||
; 4-column tab indentation and .NET coding conventions
|
||||
[*.cs]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
dotnet_separate_import_directive_groups = false
|
||||
dotnet_sort_system_directives_first = true
|
||||
|
||||
csharp_style_var_elsewhere = true:suggestion
|
||||
csharp_style_var_for_built_in_types = true:suggestion
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
|
||||
csharp_prefer_braces = when_multiline:suggestion
|
||||
csharp_using_directive_placement = outside_namespace:suggestion
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
|
||||
#### Naming styles ####
|
||||
|
||||
dotnet_naming_style.camel_case.capitalization = camel_case
|
||||
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
|
||||
# Symbol specifications
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal
|
||||
|
||||
dotnet_naming_symbols.const_private_field.applicable_kinds = field
|
||||
dotnet_naming_symbols.const_private_field.required_modifiers = const
|
||||
dotnet_naming_symbols.const_private_field.applicable_accessibilities = private
|
||||
|
||||
dotnet_naming_symbols.internal_field.applicable_kinds = field
|
||||
dotnet_naming_symbols.internal_field.applicable_accessibilities = internal
|
||||
|
||||
dotnet_naming_symbols.static_private_or_internal_field.required_modifiers = static
|
||||
dotnet_naming_symbols.static_private_or_internal_field.applicable_accessibilities = internal, private
|
||||
|
||||
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private
|
||||
|
||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal
|
||||
|
||||
# Naming rules
|
||||
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||
|
||||
|
||||
dotnet_naming_rule.static_private_or_internal_field_should_be_pascal_case.severity = none
|
||||
dotnet_naming_rule.static_private_or_internal_field_should_be_pascal_case.symbols = static_private_or_internal_field
|
||||
dotnet_naming_rule.static_private_or_internal_field_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.const_private_field_should_be_pascal_case.severity = warning
|
||||
dotnet_naming_rule.const_private_field_should_be_pascal_case.symbols = const_private_field
|
||||
dotnet_naming_rule.const_private_field_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.const_private_or_internal_field_should_be_pascal_case.severity = warning
|
||||
dotnet_naming_rule.const_private_or_internal_field_should_be_pascal_case.symbols = internal_field
|
||||
dotnet_naming_rule.const_private_or_internal_field_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.private_or_internal_field_should_be_camel_case.severity = warning
|
||||
dotnet_naming_rule.private_or_internal_field_should_be_camel_case.symbols = private_or_internal_field
|
||||
dotnet_naming_rule.private_or_internal_field_should_be_camel_case.style = camel_case
|
||||
|
||||
# Naming rules
|
||||
|
||||
#require a space before the colon for bases or interfaces in a type declaration
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
#require a space after a keyword in a control flow statement such as a for loop
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
#require a space before the colon for bases or interfaces in a type declaration
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
|
||||
#Formatting - wrapping options
|
||||
|
||||
#leave code block on single line
|
||||
csharp_preserve_single_line_blocks = true
|
||||
#leave statements and member declarations on the same line
|
||||
csharp_preserve_single_line_statements = true
|
||||
|
||||
#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
|
||||
#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
|
||||
; 4-column tab indentation
|
||||
[*.yaml]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
indent_size = 4
|
||||
|
||||
41
.travis.yml
41
.travis.yml
@@ -3,6 +3,8 @@
|
||||
|
||||
language: csharp
|
||||
mono: 6.4.0
|
||||
os: linux
|
||||
dist: xenial
|
||||
|
||||
jobs:
|
||||
include:
|
||||
@@ -12,17 +14,13 @@ jobs:
|
||||
if: tag IS present
|
||||
osx_image: xcode10
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- thirdparty/download
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- lua5.1
|
||||
- dpkg
|
||||
- zsync
|
||||
- markdown
|
||||
- imagemagick
|
||||
|
||||
# Environment variables
|
||||
env:
|
||||
@@ -35,11 +33,12 @@ env:
|
||||
# Run the NUnit tests
|
||||
script:
|
||||
- make all
|
||||
- test "$TRAVIS_OS_NAME" == "linux" && make check || echo "Skipping check"
|
||||
- test "$TRAVIS_OS_NAME" == "linux" && make check-scripts || echo "Skipping scripts check"
|
||||
- test "$TRAVIS_OS_NAME" == "linux" && make test || echo "Skipping tests"
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
||||
mono ~/.nuget/packages/nunit.consolerunner/3.11.1/tools/nunit3-console.exe --noresult OpenRA.Test.dll;
|
||||
- |
|
||||
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
||||
make check || travis_terminate 1;
|
||||
make check-scripts || travis_terminate 1;
|
||||
make test || travis_terminate 1;
|
||||
mono ~/.nuget/packages/nunit.consolerunner/3.11.1/tools/nunit3-console.exe --noresult OpenRA.Test.dll || travis_terminate 1;
|
||||
fi
|
||||
|
||||
# Only watch the development branch and tagged release.
|
||||
@@ -47,13 +46,14 @@ branches:
|
||||
only:
|
||||
- /^release-.*$/
|
||||
- /^playtest-.*$/
|
||||
- /^pkgtest-.*$/
|
||||
- /^devtest-.*$/
|
||||
- /^prep-.*$/
|
||||
- bleed
|
||||
|
||||
# Notify developers when build passed/failed.
|
||||
notifications:
|
||||
irc:
|
||||
if: repo = OpenRA/OpenRA
|
||||
template:
|
||||
- "%{repository}#%{build_number} %{commit} %{author}: %{message} %{build_url}"
|
||||
channels:
|
||||
@@ -62,26 +62,31 @@ notifications:
|
||||
skip_join: true
|
||||
|
||||
before_deploy:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
|
||||
wget https://mirrors.edge.kernel.org/ubuntu/pool/universe/n/nsis/nsis-common_3.04-1_all.deb;
|
||||
wget https://mirrors.edge.kernel.org/ubuntu/pool/universe/n/nsis/nsis_3.04-1_amd64.deb;
|
||||
sudo dpkg -i nsis-common_3.04-1_all.deb;
|
||||
sudo dpkg -i nsis_3.04-1_amd64.deb;
|
||||
cd packaging && ./update-wiki.sh ${TRAVIS_TAG} && cd ..;
|
||||
fi;
|
||||
- export PATH=$PATH:$HOME/usr/bin
|
||||
echo ${TRAVIS_REPO_SLUG};
|
||||
if [[ "${TRAVIS_REPO_SLUG}" == "OpenRA/OpenRA" ]]; then
|
||||
cd packaging && ./update-wiki.sh ${TRAVIS_TAG} && cd ..;
|
||||
fi;
|
||||
fi
|
||||
- export PATH=${PATH}:${HOME}/usr/bin
|
||||
- DOTVERSION=`echo ${TRAVIS_TAG} | sed "s/-/\\./g"`
|
||||
- cd packaging
|
||||
- mkdir build
|
||||
- ./package-all.sh ${TRAVIS_TAG} ${PWD}/build/
|
||||
- if [[ "${TRAVIS_REPO_SLUG}" == "OpenRA/OpenRA" ]]; then
|
||||
./upload-itch.sh ${TRAVIS_TAG} ${PWD}/build/;
|
||||
fi
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: "g/LU11f+mjqv+lj0sR1UliHwogXL4ofJUwoG5Dbqlvdf5UTLWytw/OWSCv8RGyuh10miyWeaoqHh1cn2C1IFhUEqN1sSeKKKOWOTvJ2FR5mzi9uH3d/MOBzG5icQ7Qh0fZ1YPz5RaJJhYu6bmfvA/1gD49GoaX2kxQL4J5cEBgg="
|
||||
token: ${GH_DEPLOY_API_KEY}
|
||||
file_glob: true
|
||||
file: build/*
|
||||
skip_cleanup: true
|
||||
on:
|
||||
all_branches: true
|
||||
tags: true
|
||||
repo: OpenRA/OpenRA
|
||||
|
||||
7
.vscode/extensions.json
vendored
Normal file
7
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"ms-dotnettools.csharp",
|
||||
"EditorConfig.EditorConfig",
|
||||
"ms-vscode.mono-debug"
|
||||
]
|
||||
}
|
||||
61
.vscode/launch.json
vendored
Normal file
61
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch (TD)",
|
||||
"type": "clr",
|
||||
"linux": {
|
||||
"type": "mono"
|
||||
},
|
||||
"osx": {
|
||||
"type": "mono"
|
||||
},
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=cnc"]
|
||||
},
|
||||
{
|
||||
"name": "Launch (RA)",
|
||||
"type": "clr",
|
||||
"linux": {
|
||||
"type": "mono"
|
||||
},
|
||||
"osx": {
|
||||
"type": "mono"
|
||||
},
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=ra"]
|
||||
},
|
||||
{
|
||||
"name": "Launch (D2k)",
|
||||
"type": "clr",
|
||||
"linux": {
|
||||
"type": "mono"
|
||||
},
|
||||
"osx": {
|
||||
"type": "mono"
|
||||
},
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=d2k"]
|
||||
},
|
||||
{
|
||||
"name": "Launch (TS)",
|
||||
"type": "clr",
|
||||
"linux": {
|
||||
"type": "mono"
|
||||
},
|
||||
"osx": {
|
||||
"type": "mono"
|
||||
},
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=ts"]
|
||||
},
|
||||
]
|
||||
}
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"omnisharp.enableRoslynAnalyzers": true
|
||||
}
|
||||
13
.vscode/tasks.json
vendored
Normal file
13
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "make",
|
||||
"args": ["all"],
|
||||
"windows": {
|
||||
"command": "make.cmd"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
9
AUTHORS
9
AUTHORS
@@ -40,6 +40,7 @@ Also thanks to:
|
||||
* Biofreak
|
||||
* Braxton Williams (Buddytex)
|
||||
* Brendan Gluth (Mechanical_Man)
|
||||
* Brent Gardner (bggardner)
|
||||
* Bryan Wilbur
|
||||
* Bugra Cuhadaroglu (BugraC)
|
||||
* Chris Cameron (Vesuvian)
|
||||
@@ -137,6 +138,7 @@ Also thanks to:
|
||||
* Sebastien Kerguen (xanax)
|
||||
* Shawn Collins (UberWaffe)
|
||||
* Simon Verbeke (Saticmotion)
|
||||
* Stuart McHattie (SDJMcHattie)
|
||||
* Taryn Hill (Phrohdoh)
|
||||
* Teemu Nieminen (Temeez)
|
||||
* Tim Mylemans (gecko)
|
||||
@@ -147,6 +149,7 @@ Also thanks to:
|
||||
* Tristan Mühlbacher (MicroBit)
|
||||
* UnknownProgrammer
|
||||
* Vladimir Komarov (VrKomarov)
|
||||
* Wojciech Walaszek (Voidwalker)
|
||||
* Wuschel
|
||||
|
||||
Using GNU FreeFont distributed under the GNU GPL
|
||||
@@ -179,6 +182,12 @@ Krueger and distributed under the GNU GPL terms.
|
||||
Using rix0rrr.BeaconLib developed by Rico Huijbers
|
||||
distributed under MIT License.
|
||||
|
||||
Using DiscordRichPresence developed by Lachee
|
||||
distributed under MIT License.
|
||||
|
||||
Using Json.NET developed by James Newton-King
|
||||
distributed under MIT License.
|
||||
|
||||
This site or product includes IP2Location LITE data
|
||||
available from http://www.ip2location.com.
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -21,7 +21,7 @@ Linux
|
||||
|
||||
Mono, version 5.18 or later, is required to compile OpenRA. You can add the [upstream mono repository](https://www.mono-project.com/download/stable/#download-lin) for your distro to obtain the latest version if your system packages are not sufficient.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
The default behaviour on the x86_64 architecture is to download several pre-compiled native libraries using the Nuget packaging manager. If you prefer to use system libraries, compile instead using `make TARGETPLATFORM=unix-generic`.
|
||||
|
||||
@@ -31,7 +31,7 @@ If you choose to use system libraries, or your system is not x86_64, you will ne
|
||||
* [OpenAL](http://kcat.strangesoft.net/openal.html)
|
||||
* [liblua 5.1](http://luabinaries.sourceforge.net/download.html)
|
||||
|
||||
Type `sudo make install` for system-wide installation. Run `sudo make install-linux-shortcuts` to get startup scripts, icons and desktop files. You can then run the Red Alert by executing the `openra-ra` command, the Dune 2000 mod by running the `openra-d2k` command and Tiberian Dawn by the `openra-cnc` command. Alternatively, you can also run these mods by clicking on their desktop shortcuts if you ran `sudo make install-linux-shortcuts`.
|
||||
Type `sudo make install` for system-wide installation. Run `sudo make install-linux-shortcuts` to get startup scripts, icons and desktop files. You can then run the Red Alert by executing the `openra-ra` command, the Dune 2000 mod by running the `openra-d2k` command and Tiberian Dawn by the `openra-cnc` command. Alternatively, you can also run these mods by clicking on their desktop shortcuts if you ran `sudo make install-linux-shortcuts`.
|
||||
|
||||
Arch Linux
|
||||
----------
|
||||
@@ -89,7 +89,7 @@ sudo zypper in mono-devel openal-soft freetype2 SDL2 lua51 xdg-utils zenity
|
||||
Red Hat Enterprise Linux (and rebuilds, e.g. CentOS)
|
||||
----------------------------------------------------
|
||||
|
||||
The EPEL repository is required in order for the following command to run properly.
|
||||
The EPEL repository is required in order for the following command to run properly.
|
||||
|
||||
```
|
||||
sudo yum install "pkgconfig(mono)" SDL2 freetype "lua = 5.1" openal-soft xdg-utils zenity
|
||||
|
||||
22
Makefile
22
Makefile
@@ -39,7 +39,7 @@
|
||||
WHITELISTED_OPENRA_ASSEMBLIES = OpenRA.Game.exe OpenRA.Utility.exe OpenRA.Platforms.Default.dll OpenRA.Mods.Common.dll OpenRA.Mods.Cnc.dll OpenRA.Mods.D2k.dll OpenRA.Game.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
|
||||
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
|
||||
@@ -158,7 +158,8 @@ version: VERSION mods/ra/mod.yaml mods/cnc/mod.yaml mods/d2k/mod.yaml mods/ts/mo
|
||||
rm $${i}.tmp; \
|
||||
done
|
||||
|
||||
install: core install-core
|
||||
install: core install-engine install-common-mod-files install-default-mods
|
||||
@$(CP) *.sh "$(DATA_INSTALL_DIR)"
|
||||
|
||||
install-linux-shortcuts: install-linux-scripts install-linux-icons install-linux-desktop
|
||||
|
||||
@@ -210,6 +211,8 @@ endif
|
||||
@$(INSTALL_PROGRAM) FuzzyLogicLibrary.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) Open.Nat.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) BeaconLib.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) DiscordRPC.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) Newtonsoft.Json.dll "$(DATA_INSTALL_DIR)"
|
||||
|
||||
install-common-mod-files:
|
||||
@-echo "Installing OpenRA common mod files to $(DATA_INSTALL_DIR)"
|
||||
@@ -228,19 +231,16 @@ install-default-mods:
|
||||
@$(INSTALL_PROGRAM) mods/d2k/OpenRA.Mods.D2k.dll "$(DATA_INSTALL_DIR)/mods/d2k"
|
||||
@$(CP_R) mods/modcontent "$(DATA_INSTALL_DIR)/mods/"
|
||||
|
||||
install-core: install-engine install-common-mod-files install-default-mods
|
||||
@$(CP) *.sh "$(DATA_INSTALL_DIR)"
|
||||
|
||||
install-linux-icons:
|
||||
for SIZE in 16x16 32x32 48x48 64x64 128x128; do \
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps"; \
|
||||
$(INSTALL_DATA) packaging/linux/icons/ra_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-ra.png"; \
|
||||
$(INSTALL_DATA) packaging/linux/icons/cnc_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-cnc.png"; \
|
||||
$(INSTALL_DATA) packaging/linux/icons/d2k_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-d2k.png"; \
|
||||
$(INSTALL_DATA) packaging/artwork/ra_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-ra.png"; \
|
||||
$(INSTALL_DATA) packaging/artwork/cnc_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-cnc.png"; \
|
||||
$(INSTALL_DATA) packaging/artwork/d2k_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-d2k.png"; \
|
||||
done
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps"
|
||||
$(INSTALL_DATA) packaging/linux/icons/ra_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-ra.svg"
|
||||
$(INSTALL_DATA) packaging/linux/icons/cnc_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-cnc.svg"
|
||||
$(INSTALL_DATA) packaging/artwork/ra_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-ra.svg"
|
||||
$(INSTALL_DATA) packaging/artwork/cnc_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-cnc.svg"
|
||||
|
||||
install-linux-desktop:
|
||||
@$(INSTALL_DIR) "$(DESTDIR)$(datadir)/applications"
|
||||
@@ -368,4 +368,4 @@ help:
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
.PHONY: check-scripts check test all core clean version install install-linux-shortcuts install-dependencies install-engine install-common-mod-files install-default-mods install-core install-linux-icons install-linux-desktop install-linux-mime install-linux-appdata install-man-page install-linux-scripts uninstall help
|
||||
.PHONY: check-scripts check test all core clean version install install-linux-shortcuts install-dependencies install-engine install-common-mod-files install-default-mods install-linux-icons install-linux-desktop install-linux-mime install-linux-appdata install-man-page install-linux-scripts uninstall help
|
||||
|
||||
@@ -185,8 +185,7 @@ namespace OpenRA.Activities
|
||||
/// </summary>
|
||||
internal void OnActorDisposeOuter(Actor self)
|
||||
{
|
||||
if (ChildActivity != null)
|
||||
ChildActivity.OnActorDisposeOuter(self);
|
||||
ChildActivity?.OnActorDisposeOuter(self);
|
||||
|
||||
OnActorDispose(self);
|
||||
}
|
||||
@@ -199,8 +198,7 @@ namespace OpenRA.Activities
|
||||
if (!IsInterruptible)
|
||||
return;
|
||||
|
||||
if (ChildActivity != null)
|
||||
ChildActivity.Cancel(self);
|
||||
ChildActivity?.Cancel(self);
|
||||
|
||||
// Directly mark activities that are queued and therefore didn't run yet as done
|
||||
State = State == ActivityState.Queued ? ActivityState.Done : ActivityState.Canceling;
|
||||
@@ -243,11 +241,9 @@ namespace OpenRA.Activities
|
||||
|
||||
Console.WriteLine(GetType().ToString().Split('.').Last());
|
||||
|
||||
if (ChildActivity != null)
|
||||
ChildActivity.PrintActivityTree(self, origin, level + 1);
|
||||
ChildActivity?.PrintActivityTree(self, origin, level + 1);
|
||||
|
||||
if (NextActivity != null)
|
||||
NextActivity.PrintActivityTree(self, origin, level);
|
||||
NextActivity?.PrintActivityTree(self, origin, level);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace OpenRA.Activities
|
||||
|
||||
public override bool Tick(Actor self)
|
||||
{
|
||||
if (a != null) a();
|
||||
a?.Invoke();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
@@ -68,9 +69,7 @@ namespace OpenRA
|
||||
{
|
||||
get
|
||||
{
|
||||
// TODO: Support non-zero pitch/roll in IFacing (IOrientation?)
|
||||
var facingValue = facing != null ? facing.Facing : 0;
|
||||
return new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(facingValue));
|
||||
return facing != null ? facing.Orientation : WRot.None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,14 +115,21 @@ namespace OpenRA
|
||||
|
||||
internal Actor(World world, string name, TypeDictionary initDict)
|
||||
{
|
||||
var duplicateInit = initDict.WithInterface<ISingleInstanceInit>().GroupBy(i => i.GetType())
|
||||
.FirstOrDefault(i => i.Count() > 1);
|
||||
|
||||
if (duplicateInit != null)
|
||||
throw new InvalidDataException("Duplicate initializer '{0}'".F(duplicateInit.Key.Name));
|
||||
|
||||
var init = new ActorInitializer(this, initDict);
|
||||
|
||||
readOnlyConditionCache = new ReadOnlyDictionary<string, int>(conditionCache);
|
||||
|
||||
World = world;
|
||||
ActorID = world.NextAID();
|
||||
if (initDict.Contains<OwnerInit>())
|
||||
Owner = init.Get<OwnerInit, Player>();
|
||||
var ownerInit = init.GetOrDefault<OwnerInit>();
|
||||
if (ownerInit != null)
|
||||
Owner = ownerInit.Value(world);
|
||||
|
||||
if (name != null)
|
||||
{
|
||||
@@ -171,7 +177,7 @@ namespace OpenRA
|
||||
SyncHashes = TraitsImplementing<ISync>().Select(sync => new SyncHash(sync)).ToArray();
|
||||
}
|
||||
|
||||
internal void Created()
|
||||
internal void Initialize(bool addToWorld = true)
|
||||
{
|
||||
created = true;
|
||||
|
||||
@@ -225,6 +231,9 @@ namespace OpenRA
|
||||
activity.Queue(CurrentActivity);
|
||||
CurrentActivity = activity;
|
||||
}
|
||||
|
||||
if (addToWorld)
|
||||
World.Add(this);
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
@@ -320,8 +329,7 @@ namespace OpenRA
|
||||
|
||||
public void CancelActivity()
|
||||
{
|
||||
if (CurrentActivity != null)
|
||||
CurrentActivity.Cancel(this);
|
||||
CurrentActivity?.Cancel(this);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
@@ -373,8 +381,7 @@ namespace OpenRA
|
||||
{
|
||||
// If CurrentActivity isn't null, run OnActorDisposeOuter in case some cleanups are needed.
|
||||
// This should be done before the FrameEndTask to avoid dependency issues.
|
||||
if (CurrentActivity != null)
|
||||
CurrentActivity.OnActorDisposeOuter(this);
|
||||
CurrentActivity?.OnActorDisposeOuter(this);
|
||||
|
||||
// Allow traits/activities to prevent a race condition when they depend on disposing the actor (e.g. Transforms)
|
||||
WillDispose = true;
|
||||
@@ -393,8 +400,7 @@ namespace OpenRA
|
||||
World.TraitDict.RemoveActor(this);
|
||||
Disposed = true;
|
||||
|
||||
if (luaInterface != null)
|
||||
luaInterface.Value.OnActorDestroyed();
|
||||
luaInterface?.Value.OnActorDestroyed();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -528,10 +534,16 @@ namespace OpenRA
|
||||
notify(this, readOnlyConditionCache);
|
||||
}
|
||||
|
||||
/// <summary>Grants a specified condition.</summary>
|
||||
/// <summary>
|
||||
/// Grants a specified condition if it is valid.
|
||||
/// Otherwise, just returns InvalidConditionToken.
|
||||
/// </summary>
|
||||
/// <returns>The token that is used to revoke this condition.</returns>
|
||||
public int GrantCondition(string condition)
|
||||
{
|
||||
if (string.IsNullOrEmpty(condition))
|
||||
return InvalidConditionToken;
|
||||
|
||||
var token = nextConditionToken++;
|
||||
conditionTokens.Add(token, condition);
|
||||
UpdateConditionState(condition, token, false);
|
||||
@@ -545,8 +557,7 @@ namespace OpenRA
|
||||
/// <returns>The invalid token ID.</returns>
|
||||
public int RevokeCondition(int token)
|
||||
{
|
||||
string condition;
|
||||
if (!conditionTokens.TryGetValue(token, out condition))
|
||||
if (!conditionTokens.TryGetValue(token, out var condition))
|
||||
throw new InvalidOperationException("Attempting to revoke condition with invalid token {0} for {1}.".F(token, this));
|
||||
|
||||
conditionTokens.Remove(token);
|
||||
@@ -579,8 +590,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
Actor a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out Actor a) || !right.TryGetClrValue(out Actor b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
|
||||
@@ -89,9 +89,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CPos a;
|
||||
CVec b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out CPos a) || !right.TryGetClrValue(out CVec b))
|
||||
throw new LuaException("Attempted to call CPos.Add(CPos, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
|
||||
|
||||
return new LuaCustomClrObject(a + b);
|
||||
@@ -99,21 +97,18 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CPos a;
|
||||
var rightType = right.WrappedClrType();
|
||||
if (!left.TryGetClrValue(out a))
|
||||
if (!left.TryGetClrValue(out CPos a))
|
||||
throw new LuaException("Attempted to call CPos.Subtract(CPos, (CPos|CVec)) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, rightType.Name));
|
||||
|
||||
if (rightType == typeof(CPos))
|
||||
{
|
||||
CPos b;
|
||||
right.TryGetClrValue(out b);
|
||||
right.TryGetClrValue(out CPos b);
|
||||
return new LuaCustomClrObject(a - b);
|
||||
}
|
||||
else if (rightType == typeof(CVec))
|
||||
{
|
||||
CVec b;
|
||||
right.TryGetClrValue(out b);
|
||||
right.TryGetClrValue(out CVec b);
|
||||
return new LuaCustomClrObject(a - b);
|
||||
}
|
||||
|
||||
@@ -122,8 +117,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CPos a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out CPos a) || !right.TryGetClrValue(out CPos b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
|
||||
@@ -75,8 +75,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CVec a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out CVec a) || !right.TryGetClrValue(out CVec b))
|
||||
throw new LuaException("Attempted to call CVec.Add(CVec, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
|
||||
|
||||
return new LuaCustomClrObject(a + b);
|
||||
@@ -84,8 +83,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CVec a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out CVec a) || !right.TryGetClrValue(out CVec b))
|
||||
throw new LuaException("Attempted to call CVec.Subtract(CVec, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
|
||||
|
||||
return new LuaCustomClrObject(a - b);
|
||||
@@ -98,8 +96,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CVec a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out CVec a) || !right.TryGetClrValue(out CVec b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
|
||||
@@ -90,8 +90,7 @@ namespace OpenRA
|
||||
public void CancelAsync()
|
||||
{
|
||||
lock (syncObject)
|
||||
if (wc != null)
|
||||
wc.CancelAsync();
|
||||
wc?.CancelAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,8 +125,7 @@ namespace OpenRA
|
||||
|
||||
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k, Func<K, V> createFn)
|
||||
{
|
||||
V ret;
|
||||
if (!d.TryGetValue(k, out ret))
|
||||
if (!d.TryGetValue(k, out var ret))
|
||||
d.Add(k, ret = createFn(k));
|
||||
return ret;
|
||||
}
|
||||
@@ -353,8 +352,7 @@ namespace OpenRA
|
||||
|
||||
public static int IntegerDivisionRoundingAwayFromZero(int dividend, int divisor)
|
||||
{
|
||||
int remainder;
|
||||
var quotient = Math.DivRem(dividend, divisor, out remainder);
|
||||
var quotient = Math.DivRem(dividend, divisor, out var remainder);
|
||||
if (remainder == 0)
|
||||
return quotient;
|
||||
return quotient + (Math.Sign(dividend) == Math.Sign(divisor) ? 1 : -1);
|
||||
@@ -405,8 +403,7 @@ namespace OpenRA
|
||||
// Check for a key conflict:
|
||||
if (d.ContainsKey(key))
|
||||
{
|
||||
List<string> dupKeyMessages;
|
||||
if (!dupKeys.TryGetValue(key, out dupKeyMessages))
|
||||
if (!dupKeys.TryGetValue(key, out var dupKeyMessages))
|
||||
{
|
||||
// Log the initial conflicting value already inserted:
|
||||
dupKeyMessages = new List<string>();
|
||||
|
||||
@@ -18,7 +18,6 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
|
||||
@@ -122,8 +121,7 @@ namespace OpenRA
|
||||
{
|
||||
ret = null;
|
||||
|
||||
MiniYaml yaml;
|
||||
if (!md.TryGetValue(yamlName, out yaml))
|
||||
if (!md.TryGetValue(yamlName, out var yaml))
|
||||
return false;
|
||||
|
||||
ret = GetValue(field.Name, field.FieldType, yaml, field);
|
||||
@@ -181,42 +179,36 @@ namespace OpenRA
|
||||
|
||||
public static object GetValue(string fieldName, Type fieldType, MiniYaml yaml, MemberInfo field)
|
||||
{
|
||||
var value = yaml.Value;
|
||||
if (value != null) value = value.Trim();
|
||||
var value = yaml.Value?.Trim();
|
||||
|
||||
if (fieldType == typeof(int))
|
||||
{
|
||||
int res;
|
||||
if (Exts.TryParseIntegerInvariant(value, out res))
|
||||
if (Exts.TryParseIntegerInvariant(value, out var res))
|
||||
return res;
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
else if (fieldType == typeof(ushort))
|
||||
{
|
||||
ushort res;
|
||||
if (ushort.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out res))
|
||||
if (ushort.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out var res))
|
||||
return res;
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
|
||||
if (fieldType == typeof(long))
|
||||
{
|
||||
long res;
|
||||
if (long.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out res))
|
||||
if (long.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out var res))
|
||||
return res;
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
else if (fieldType == typeof(float))
|
||||
{
|
||||
float res;
|
||||
if (value != null && float.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
|
||||
if (value != null && float.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var res))
|
||||
return res * (value.Contains('%') ? 0.01f : 1f);
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
else if (fieldType == typeof(decimal))
|
||||
{
|
||||
decimal res;
|
||||
if (value != null && decimal.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
|
||||
if (value != null && decimal.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var res))
|
||||
return res * (value.Contains('%') ? 0.01m : 1m);
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
@@ -228,16 +220,14 @@ namespace OpenRA
|
||||
}
|
||||
else if (fieldType == typeof(Color))
|
||||
{
|
||||
Color color;
|
||||
if (value != null && Color.TryParse(value, out color))
|
||||
if (value != null && Color.TryParse(value, out var color))
|
||||
return color;
|
||||
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
else if (fieldType == typeof(Hotkey))
|
||||
{
|
||||
Hotkey res;
|
||||
if (Hotkey.TryParse(value, out res))
|
||||
if (Hotkey.TryParse(value, out var res))
|
||||
return res;
|
||||
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
@@ -248,8 +238,7 @@ namespace OpenRA
|
||||
}
|
||||
else if (fieldType == typeof(WDist))
|
||||
{
|
||||
WDist res;
|
||||
if (WDist.TryParse(value, out res))
|
||||
if (WDist.TryParse(value, out var res))
|
||||
return res;
|
||||
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
@@ -261,8 +250,7 @@ namespace OpenRA
|
||||
var parts = value.Split(',');
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
WDist rx, ry, rz;
|
||||
if (WDist.TryParse(parts[0], out rx) && WDist.TryParse(parts[1], out ry) && WDist.TryParse(parts[2], out rz))
|
||||
if (WDist.TryParse(parts[0], out var rx) && WDist.TryParse(parts[1], out var ry) && WDist.TryParse(parts[2], out var rz))
|
||||
return new WVec(rx, ry, rz);
|
||||
}
|
||||
}
|
||||
@@ -282,8 +270,7 @@ namespace OpenRA
|
||||
|
||||
for (var i = 0; i < vecs.Length; ++i)
|
||||
{
|
||||
WDist rx, ry, rz;
|
||||
if (WDist.TryParse(parts[3 * i], out rx) && WDist.TryParse(parts[3 * i + 1], out ry) && WDist.TryParse(parts[3 * i + 2], out rz))
|
||||
if (WDist.TryParse(parts[3 * i], out var rx) && WDist.TryParse(parts[3 * i + 1], out var ry) && WDist.TryParse(parts[3 * i + 2], out var rz))
|
||||
vecs[i] = new WVec(rx, ry, rz);
|
||||
}
|
||||
|
||||
@@ -299,8 +286,7 @@ namespace OpenRA
|
||||
var parts = value.Split(',');
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
WDist rx, ry, rz;
|
||||
if (WDist.TryParse(parts[0], out rx) && WDist.TryParse(parts[1], out ry) && WDist.TryParse(parts[2], out rz))
|
||||
if (WDist.TryParse(parts[0], out var rx) && WDist.TryParse(parts[1], out var ry) && WDist.TryParse(parts[2], out var rz))
|
||||
return new WPos(rx, ry, rz);
|
||||
}
|
||||
}
|
||||
@@ -309,8 +295,7 @@ namespace OpenRA
|
||||
}
|
||||
else if (fieldType == typeof(WAngle))
|
||||
{
|
||||
int res;
|
||||
if (Exts.TryParseIntegerInvariant(value, out res))
|
||||
if (Exts.TryParseIntegerInvariant(value, out var res))
|
||||
return new WAngle(res);
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
@@ -321,8 +306,7 @@ namespace OpenRA
|
||||
var parts = value.Split(',');
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
int rr, rp, ry;
|
||||
if (Exts.TryParseIntegerInvariant(parts[0], out rr) && Exts.TryParseIntegerInvariant(parts[1], out rp) && Exts.TryParseIntegerInvariant(parts[2], out ry))
|
||||
if (Exts.TryParseIntegerInvariant(parts[0], out var rr) && Exts.TryParseIntegerInvariant(parts[1], out var rp) && Exts.TryParseIntegerInvariant(parts[2], out var ry))
|
||||
return new WRot(new WAngle(rr), new WAngle(rp), new WAngle(ry));
|
||||
}
|
||||
}
|
||||
@@ -361,8 +345,7 @@ namespace OpenRA
|
||||
var vecs = new CVec[parts.Length / 2];
|
||||
for (var i = 0; i < vecs.Length; i++)
|
||||
{
|
||||
int rx, ry;
|
||||
if (int.TryParse(parts[2 * i], out rx) && int.TryParse(parts[2 * i + 1], out ry))
|
||||
if (int.TryParse(parts[2 * i], out var rx) && int.TryParse(parts[2 * i + 1], out var ry))
|
||||
vecs[i] = new CVec(rx, ry);
|
||||
}
|
||||
|
||||
@@ -416,8 +399,7 @@ namespace OpenRA
|
||||
}
|
||||
else if (fieldType == typeof(bool))
|
||||
{
|
||||
bool result;
|
||||
if (bool.TryParse(value.ToLowerInvariant(), out result))
|
||||
if (bool.TryParse(value.ToLowerInvariant(), out var result))
|
||||
return result;
|
||||
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
@@ -510,8 +492,7 @@ namespace OpenRA
|
||||
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
float xx = 0;
|
||||
float yy = 0;
|
||||
float res;
|
||||
if (float.TryParse(parts[0].Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
|
||||
if (float.TryParse(parts[0].Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var res))
|
||||
xx = res * (parts[0].Contains('%') ? 0.01f : 1f);
|
||||
if (float.TryParse(parts[1].Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
|
||||
yy = res * (parts[1].Contains('%') ? 0.01f : 1f);
|
||||
@@ -525,13 +506,11 @@ namespace OpenRA
|
||||
if (value != null)
|
||||
{
|
||||
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
float z = 0;
|
||||
float.TryParse(parts[0], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out x);
|
||||
float.TryParse(parts[1], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out y);
|
||||
float.TryParse(parts[0], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var x);
|
||||
float.TryParse(parts[1], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var y);
|
||||
|
||||
// z component is optional for compatibility with older float2 definitions
|
||||
float z = 0;
|
||||
if (parts.Length > 2)
|
||||
float.TryParse(parts[2], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out z);
|
||||
|
||||
@@ -567,14 +546,16 @@ namespace OpenRA
|
||||
}
|
||||
else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return null;
|
||||
|
||||
var innerType = fieldType.GetGenericArguments().First();
|
||||
var innerValue = GetValue("Nullable<T>", innerType, value, field);
|
||||
return fieldType.GetConstructor(new[] { innerType }).Invoke(new[] { innerValue });
|
||||
}
|
||||
else if (fieldType == typeof(DateTime))
|
||||
{
|
||||
DateTime dt;
|
||||
if (DateTime.TryParseExact(value, "yyyy-MM-dd HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out dt))
|
||||
if (DateTime.TryParseExact(value, "yyyy-MM-dd HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var dt))
|
||||
return dt;
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
@@ -724,8 +705,7 @@ namespace OpenRA
|
||||
if (translations == null)
|
||||
return key;
|
||||
|
||||
string value;
|
||||
if (!translations.TryGetValue(key, out value))
|
||||
if (!translations.TryGetValue(key, out var value))
|
||||
return key;
|
||||
|
||||
return value;
|
||||
|
||||
@@ -15,7 +15,6 @@ using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
|
||||
@@ -67,15 +67,12 @@ namespace OpenRA.FileSystem
|
||||
return new Folder(resolvedPath);
|
||||
|
||||
// Children of another package require special handling
|
||||
IReadOnlyPackage parent;
|
||||
string subPath = null;
|
||||
if (TryGetPackageContaining(filename, out parent, out subPath))
|
||||
if (TryGetPackageContaining(filename, out var parent, out var subPath))
|
||||
return parent.OpenPackage(subPath, this);
|
||||
|
||||
// Try and open it normally
|
||||
IReadOnlyPackage package;
|
||||
var stream = Open(filename);
|
||||
if (TryParsePackage(stream, filename, out package))
|
||||
if (TryParsePackage(stream, filename, out var package))
|
||||
return package;
|
||||
|
||||
// No package loaders took ownership of the stream, so clean it up
|
||||
@@ -97,8 +94,7 @@ namespace OpenRA.FileSystem
|
||||
{
|
||||
name = name.Substring(1);
|
||||
|
||||
Manifest mod;
|
||||
if (!installedMods.TryGetValue(name, out mod))
|
||||
if (!installedMods.TryGetValue(name, out var mod))
|
||||
throw new InvalidOperationException("Could not load mod '{0}'. Available mods: {1}".F(name, installedMods.Keys.JoinWith(", ")));
|
||||
|
||||
package = mod.Package;
|
||||
@@ -122,8 +118,7 @@ namespace OpenRA.FileSystem
|
||||
|
||||
public void Mount(IReadOnlyPackage package, string explicitName = null)
|
||||
{
|
||||
var mountCount = 0;
|
||||
if (mountedPackages.TryGetValue(package, out mountCount))
|
||||
if (mountedPackages.TryGetValue(package, out var mountCount))
|
||||
{
|
||||
// Package is already mounted
|
||||
// Increment the mount count and bump up the file loading priority
|
||||
@@ -149,8 +144,7 @@ namespace OpenRA.FileSystem
|
||||
|
||||
public bool Unmount(IReadOnlyPackage package)
|
||||
{
|
||||
var mountCount = 0;
|
||||
if (!mountedPackages.TryGetValue(package, out mountCount))
|
||||
if (!mountedPackages.TryGetValue(package, out var mountCount))
|
||||
return false;
|
||||
|
||||
if (--mountCount <= 0)
|
||||
@@ -203,16 +197,12 @@ namespace OpenRA.FileSystem
|
||||
var package = fileIndex[filename]
|
||||
.LastOrDefault(x => x.Contains(filename));
|
||||
|
||||
if (package != null)
|
||||
return package.GetStream(filename);
|
||||
|
||||
return null;
|
||||
return package?.GetStream(filename);
|
||||
}
|
||||
|
||||
public Stream Open(string filename)
|
||||
{
|
||||
Stream s;
|
||||
if (!TryOpen(filename, out s))
|
||||
if (!TryOpen(filename, out var s))
|
||||
throw new FileNotFoundException("File not found: {0}".F(filename), filename);
|
||||
|
||||
return s;
|
||||
@@ -238,8 +228,7 @@ namespace OpenRA.FileSystem
|
||||
var explicitSplit = filename.IndexOf('|');
|
||||
if (explicitSplit > 0)
|
||||
{
|
||||
IReadOnlyPackage explicitPackage;
|
||||
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out explicitPackage))
|
||||
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
|
||||
{
|
||||
s = explicitPackage.GetStream(filename.Substring(explicitSplit + 1));
|
||||
if (s != null)
|
||||
@@ -274,12 +263,9 @@ namespace OpenRA.FileSystem
|
||||
{
|
||||
var explicitSplit = filename.IndexOf('|');
|
||||
if (explicitSplit > 0)
|
||||
{
|
||||
IReadOnlyPackage explicitPackage;
|
||||
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out explicitPackage))
|
||||
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
|
||||
if (explicitPackage.Contains(filename.Substring(explicitSplit + 1)))
|
||||
return true;
|
||||
}
|
||||
|
||||
return fileIndex.ContainsKey(filename);
|
||||
}
|
||||
@@ -293,8 +279,7 @@ namespace OpenRA.FileSystem
|
||||
if (explicitSplit < 0)
|
||||
return false;
|
||||
|
||||
IReadOnlyPackage explicitPackage;
|
||||
if (!explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out explicitPackage))
|
||||
if (!explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
|
||||
return false;
|
||||
|
||||
if (installedMods[modID].Package == explicitPackage)
|
||||
@@ -321,8 +306,7 @@ namespace OpenRA.FileSystem
|
||||
|
||||
if (parentPath.StartsWith("$", StringComparison.Ordinal))
|
||||
{
|
||||
Manifest mod;
|
||||
if (!installedMods.TryGetValue(parentPath.Substring(1), out mod))
|
||||
if (!installedMods.TryGetValue(parentPath.Substring(1), out var mod))
|
||||
return null;
|
||||
|
||||
if (!(mod.Package is Folder))
|
||||
|
||||
@@ -58,17 +58,15 @@ namespace OpenRA.FileSystem
|
||||
return new Folder(resolvedPath);
|
||||
|
||||
// Zip files loaded from Folders (and *only* from Folders) can be read-write
|
||||
IReadWritePackage readWritePackage;
|
||||
if (ZipFileLoader.TryParseReadWritePackage(resolvedPath, out readWritePackage))
|
||||
if (ZipFileLoader.TryParseReadWritePackage(resolvedPath, out var readWritePackage))
|
||||
return readWritePackage;
|
||||
|
||||
// Other package types can be loaded normally
|
||||
IReadOnlyPackage package;
|
||||
var s = GetStream(filename);
|
||||
if (s == null)
|
||||
return null;
|
||||
|
||||
if (context.TryParsePackage(s, filename, out package))
|
||||
if (context.TryParsePackage(s, filename, out var package))
|
||||
return package;
|
||||
|
||||
s.Dispose();
|
||||
|
||||
@@ -14,7 +14,6 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.FileSystem
|
||||
{
|
||||
@@ -67,8 +66,7 @@ namespace OpenRA.FileSystem
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (pkg != null)
|
||||
pkg.Close();
|
||||
pkg?.Close();
|
||||
}
|
||||
|
||||
public IReadOnlyPackage OpenPackage(string filename, FileSystem context)
|
||||
@@ -82,12 +80,11 @@ namespace OpenRA.FileSystem
|
||||
return new ZipFolder(this, filename);
|
||||
|
||||
// Other package types can be loaded normally
|
||||
IReadOnlyPackage package;
|
||||
var s = GetStream(filename);
|
||||
if (s == null)
|
||||
return null;
|
||||
|
||||
if (context.TryParsePackage(s, filename, out package))
|
||||
if (context.TryParsePackage(s, filename, out var package))
|
||||
return package;
|
||||
|
||||
s.Dispose();
|
||||
|
||||
@@ -75,12 +75,12 @@ namespace OpenRA
|
||||
static string TimestampedFilename(bool includemilliseconds = false)
|
||||
{
|
||||
var format = includemilliseconds ? "yyyy-MM-ddTHHmmssfffZ" : "yyyy-MM-ddTHHmmssZ";
|
||||
return "OpenRA-" + DateTime.UtcNow.ToString(format, CultureInfo.InvariantCulture);
|
||||
return ModData.Manifest.Id + "-" + DateTime.UtcNow.ToString(format, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
static void JoinInner(OrderManager om)
|
||||
{
|
||||
if (OrderManager != null) OrderManager.Dispose();
|
||||
OrderManager?.Dispose();
|
||||
OrderManager = om;
|
||||
lastConnectionState = ConnectionState.PreConnecting;
|
||||
ConnectionStateChanged(OrderManager);
|
||||
@@ -154,8 +154,7 @@ namespace OpenRA
|
||||
internal static void StartGame(string mapUID, WorldType type)
|
||||
{
|
||||
// Dispose of the old world before creating a new one.
|
||||
if (worldRenderer != null)
|
||||
worldRenderer.Dispose();
|
||||
worldRenderer?.Dispose();
|
||||
|
||||
Cursor.SetCursor(null);
|
||||
BeforeGameStart();
|
||||
@@ -330,11 +329,9 @@ namespace OpenRA
|
||||
Log.Write("graphics", "{0}", e);
|
||||
Console.WriteLine("Renderer initialization failed. Check graphics.log for details.");
|
||||
|
||||
if (Renderer != null)
|
||||
Renderer.Dispose();
|
||||
Renderer?.Dispose();
|
||||
|
||||
if (Sound != null)
|
||||
Sound.Dispose();
|
||||
Sound?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,8 +352,7 @@ namespace OpenRA
|
||||
|
||||
ExternalMods = new ExternalMods();
|
||||
|
||||
Manifest currentMod;
|
||||
if (modID != null && Mods.TryGetValue(modID, out currentMod))
|
||||
if (modID != null && Mods.TryGetValue(modID, out _))
|
||||
{
|
||||
var launchPath = args.GetValue("Engine.LaunchPath", Assembly.GetEntryAssembly().Location);
|
||||
|
||||
@@ -367,8 +363,7 @@ namespace OpenRA
|
||||
|
||||
ExternalMods.Register(Mods[modID], launchPath, ModRegistration.User);
|
||||
|
||||
ExternalMod activeMod;
|
||||
if (ExternalMods.TryGetValue(ExternalMod.MakeKey(Mods[modID]), out activeMod))
|
||||
if (ExternalMods.TryGetValue(ExternalMod.MakeKey(Mods[modID]), out var activeMod))
|
||||
ExternalMods.ClearInvalidRegistrations(activeMod, ModRegistration.User);
|
||||
}
|
||||
|
||||
@@ -390,13 +385,10 @@ namespace OpenRA
|
||||
|
||||
Ui.ResetAll();
|
||||
|
||||
if (worldRenderer != null)
|
||||
worldRenderer.Dispose();
|
||||
worldRenderer?.Dispose();
|
||||
worldRenderer = null;
|
||||
if (server != null)
|
||||
server.Shutdown();
|
||||
if (OrderManager != null)
|
||||
OrderManager.Dispose();
|
||||
server?.Shutdown();
|
||||
OrderManager?.Dispose();
|
||||
|
||||
if (ModData != null)
|
||||
{
|
||||
@@ -432,8 +424,7 @@ namespace OpenRA
|
||||
var grid = ModData.Manifest.Contains<MapGrid>() ? ModData.Manifest.Get<MapGrid>() : null;
|
||||
Renderer.InitializeDepthBuffer(grid);
|
||||
|
||||
if (Cursor != null)
|
||||
Cursor.Dispose();
|
||||
Cursor?.Dispose();
|
||||
|
||||
Cursor = new CursorManager(ModData.CursorProvider);
|
||||
|
||||
@@ -442,13 +433,13 @@ namespace OpenRA
|
||||
PerfHistory.Items["render_world"].HasNormalTick = false;
|
||||
PerfHistory.Items["render_widgets"].HasNormalTick = false;
|
||||
PerfHistory.Items["render_flip"].HasNormalTick = false;
|
||||
PerfHistory.Items["terrain_lighting"].HasNormalTick = false;
|
||||
|
||||
JoinLocal();
|
||||
|
||||
try
|
||||
{
|
||||
if (discoverNat != null)
|
||||
discoverNat.Wait();
|
||||
discoverNat?.Wait();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -611,8 +602,7 @@ namespace OpenRA
|
||||
Sync.RunUnsynced(Settings.Debug.SyncCheckUnsyncedCode, world, () => world.TickRender(worldRenderer));
|
||||
}
|
||||
|
||||
if (benchmark != null)
|
||||
benchmark.Tick(LocalTick);
|
||||
benchmark?.Tick(LocalTick);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -710,6 +700,7 @@ namespace OpenRA
|
||||
PerfHistory.Items["render_world"].Tick();
|
||||
PerfHistory.Items["render_widgets"].Tick();
|
||||
PerfHistory.Items["render_flip"].Tick();
|
||||
PerfHistory.Items["terrain_lighting"].Tick();
|
||||
}
|
||||
|
||||
static void Loop()
|
||||
@@ -836,12 +827,10 @@ namespace OpenRA
|
||||
finally
|
||||
{
|
||||
// Ensure that the active replay is properly saved
|
||||
if (OrderManager != null)
|
||||
OrderManager.Dispose();
|
||||
OrderManager?.Dispose();
|
||||
}
|
||||
|
||||
if (worldRenderer != null)
|
||||
worldRenderer.Dispose();
|
||||
worldRenderer?.Dispose();
|
||||
ModData.Dispose();
|
||||
ChromeProvider.Deinitialize();
|
||||
|
||||
@@ -880,8 +869,7 @@ namespace OpenRA
|
||||
|
||||
public static void Disconnect()
|
||||
{
|
||||
if (OrderManager.World != null)
|
||||
OrderManager.World.TraitDict.PrintReport();
|
||||
OrderManager.World?.TraitDict.PrintReport();
|
||||
|
||||
OrderManager.Dispose();
|
||||
CloseServer();
|
||||
@@ -890,8 +878,7 @@ namespace OpenRA
|
||||
|
||||
public static void CloseServer()
|
||||
{
|
||||
if (server != null)
|
||||
server.Shutdown();
|
||||
server?.Shutdown();
|
||||
}
|
||||
|
||||
public static T CreateObject<T>(string name)
|
||||
|
||||
@@ -133,9 +133,7 @@ namespace OpenRA
|
||||
/// <summary>Gets the player information for the specified runtime player instance.</summary>
|
||||
public Player GetPlayer(OpenRA.Player runtimePlayer)
|
||||
{
|
||||
Player player;
|
||||
|
||||
playersByRuntime.TryGetValue(runtimePlayer, out player);
|
||||
playersByRuntime.TryGetValue(runtimePlayer, out var player);
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace OpenRA
|
||||
public class ActorInfo
|
||||
{
|
||||
public const string AbstractActorPrefix = "^";
|
||||
public const char TraitInstanceSeparator = '@';
|
||||
|
||||
/// <summary>
|
||||
/// The actor name can be anything, but the sprites used in the Render*: traits default to this one.
|
||||
@@ -32,7 +33,7 @@ namespace OpenRA
|
||||
/// </summary>
|
||||
public readonly string Name;
|
||||
readonly TypeDictionary traits = new TypeDictionary();
|
||||
List<ITraitInfo> constructOrderCache = null;
|
||||
List<TraitInfo> constructOrderCache = null;
|
||||
|
||||
public ActorInfo(ObjectCreator creator, string name, MiniYaml node)
|
||||
{
|
||||
@@ -46,7 +47,7 @@ namespace OpenRA
|
||||
{
|
||||
// HACK: The linter does not want to crash when a trait doesn't exist but only print an error instead
|
||||
// LoadTraitInfo will only return null to signal us to abort here if the linter is running
|
||||
var trait = LoadTraitInfo(creator, t.Key.Split('@')[0], t.Value);
|
||||
var trait = LoadTraitInfo(creator, t.Key, t.Value);
|
||||
if (trait != null)
|
||||
traits.Add(trait);
|
||||
}
|
||||
@@ -64,7 +65,7 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
public ActorInfo(string name, params ITraitInfo[] traitInfos)
|
||||
public ActorInfo(string name, params TraitInfo[] traitInfos)
|
||||
{
|
||||
Name = name;
|
||||
foreach (var t in traitInfos)
|
||||
@@ -72,7 +73,7 @@ namespace OpenRA
|
||||
traits.TrimExcess();
|
||||
}
|
||||
|
||||
static ITraitInfo LoadTraitInfo(ObjectCreator creator, string traitName, MiniYaml my)
|
||||
static TraitInfo LoadTraitInfo(ObjectCreator creator, string traitName, MiniYaml my)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(my.Value))
|
||||
throw new YamlException("Junk value `{0}` on trait node {1}"
|
||||
@@ -80,12 +81,16 @@ namespace OpenRA
|
||||
|
||||
// HACK: The linter does not want to crash when a trait doesn't exist but only print an error instead
|
||||
// ObjectCreator will only return null to signal us to abort here if the linter is running
|
||||
var info = creator.CreateObject<ITraitInfo>(traitName + "Info");
|
||||
var traitInstance = traitName.Split(TraitInstanceSeparator);
|
||||
var info = creator.CreateObject<TraitInfo>(traitInstance[0] + "Info");
|
||||
if (info == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
if (traitInstance.Length > 1)
|
||||
info.GetType().GetField("InstanceName").SetValue(info, traitInstance[1]);
|
||||
|
||||
FieldLoader.Load(info, my);
|
||||
}
|
||||
catch (FieldLoader.MissingFieldsException e)
|
||||
@@ -97,12 +102,12 @@ namespace OpenRA
|
||||
return info;
|
||||
}
|
||||
|
||||
public IEnumerable<ITraitInfo> TraitsInConstructOrder()
|
||||
public IEnumerable<TraitInfo> TraitsInConstructOrder()
|
||||
{
|
||||
if (constructOrderCache != null)
|
||||
return constructOrderCache;
|
||||
|
||||
var source = traits.WithInterface<ITraitInfo>().Select(i => new
|
||||
var source = traits.WithInterface<TraitInfo>().Select(i => new
|
||||
{
|
||||
Trait = i,
|
||||
Type = i.GetType(),
|
||||
@@ -148,7 +153,7 @@ namespace OpenRA
|
||||
return constructOrderCache;
|
||||
}
|
||||
|
||||
public static IEnumerable<Type> PrerequisitesOf(ITraitInfo info)
|
||||
public static IEnumerable<Type> PrerequisitesOf(TraitInfo info)
|
||||
{
|
||||
return info
|
||||
.GetType()
|
||||
|
||||
@@ -41,8 +41,7 @@ namespace OpenRA.GameRules
|
||||
|
||||
public void Load(IReadOnlyFileSystem fileSystem)
|
||||
{
|
||||
Stream stream;
|
||||
if (!fileSystem.TryOpen(Filename, out stream))
|
||||
if (!fileSystem.TryOpen(Filename, out var stream))
|
||||
return;
|
||||
|
||||
try
|
||||
@@ -50,8 +49,7 @@ namespace OpenRA.GameRules
|
||||
Exists = true;
|
||||
foreach (var loader in Game.ModData.SoundLoaders)
|
||||
{
|
||||
ISoundFormat soundFormat;
|
||||
if (loader.TryParseSound(stream, out soundFormat))
|
||||
if (loader.TryParseSound(stream, out var soundFormat))
|
||||
{
|
||||
Length = (int)soundFormat.LengthInSeconds;
|
||||
soundFormat.Dispose();
|
||||
|
||||
@@ -24,8 +24,8 @@ namespace OpenRA.GameRules
|
||||
public int[] DamageModifiers;
|
||||
public int[] InaccuracyModifiers;
|
||||
public int[] RangeModifiers;
|
||||
public int Facing;
|
||||
public Func<int> CurrentMuzzleFacing;
|
||||
public WAngle Facing;
|
||||
public Func<WAngle> CurrentMuzzleFacing;
|
||||
public WPos Source;
|
||||
public Func<WPos> CurrentSource;
|
||||
public Actor SourceActor;
|
||||
@@ -38,6 +38,8 @@ namespace OpenRA.GameRules
|
||||
public WeaponInfo Weapon;
|
||||
public int[] DamageModifiers = { };
|
||||
public WPos? Source;
|
||||
public WRot ImpactOrientation;
|
||||
public WPos ImpactPosition;
|
||||
public Actor SourceActor;
|
||||
public Target WeaponTarget;
|
||||
|
||||
@@ -45,6 +47,7 @@ namespace OpenRA.GameRules
|
||||
{
|
||||
Weapon = args.Weapon;
|
||||
DamageModifiers = args.DamageModifiers;
|
||||
ImpactPosition = args.PassiveTarget;
|
||||
Source = args.Source;
|
||||
SourceActor = args.SourceActor;
|
||||
WeaponTarget = args.GuidedTarget;
|
||||
@@ -102,6 +105,12 @@ namespace OpenRA.GameRules
|
||||
[Desc("What types of targets are unaffected.", "Overrules ValidTargets.")]
|
||||
public readonly BitSet<TargetableType> InvalidTargets;
|
||||
|
||||
static readonly BitSet<TargetableType> TargetTypeAir = new BitSet<TargetableType>("Air");
|
||||
|
||||
[Desc("If weapon is not directly targeting an actor and targeted position is above this altitude,",
|
||||
"the weapon will ignore terrain target types and only check TargetTypeAir for validity.")]
|
||||
public readonly WDist AirThreshold = new WDist(128);
|
||||
|
||||
[Desc("Delay in ticks between firing shots from the same ammo magazine. If one entry, it will be used for all bursts.",
|
||||
"If multiple entries, their number needs to match Burst - 1.")]
|
||||
public readonly int[] BurstDelays = { 5 };
|
||||
@@ -128,8 +137,7 @@ namespace OpenRA.GameRules
|
||||
|
||||
static object LoadProjectile(MiniYaml yaml)
|
||||
{
|
||||
MiniYaml proj;
|
||||
if (!yaml.ToDictionary().TryGetValue("Projectile", out proj))
|
||||
if (!yaml.ToDictionary().TryGetValue("Projectile", out var proj))
|
||||
return null;
|
||||
var ret = Game.CreateObject<IProjectileInfo>(proj.Value + "Info");
|
||||
FieldLoader.Load(ret, proj);
|
||||
@@ -165,6 +173,10 @@ namespace OpenRA.GameRules
|
||||
|
||||
if (target.Type == TargetType.Terrain)
|
||||
{
|
||||
var dat = world.Map.DistanceAboveTerrain(target.CenterPosition);
|
||||
if (dat > AirThreshold)
|
||||
return IsValidTarget(TargetTypeAir);
|
||||
|
||||
var cell = world.Map.CellContaining(target.CenterPosition);
|
||||
if (!world.Map.Contains(cell))
|
||||
return false;
|
||||
|
||||
@@ -54,12 +54,12 @@ namespace OpenRA.Graphics
|
||||
|
||||
public IRenderable[] Render(WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale)
|
||||
{
|
||||
var imageRenderable = new SpriteRenderable(Image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, scale, IsDecoration);
|
||||
var imageRenderable = new SpriteRenderable(Image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, scale, IsDecoration, CurrentSequence.IgnoreWorldTint);
|
||||
|
||||
if (CurrentSequence.ShadowStart >= 0)
|
||||
{
|
||||
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
|
||||
var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, scale, true);
|
||||
var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, scale, true, CurrentSequence.IgnoreWorldTint);
|
||||
return new IRenderable[] { shadowRenderable, imageRenderable };
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
frame = CurrentSequence.Length - 1;
|
||||
tickFunc = () => { };
|
||||
if (after != null) after();
|
||||
after?.Invoke();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,10 +54,10 @@ namespace OpenRA.Graphics
|
||||
|
||||
public static IReadOnlyDictionary<string, Collection> Collections { get; private set; }
|
||||
static Dictionary<string, Collection> collections;
|
||||
static Dictionary<string, Pair<Sheet, int>> cachedSheets;
|
||||
static Dictionary<string, (Sheet Sheet, int Density)> cachedSheets;
|
||||
static Dictionary<string, Dictionary<string, Sprite>> cachedSprites;
|
||||
static Dictionary<string, Sprite[]> cachedPanelSprites;
|
||||
static Dictionary<Collection, Pair<Sheet, int>> cachedCollectionSheets;
|
||||
static Dictionary<Collection, (Sheet Sheet, int)> cachedCollectionSheets;
|
||||
|
||||
static IReadOnlyFileSystem fileSystem;
|
||||
static float dpiScale = 1;
|
||||
@@ -72,10 +72,10 @@ namespace OpenRA.Graphics
|
||||
|
||||
fileSystem = modData.DefaultFileSystem;
|
||||
collections = new Dictionary<string, Collection>();
|
||||
cachedSheets = new Dictionary<string, Pair<Sheet, int>>();
|
||||
cachedSheets = new Dictionary<string, (Sheet, int)>();
|
||||
cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>();
|
||||
cachedPanelSprites = new Dictionary<string, Sprite[]>();
|
||||
cachedCollectionSheets = new Dictionary<Collection, Pair<Sheet, int>>();
|
||||
cachedCollectionSheets = new Dictionary<Collection, (Sheet, int)>();
|
||||
|
||||
Collections = new ReadOnlyDictionary<string, Collection>(collections);
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
if (cachedSheets != null)
|
||||
foreach (var sheet in cachedSheets.Values)
|
||||
sheet.First.Dispose();
|
||||
sheet.Sheet.Dispose();
|
||||
|
||||
collections = null;
|
||||
cachedSheets = null;
|
||||
@@ -108,12 +108,10 @@ namespace OpenRA.Graphics
|
||||
collections.Add(name, FieldLoader.Load<Collection>(yaml));
|
||||
}
|
||||
|
||||
static Pair<Sheet, int> SheetForCollection(Collection c)
|
||||
static (Sheet Sheet, int Density) SheetForCollection(Collection c)
|
||||
{
|
||||
Pair<Sheet, int> sheetDensity;
|
||||
|
||||
// Outer cache avoids recalculating image names
|
||||
if (!cachedCollectionSheets.TryGetValue(c, out sheetDensity))
|
||||
if (!cachedCollectionSheets.TryGetValue(c, out (Sheet, int) sheetDensity))
|
||||
{
|
||||
var image = c.Image;
|
||||
var density = 1;
|
||||
@@ -137,7 +135,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
sheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear;
|
||||
|
||||
sheetDensity = Pair.New(sheet, density);
|
||||
sheetDensity = (sheet, density);
|
||||
cachedSheets.Add(image, sheetDensity);
|
||||
}
|
||||
|
||||
@@ -153,20 +151,16 @@ namespace OpenRA.Graphics
|
||||
return null;
|
||||
|
||||
// Cached sprite
|
||||
Dictionary<string, Sprite> cachedCollection;
|
||||
Sprite sprite;
|
||||
if (cachedSprites.TryGetValue(collectionName, out cachedCollection) && cachedCollection.TryGetValue(imageName, out sprite))
|
||||
if (cachedSprites.TryGetValue(collectionName, out var cachedCollection) && cachedCollection.TryGetValue(imageName, out var sprite))
|
||||
return sprite;
|
||||
|
||||
Collection collection;
|
||||
if (!collections.TryGetValue(collectionName, out collection))
|
||||
if (!collections.TryGetValue(collectionName, out var collection))
|
||||
{
|
||||
Log.Write("debug", "Could not find collection '{0}'", collectionName);
|
||||
return null;
|
||||
}
|
||||
|
||||
Rectangle mi;
|
||||
if (!collection.Regions.TryGetValue(imageName, out mi))
|
||||
if (!collection.Regions.TryGetValue(imageName, out var mi))
|
||||
return null;
|
||||
|
||||
// Cache the sprite
|
||||
@@ -177,7 +171,7 @@ namespace OpenRA.Graphics
|
||||
cachedSprites.Add(collectionName, cachedCollection);
|
||||
}
|
||||
|
||||
var image = new Sprite(sheetDensity.First, sheetDensity.Second * mi, TextureChannel.RGBA, 1f / sheetDensity.Second);
|
||||
var image = new Sprite(sheetDensity.Sheet, sheetDensity.Density * mi, TextureChannel.RGBA, 1f / sheetDensity.Density);
|
||||
cachedCollection.Add(imageName, image);
|
||||
|
||||
return image;
|
||||
@@ -189,12 +183,10 @@ namespace OpenRA.Graphics
|
||||
return null;
|
||||
|
||||
// Cached sprite
|
||||
Sprite[] cachedSprites;
|
||||
if (cachedPanelSprites.TryGetValue(collectionName, out cachedSprites))
|
||||
if (cachedPanelSprites.TryGetValue(collectionName, out var cachedSprites))
|
||||
return cachedSprites;
|
||||
|
||||
Collection collection;
|
||||
if (!collections.TryGetValue(collectionName, out collection))
|
||||
if (!collections.TryGetValue(collectionName, out var collection))
|
||||
{
|
||||
Log.Write("debug", "Could not find collection '{0}'", collectionName);
|
||||
return null;
|
||||
@@ -214,20 +206,20 @@ namespace OpenRA.Graphics
|
||||
var pr = collection.PanelRegion;
|
||||
var ps = collection.PanelSides;
|
||||
|
||||
var sides = new[]
|
||||
var sides = new (PanelSides PanelSides, Rectangle Bounds)[]
|
||||
{
|
||||
Pair.New(PanelSides.Top | PanelSides.Left, new Rectangle(pr[0], pr[1], pr[2], pr[3])),
|
||||
Pair.New(PanelSides.Top, new Rectangle(pr[0] + pr[2], pr[1], pr[4], pr[3])),
|
||||
Pair.New(PanelSides.Top | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1], pr[6], pr[3])),
|
||||
Pair.New(PanelSides.Left, new Rectangle(pr[0], pr[1] + pr[3], pr[2], pr[5])),
|
||||
Pair.New(PanelSides.Center, new Rectangle(pr[0] + pr[2], pr[1] + pr[3], pr[4], pr[5])),
|
||||
Pair.New(PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3], pr[6], pr[5])),
|
||||
Pair.New(PanelSides.Bottom | PanelSides.Left, new Rectangle(pr[0], pr[1] + pr[3] + pr[5], pr[2], pr[7])),
|
||||
Pair.New(PanelSides.Bottom, new Rectangle(pr[0] + pr[2], pr[1] + pr[3] + pr[5], pr[4], pr[7])),
|
||||
Pair.New(PanelSides.Bottom | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3] + pr[5], pr[6], pr[7]))
|
||||
(PanelSides.Top | PanelSides.Left, new Rectangle(pr[0], pr[1], pr[2], pr[3])),
|
||||
(PanelSides.Top, new Rectangle(pr[0] + pr[2], pr[1], pr[4], pr[3])),
|
||||
(PanelSides.Top | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1], pr[6], pr[3])),
|
||||
(PanelSides.Left, new Rectangle(pr[0], pr[1] + pr[3], pr[2], pr[5])),
|
||||
(PanelSides.Center, new Rectangle(pr[0] + pr[2], pr[1] + pr[3], pr[4], pr[5])),
|
||||
(PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3], pr[6], pr[5])),
|
||||
(PanelSides.Bottom | PanelSides.Left, new Rectangle(pr[0], pr[1] + pr[3] + pr[5], pr[2], pr[7])),
|
||||
(PanelSides.Bottom, new Rectangle(pr[0] + pr[2], pr[1] + pr[3] + pr[5], pr[4], pr[7])),
|
||||
(PanelSides.Bottom | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3] + pr[5], pr[6], pr[7]))
|
||||
};
|
||||
|
||||
sprites = sides.Select(x => ps.HasSide(x.First) ? new Sprite(sheetDensity.First, sheetDensity.Second * x.Second, TextureChannel.RGBA, 1f / sheetDensity.Second) : null)
|
||||
sprites = sides.Select(x => ps.HasSide(x.PanelSides) ? new Sprite(sheetDensity.Sheet, sheetDensity.Density * x.Bounds, TextureChannel.RGBA, 1f / sheetDensity.Density) : null)
|
||||
.ToArray();
|
||||
}
|
||||
else
|
||||
@@ -256,8 +248,7 @@ namespace OpenRA.Graphics
|
||||
if (string.IsNullOrEmpty(collectionName))
|
||||
return new Size(0, 0);
|
||||
|
||||
Collection collection;
|
||||
if (!collections.TryGetValue(collectionName, out collection))
|
||||
if (!collections.TryGetValue(collectionName, out var collection))
|
||||
{
|
||||
Log.Write("debug", "Could not find collection '{0}'", collectionName);
|
||||
return new Size(0, 0);
|
||||
|
||||
@@ -47,15 +47,13 @@ namespace OpenRA.Graphics
|
||||
|
||||
if (d.ContainsKey("X"))
|
||||
{
|
||||
int x;
|
||||
Exts.TryParseIntegerInvariant(d["X"].Value, out x);
|
||||
Exts.TryParseIntegerInvariant(d["X"].Value, out var x);
|
||||
Hotspot = Hotspot.WithX(x);
|
||||
}
|
||||
|
||||
if (d.ContainsKey("Y"))
|
||||
{
|
||||
int y;
|
||||
Exts.TryParseIntegerInvariant(d["Y"].Value, out y);
|
||||
Exts.TryParseIntegerInvariant(d["Y"].Value, out var y);
|
||||
Hotspot = Hotspot.WithY(y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,19 +38,16 @@ namespace OpenRA.Graphics
|
||||
|
||||
public IPalette GetPalette(string name)
|
||||
{
|
||||
MutablePalette mutable;
|
||||
if (modifiablePalettes.TryGetValue(name, out mutable))
|
||||
if (modifiablePalettes.TryGetValue(name, out var mutable))
|
||||
return mutable.AsReadOnly();
|
||||
ImmutablePalette immutable;
|
||||
if (palettes.TryGetValue(name, out immutable))
|
||||
if (palettes.TryGetValue(name, out var immutable))
|
||||
return immutable;
|
||||
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
|
||||
}
|
||||
|
||||
public int GetPaletteIndex(string name)
|
||||
{
|
||||
int ret;
|
||||
if (!indices.TryGetValue(name, out ret))
|
||||
if (!indices.TryGetValue(name, out var ret))
|
||||
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
@@ -19,12 +18,12 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public readonly IModel Model;
|
||||
public readonly Func<WVec> OffsetFunc;
|
||||
public readonly Func<IEnumerable<WRot>> RotationFunc;
|
||||
public readonly Func<WRot> RotationFunc;
|
||||
public readonly Func<bool> DisableFunc;
|
||||
public readonly Func<uint> FrameFunc;
|
||||
public readonly bool ShowShadow;
|
||||
|
||||
public ModelAnimation(IModel model, Func<WVec> offset, Func<IEnumerable<WRot>> rotation, Func<bool> disable, Func<uint> frame, bool showshadow)
|
||||
public ModelAnimation(IModel model, Func<WVec> offset, Func<WRot> rotation, Func<bool> disable, Func<uint> frame, bool showshadow)
|
||||
{
|
||||
Model = model;
|
||||
OffsetFunc = offset;
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
readonly Dictionary<Sheet, IFrameBuffer> mappedBuffers = new Dictionary<Sheet, IFrameBuffer>();
|
||||
readonly Stack<KeyValuePair<Sheet, IFrameBuffer>> unmappedBuffers = new Stack<KeyValuePair<Sheet, IFrameBuffer>>();
|
||||
readonly List<Pair<Sheet, Action>> doRender = new List<Pair<Sheet, Action>>();
|
||||
readonly List<(Sheet Sheet, Action Func)> doRender = new List<(Sheet, Action)>();
|
||||
|
||||
SheetBuilder sheetBuilderForFrame;
|
||||
bool isInFrame;
|
||||
@@ -114,8 +114,7 @@ namespace OpenRA.Graphics
|
||||
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(m.OffsetFunc()));
|
||||
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
|
||||
|
||||
var worldTransform = m.RotationFunc().Aggregate(Util.IdentityMatrix(),
|
||||
(x, y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x));
|
||||
var worldTransform = Util.MakeFloatMatrix(m.RotationFunc().AsMatrix());
|
||||
worldTransform = Util.MatrixMultiply(scaleTransform, worldTransform);
|
||||
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
|
||||
|
||||
@@ -161,10 +160,8 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
|
||||
// Shadows are rendered at twice the resolution to reduce artifacts
|
||||
Size spriteSize, shadowSpriteSize;
|
||||
int2 spriteOffset, shadowSpriteOffset;
|
||||
CalculateSpriteGeometry(tl, br, 1, out spriteSize, out spriteOffset);
|
||||
CalculateSpriteGeometry(stl, sbr, 2, out shadowSpriteSize, out shadowSpriteOffset);
|
||||
CalculateSpriteGeometry(tl, br, 1, out var spriteSize, out var spriteOffset);
|
||||
CalculateSpriteGeometry(stl, sbr, 2, out var shadowSpriteSize, out var shadowSpriteOffset);
|
||||
|
||||
if (sheetBuilderForFrame == null)
|
||||
sheetBuilderForFrame = new SheetBuilder(SheetType.BGRA, AllocateSheet);
|
||||
@@ -181,7 +178,7 @@ namespace OpenRA.Graphics
|
||||
var correctionTransform = Util.MatrixMultiply(translateMtx, FlipMtx);
|
||||
var shadowCorrectionTransform = Util.MatrixMultiply(shadowTranslateMtx, ShadowScaleFlipMtx);
|
||||
|
||||
doRender.Add(Pair.New<Sheet, Action>(sprite.Sheet, () =>
|
||||
doRender.Add((sprite.Sheet, () =>
|
||||
{
|
||||
foreach (var m in models)
|
||||
{
|
||||
@@ -189,8 +186,7 @@ namespace OpenRA.Graphics
|
||||
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(m.OffsetFunc()));
|
||||
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
|
||||
|
||||
var rotations = m.RotationFunc().Aggregate(Util.IdentityMatrix(),
|
||||
(x, y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x));
|
||||
var rotations = Util.MakeFloatMatrix(m.RotationFunc().AsMatrix());
|
||||
var worldTransform = Util.MatrixMultiply(scaleTransform, rotations);
|
||||
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
|
||||
|
||||
@@ -326,16 +322,16 @@ namespace OpenRA.Graphics
|
||||
foreach (var v in doRender)
|
||||
{
|
||||
// Change sheet
|
||||
if (v.First != currentSheet)
|
||||
if (v.Sheet != currentSheet)
|
||||
{
|
||||
if (fbo != null)
|
||||
DisableFrameBuffer(fbo);
|
||||
|
||||
currentSheet = v.First;
|
||||
currentSheet = v.Sheet;
|
||||
fbo = EnableFrameBuffer(currentSheet);
|
||||
}
|
||||
|
||||
v.Second();
|
||||
v.Func();
|
||||
}
|
||||
|
||||
if (fbo != null)
|
||||
|
||||
@@ -47,14 +47,13 @@ namespace OpenRA.Graphics
|
||||
remapRamp = ramp.Select(r => r - ramp[rampMaxIndex]);
|
||||
}
|
||||
|
||||
remapColors = remapRamp.Select((x, i) => Pair.New(baseIndex + i, Exts.ColorLerp(x / (float)ramp.Length, c1, c2)))
|
||||
.ToDictionary(u => u.First, u => u.Second);
|
||||
remapColors = remapRamp.Select((x, i) => (baseIndex + i, Exts.ColorLerp(x / (float)ramp.Length, c1, c2)))
|
||||
.ToDictionary(u => u.Item1, u => u.Item2);
|
||||
}
|
||||
|
||||
public Color GetRemappedColor(Color original, int index)
|
||||
{
|
||||
Color c;
|
||||
return remapColors.TryGetValue(index, out c)
|
||||
return remapColors.TryGetValue(index, out var c)
|
||||
? c : original;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,11 @@ namespace OpenRA.Graphics
|
||||
IFinalizedRenderable PrepareRender(WorldRenderer wr);
|
||||
}
|
||||
|
||||
public interface ITintableRenderable
|
||||
{
|
||||
IRenderable WithTint(float3 newTint);
|
||||
}
|
||||
|
||||
public interface IFinalizedRenderable
|
||||
{
|
||||
void Render(WorldRenderer wr);
|
||||
|
||||
@@ -45,5 +45,21 @@ namespace OpenRA.Graphics
|
||||
|
||||
parent.DrawSprite(s, a, b, c, d);
|
||||
}
|
||||
|
||||
public void DrawSpriteWithTint(Sprite s, float3 location, float3 size, float3 tint)
|
||||
{
|
||||
if (s.Channel != TextureChannel.RGBA)
|
||||
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
|
||||
|
||||
parent.DrawSpriteWithTint(s, location, 0, size, tint);
|
||||
}
|
||||
|
||||
public void DrawSpriteWithTint(Sprite s, float3 a, float3 b, float3 c, float3 d, float3 tint)
|
||||
{
|
||||
if (s.Channel != TextureChannel.RGBA)
|
||||
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
|
||||
|
||||
parent.DrawSpriteWithTint(s, a, b, c, d, tint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace OpenRA.Graphics
|
||||
int ShadowZOffset { get; }
|
||||
int[] Frames { get; }
|
||||
Rectangle Bounds { get; }
|
||||
bool IgnoreWorldTint { get; }
|
||||
|
||||
Sprite GetSprite(int frame);
|
||||
Sprite GetSprite(int frame, WAngle facing);
|
||||
@@ -69,12 +70,10 @@ namespace OpenRA.Graphics
|
||||
|
||||
public ISpriteSequence GetSequence(string unitName, string sequenceName)
|
||||
{
|
||||
UnitSequences unitSeq;
|
||||
if (!sequences.Value.TryGetValue(unitName, out unitSeq))
|
||||
if (!sequences.Value.TryGetValue(unitName, out var unitSeq))
|
||||
throw new InvalidOperationException("Unit `{0}` does not have any sequences defined.".F(unitName));
|
||||
|
||||
ISpriteSequence seq;
|
||||
if (!unitSeq.Value.TryGetValue(sequenceName, out seq))
|
||||
if (!unitSeq.Value.TryGetValue(sequenceName, out var seq))
|
||||
throw new InvalidOperationException("Unit `{0}` does not have a sequence named `{1}`".F(unitName, sequenceName));
|
||||
|
||||
return seq;
|
||||
@@ -87,8 +86,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public bool HasSequence(string unitName, string sequenceName)
|
||||
{
|
||||
UnitSequences unitSeq;
|
||||
if (!sequences.Value.TryGetValue(unitName, out unitSeq))
|
||||
if (!sequences.Value.TryGetValue(unitName, out var unitSeq))
|
||||
throw new InvalidOperationException("Unit `{0}` does not have any sequences defined.".F(unitName));
|
||||
|
||||
return unitSeq.Value.ContainsKey(sequenceName);
|
||||
@@ -96,8 +94,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public IEnumerable<string> Sequences(string unitName)
|
||||
{
|
||||
UnitSequences unitSeq;
|
||||
if (!sequences.Value.TryGetValue(unitName, out unitSeq))
|
||||
if (!sequences.Value.TryGetValue(unitName, out var unitSeq))
|
||||
throw new InvalidOperationException("Unit `{0}` does not have any sequences defined.".F(unitName));
|
||||
|
||||
return unitSeq.Value.Keys;
|
||||
@@ -114,8 +111,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
var key = node.Value.ToLines(node.Key).JoinWith("|");
|
||||
|
||||
UnitSequences t;
|
||||
if (sequenceCache.TryGetValue(key, out t))
|
||||
if (sequenceCache.TryGetValue(key, out var t))
|
||||
items.Add(node.Key, t);
|
||||
else
|
||||
{
|
||||
|
||||
@@ -146,8 +146,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (texture != null)
|
||||
texture.Dispose();
|
||||
texture?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ namespace OpenRA.Graphics
|
||||
readonly SheetBuilder builder;
|
||||
readonly Func<string, float> lineWidth;
|
||||
readonly IFont font;
|
||||
readonly Cache<Pair<char, Color>, GlyphInfo> glyphs;
|
||||
readonly Cache<Tuple<char, Color, int>, Sprite> contrastGlyphs;
|
||||
readonly Cache<char, GlyphInfo> glyphs;
|
||||
readonly Cache<(char C, int Radius), Sprite> contrastGlyphs;
|
||||
readonly Cache<int, float[]> dilationElements;
|
||||
|
||||
float deviceScale;
|
||||
@@ -39,17 +39,20 @@ namespace OpenRA.Graphics
|
||||
this.builder = builder;
|
||||
|
||||
font = Game.Renderer.CreateFont(data);
|
||||
|
||||
glyphs = new Cache<Pair<char, Color>, GlyphInfo>(CreateGlyph, Pair<char, Color>.EqualityComparer);
|
||||
contrastGlyphs = new Cache<Tuple<char, Color, int>, Sprite>(CreateContrastGlyph);
|
||||
glyphs = new Cache<char, GlyphInfo>(CreateGlyph);
|
||||
contrastGlyphs = new Cache<(char, int), Sprite>(CreateContrastGlyph);
|
||||
dilationElements = new Cache<int, float[]>(CreateCircularWeightMap);
|
||||
|
||||
// PERF: Cache these delegates for Measure calls.
|
||||
Func<char, float> characterWidth = character => glyphs[Pair.New(character, Color.White)].Advance;
|
||||
Func<char, float> characterWidth = character => glyphs[character].Advance;
|
||||
lineWidth = line => line.Sum(characterWidth) / deviceScale;
|
||||
|
||||
// Pre-cache small font sizes so glyphs are immediately available when we need them
|
||||
if (size <= 24)
|
||||
PrecacheColor(Color.White, name);
|
||||
using (new PerfTimer("Precache {0} {1}px".F(name, size)))
|
||||
for (var n = (char)0x20; n < (char)0x7f; n++)
|
||||
if (glyphs[n] == null)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
TopOffset = size - ascender;
|
||||
}
|
||||
@@ -61,14 +64,6 @@ namespace OpenRA.Graphics
|
||||
contrastGlyphs.Clear();
|
||||
}
|
||||
|
||||
void PrecacheColor(Color c, string name)
|
||||
{
|
||||
using (new PerfTimer("PrecacheColor {0} {1}px {2}".F(name, size, c)))
|
||||
for (var n = (char)0x20; n < (char)0x7f; n++)
|
||||
if (glyphs[Pair.New(n, c)] == null)
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
void DrawTextContrast(string text, float2 location, Color contrastColor, int contrastOffset)
|
||||
{
|
||||
// Offset from the baseline position to the top-left of the glyph for rendering
|
||||
@@ -78,6 +73,7 @@ namespace OpenRA.Graphics
|
||||
var screenContrast = (int)(contrastOffset * deviceScale);
|
||||
var screen = new int2((int)(location.X * deviceScale + 0.5f), (int)(location.Y * deviceScale + 0.5f));
|
||||
var contrastVector = new float2(screenContrast, screenContrast);
|
||||
var tint = new float3(contrastColor.R / 255f, contrastColor.G / 255f, contrastColor.B / 255f);
|
||||
foreach (var s in text)
|
||||
{
|
||||
if (s == '\n')
|
||||
@@ -87,15 +83,16 @@ namespace OpenRA.Graphics
|
||||
continue;
|
||||
}
|
||||
|
||||
var g = glyphs[Pair.New(s, Color.Black)];
|
||||
var g = glyphs[s];
|
||||
|
||||
// Convert screen coordinates back to UI coordinates for drawing
|
||||
if (g.Sprite != null)
|
||||
{
|
||||
var contrastSprite = contrastGlyphs[Tuple.Create(s, contrastColor, screenContrast)];
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(contrastSprite,
|
||||
var contrastSprite = contrastGlyphs[(s, screenContrast)];
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSpriteWithTint(contrastSprite,
|
||||
(screen + g.Offset - contrastVector) / deviceScale,
|
||||
contrastSprite.Size / deviceScale);
|
||||
contrastSprite.Size / deviceScale,
|
||||
tint);
|
||||
}
|
||||
|
||||
screen += new int2((int)(g.Advance + 0.5f), 0);
|
||||
@@ -109,6 +106,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
// Calculate positions in screen pixel coordinates
|
||||
var screen = new int2((int)(location.X * deviceScale + 0.5f), (int)(location.Y * deviceScale + 0.5f));
|
||||
var tint = new float3(c.R / 255f, c.G / 255f, c.B / 255f);
|
||||
foreach (var s in text)
|
||||
{
|
||||
if (s == '\n')
|
||||
@@ -118,13 +116,14 @@ namespace OpenRA.Graphics
|
||||
continue;
|
||||
}
|
||||
|
||||
var g = glyphs[Pair.New(s, c)];
|
||||
var g = glyphs[s];
|
||||
|
||||
// Convert screen coordinates back to UI coordinates for drawing
|
||||
if (g.Sprite != null)
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSpriteWithTint(g.Sprite,
|
||||
(screen + g.Offset).ToFloat2() / deviceScale,
|
||||
g.Sprite.Size / deviceScale);
|
||||
g.Sprite.Size / deviceScale,
|
||||
tint);
|
||||
|
||||
screen += new int2((int)(g.Advance + 0.5f), 0);
|
||||
}
|
||||
@@ -144,6 +143,7 @@ namespace OpenRA.Graphics
|
||||
var offset = new float2(0, size);
|
||||
var cosa = (float)Math.Cos(-angle);
|
||||
var sina = (float)Math.Sin(-angle);
|
||||
var tint = new float3(c.R / 255f, c.G / 255f, c.B / 255f);
|
||||
|
||||
var p = offset;
|
||||
foreach (var s in text)
|
||||
@@ -155,7 +155,7 @@ namespace OpenRA.Graphics
|
||||
continue;
|
||||
}
|
||||
|
||||
var g = glyphs[Pair.New(s, c)];
|
||||
var g = glyphs[s];
|
||||
if (g.Sprite != null)
|
||||
{
|
||||
var tl = new float2(
|
||||
@@ -172,11 +172,12 @@ namespace OpenRA.Graphics
|
||||
|
||||
// Offset rotated glyph to align the top-left corner with the screen pixel grid
|
||||
var screenOffset = new float2((int)(ra.X * deviceScale + 0.5f), (int)(ra.Y * deviceScale + 0.5f)) / deviceScale - ra;
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSpriteWithTint(g.Sprite,
|
||||
ra + screenOffset,
|
||||
rb + screenOffset,
|
||||
rc + screenOffset,
|
||||
rd + screenOffset);
|
||||
rd + screenOffset,
|
||||
tint);
|
||||
}
|
||||
|
||||
p += new float2(g.Advance / deviceScale, 0);
|
||||
@@ -241,10 +242,9 @@ namespace OpenRA.Graphics
|
||||
return new int2((int)Math.Ceiling(lines.Max(lineWidth)), lines.Length * size);
|
||||
}
|
||||
|
||||
GlyphInfo CreateGlyph(Pair<char, Color> c)
|
||||
GlyphInfo CreateGlyph(char c)
|
||||
{
|
||||
var glyph = font.CreateGlyph(c.First, size, deviceScale);
|
||||
|
||||
var glyph = font.CreateGlyph(c, size, deviceScale);
|
||||
if (glyph.Data == null)
|
||||
{
|
||||
return new GlyphInfo
|
||||
@@ -274,12 +274,10 @@ namespace OpenRA.Graphics
|
||||
if (p != 0)
|
||||
{
|
||||
var q = destStride * (j + s.Bounds.Top) + 4 * (i + s.Bounds.Left);
|
||||
var pmc = Util.PremultiplyAlpha(Color.FromArgb(p, c.Second));
|
||||
|
||||
dest[q] = pmc.B;
|
||||
dest[q + 1] = pmc.G;
|
||||
dest[q + 2] = pmc.R;
|
||||
dest[q + 3] = pmc.A;
|
||||
dest[q] = p;
|
||||
dest[q + 1] = p;
|
||||
dest[q + 2] = p;
|
||||
dest[q + 3] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,16 +345,12 @@ namespace OpenRA.Graphics
|
||||
return elem;
|
||||
}
|
||||
|
||||
Sprite CreateContrastGlyph(Tuple<char, Color, int> c)
|
||||
Sprite CreateContrastGlyph((char C, int Radius) c)
|
||||
{
|
||||
// Source glyph color doesn't matter, so use black
|
||||
var glyph = glyphs[Pair.New(c.Item1, Color.Black)];
|
||||
var color = c.Item2;
|
||||
var r = c.Item3;
|
||||
var glyph = glyphs[c.C];
|
||||
var r = c.Radius;
|
||||
|
||||
var size = new Size(glyph.Sprite.Bounds.Width + 2 * r, glyph.Sprite.Bounds.Height + 2 * r);
|
||||
|
||||
var s = builder.Allocate(size);
|
||||
var s = builder.Allocate(new Size(glyph.Sprite.Bounds.Width + 2 * r, glyph.Sprite.Bounds.Height + 2 * r));
|
||||
var dest = s.Sheet.GetData();
|
||||
var destStride = s.Sheet.Size.Width * 4;
|
||||
|
||||
@@ -398,11 +392,10 @@ namespace OpenRA.Graphics
|
||||
if (alpha > 0)
|
||||
{
|
||||
var q = destStride * (j + s.Bounds.Top) + 4 * (i + s.Bounds.Left);
|
||||
var pmc = Util.PremultiplyAlpha(Color.FromArgb(alpha, color));
|
||||
dest[q] = pmc.B;
|
||||
dest[q + 1] = pmc.G;
|
||||
dest[q + 2] = pmc.R;
|
||||
dest[q + 3] = pmc.A;
|
||||
dest[q] = alpha;
|
||||
dest[q + 1] = alpha;
|
||||
dest[q + 2] = alpha;
|
||||
dest[q + 3] = alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +76,7 @@ namespace OpenRA.Graphics
|
||||
var allSprites = sprites.GetOrAdd(filename);
|
||||
var sprite = allSprites.FirstOrDefault();
|
||||
|
||||
ISpriteFrame[] unloaded;
|
||||
if (!unloadedFrames.TryGetValue(filename, out unloaded))
|
||||
if (!unloadedFrames.TryGetValue(filename, out var unloaded))
|
||||
unloaded = null;
|
||||
|
||||
// This is the first time that the file has been requested
|
||||
@@ -85,8 +84,7 @@ namespace OpenRA.Graphics
|
||||
// the loaded cache (initially empty)
|
||||
if (sprite == null)
|
||||
{
|
||||
TypeDictionary fileMetadata = null;
|
||||
unloaded = FrameLoader.GetFrames(fileSystem, filename, loaders, out fileMetadata);
|
||||
unloaded = FrameLoader.GetFrames(fileSystem, filename, loaders, out var fileMetadata);
|
||||
unloadedFrames[filename] = unloaded;
|
||||
metadata[filename] = fileMetadata;
|
||||
|
||||
@@ -125,8 +123,7 @@ namespace OpenRA.Graphics
|
||||
/// </summary>
|
||||
public TypeDictionary FrameMetadata(string filename)
|
||||
{
|
||||
TypeDictionary fileMetadata;
|
||||
if (!metadata.TryGetValue(filename, out fileMetadata))
|
||||
if (!metadata.TryGetValue(filename, out var fileMetadata))
|
||||
{
|
||||
FrameLoader.GetFrames(fileSystem, filename, loaders, out fileMetadata);
|
||||
metadata[filename] = fileMetadata;
|
||||
@@ -142,8 +139,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public FrameCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders)
|
||||
{
|
||||
TypeDictionary metadata;
|
||||
frames = new Cache<string, ISpriteFrame[]>(filename => FrameLoader.GetFrames(fileSystem, filename, loaders, out metadata));
|
||||
frames = new Cache<string, ISpriteFrame[]>(filename => FrameLoader.GetFrames(fileSystem, filename, loaders, out _));
|
||||
}
|
||||
|
||||
public ISpriteFrame[] this[string filename] { get { return frames[filename]; } }
|
||||
@@ -165,11 +161,10 @@ namespace OpenRA.Graphics
|
||||
|
||||
public static ISpriteFrame[] GetFrames(Stream stream, ISpriteLoader[] loaders, out TypeDictionary metadata)
|
||||
{
|
||||
ISpriteFrame[] frames;
|
||||
metadata = null;
|
||||
|
||||
foreach (var loader in loaders)
|
||||
if (loader.TryParseSprite(stream, out frames, out metadata))
|
||||
if (loader.TryParseSprite(stream, out var frames, out metadata))
|
||||
return frames;
|
||||
|
||||
return null;
|
||||
|
||||
@@ -14,7 +14,7 @@ using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public struct SpriteRenderable : IRenderable, IFinalizedRenderable
|
||||
public struct SpriteRenderable : IRenderable, ITintableRenderable, IFinalizedRenderable
|
||||
{
|
||||
public static readonly IEnumerable<IRenderable> None = new IRenderable[0];
|
||||
|
||||
@@ -24,9 +24,17 @@ namespace OpenRA.Graphics
|
||||
readonly int zOffset;
|
||||
readonly PaletteReference palette;
|
||||
readonly float scale;
|
||||
readonly float3 tint;
|
||||
readonly bool isDecoration;
|
||||
readonly bool ignoreWorldTint;
|
||||
|
||||
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, bool isDecoration)
|
||||
: this(sprite, pos, offset, zOffset, palette, scale, float3.Ones, isDecoration, false) { }
|
||||
|
||||
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, bool isDecoration, bool ignoreWorldTint)
|
||||
: this(sprite, pos, offset, zOffset, palette, scale, float3.Ones, isDecoration, ignoreWorldTint) { }
|
||||
|
||||
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, float3 tint, bool isDecoration, bool ignoreWorldTint)
|
||||
{
|
||||
this.sprite = sprite;
|
||||
this.pos = pos;
|
||||
@@ -34,7 +42,9 @@ namespace OpenRA.Graphics
|
||||
this.zOffset = zOffset;
|
||||
this.palette = palette;
|
||||
this.scale = scale;
|
||||
this.tint = tint;
|
||||
this.isDecoration = isDecoration;
|
||||
this.ignoreWorldTint = ignoreWorldTint;
|
||||
}
|
||||
|
||||
public WPos Pos { get { return pos + offset; } }
|
||||
@@ -43,10 +53,12 @@ namespace OpenRA.Graphics
|
||||
public int ZOffset { get { return zOffset; } }
|
||||
public bool IsDecoration { get { return isDecoration; } }
|
||||
|
||||
public IRenderable WithPalette(PaletteReference newPalette) { return new SpriteRenderable(sprite, pos, offset, zOffset, newPalette, scale, isDecoration); }
|
||||
public IRenderable WithZOffset(int newOffset) { return new SpriteRenderable(sprite, pos, offset, newOffset, palette, scale, isDecoration); }
|
||||
public IRenderable OffsetBy(WVec vec) { return new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, isDecoration); }
|
||||
public IRenderable AsDecoration() { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, true); }
|
||||
public IRenderable WithPalette(PaletteReference newPalette) { return new SpriteRenderable(sprite, pos, offset, zOffset, newPalette, scale, tint, isDecoration, ignoreWorldTint); }
|
||||
public IRenderable WithZOffset(int newOffset) { return new SpriteRenderable(sprite, pos, offset, newOffset, palette, scale, tint, isDecoration, ignoreWorldTint); }
|
||||
public IRenderable OffsetBy(WVec vec) { return new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, tint, isDecoration, ignoreWorldTint); }
|
||||
public IRenderable AsDecoration() { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, tint, true, ignoreWorldTint); }
|
||||
|
||||
public IRenderable WithTint(float3 newTint) { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, newTint, isDecoration, ignoreWorldTint); }
|
||||
|
||||
float3 ScreenPosition(WorldRenderer wr)
|
||||
{
|
||||
@@ -59,7 +71,17 @@ namespace OpenRA.Graphics
|
||||
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
|
||||
public void Render(WorldRenderer wr)
|
||||
{
|
||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(sprite, ScreenPosition(wr), palette, scale * sprite.Size);
|
||||
var wsr = Game.Renderer.WorldSpriteRenderer;
|
||||
if (ignoreWorldTint)
|
||||
wsr.DrawSprite(sprite, ScreenPosition(wr), palette, scale * sprite.Size);
|
||||
else
|
||||
{
|
||||
var t = tint;
|
||||
if (wr.TerrainLighting != null)
|
||||
t *= wr.TerrainLighting.TintAt(pos);
|
||||
|
||||
wsr.DrawSpriteWithTint(sprite, ScreenPosition(wr), palette, scale * sprite.Size, t);
|
||||
}
|
||||
}
|
||||
|
||||
public void RenderDebugGeometry(WorldRenderer wr)
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace OpenRA.Graphics
|
||||
internal void DrawSprite(Sprite s, float3 location, float paletteTextureIndex, float3 size)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size);
|
||||
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size, float3.Ones);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,26 @@ namespace OpenRA.Graphics
|
||||
public void DrawSprite(Sprite s, float3 a, float3 b, float3 c, float3 d)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, nv);
|
||||
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, float3.Ones, nv);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
internal void DrawSpriteWithTint(Sprite s, float3 location, float paletteTextureIndex, float3 size, float3 tint)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size, tint);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
public void DrawSpriteWithTint(Sprite s, float3 location, PaletteReference pal, float3 size, float3 tint)
|
||||
{
|
||||
DrawSpriteWithTint(s, location, pal.TextureIndex, size, tint);
|
||||
}
|
||||
|
||||
public void DrawSpriteWithTint(Sprite s, float3 a, float3 b, float3 c, float3 d, float3 tint)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, tint, nv);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public sealed class TerrainSpriteLayer : IDisposable
|
||||
{
|
||||
static readonly int[] CornerVertexMap = { 0, 1, 2, 2, 3, 0 };
|
||||
|
||||
public readonly Sheet Sheet;
|
||||
public readonly BlendMode BlendMode;
|
||||
|
||||
@@ -25,6 +27,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
readonly IVertexBuffer<Vertex> vertexBuffer;
|
||||
readonly Vertex[] vertices;
|
||||
readonly bool[] ignoreTint;
|
||||
readonly HashSet<int> dirtyRows = new HashSet<int>();
|
||||
readonly int rowStride;
|
||||
readonly bool restrictToBounds;
|
||||
@@ -50,6 +53,12 @@ namespace OpenRA.Graphics
|
||||
emptySprite = new Sprite(sheet, Rectangle.Empty, TextureChannel.Alpha);
|
||||
|
||||
wr.PaletteInvalidated += UpdatePaletteIndices;
|
||||
|
||||
if (wr.TerrainLighting != null)
|
||||
{
|
||||
ignoreTint = new bool[rowStride * map.MapSize.Y];
|
||||
wr.TerrainLighting.CellChanged += UpdateTint;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatePaletteIndices()
|
||||
@@ -59,22 +68,76 @@ namespace OpenRA.Graphics
|
||||
for (var i = 0; i < vertices.Length; i++)
|
||||
{
|
||||
var v = vertices[i];
|
||||
vertices[i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C);
|
||||
vertices[i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, v.R, v.G, v.B);
|
||||
}
|
||||
|
||||
for (var row = 0; row < map.MapSize.Y; row++)
|
||||
dirtyRows.Add(row);
|
||||
}
|
||||
|
||||
public void Update(CPos cell, Sprite sprite)
|
||||
public void Clear(CPos cell)
|
||||
{
|
||||
var xyz = sprite == null ? float3.Zero :
|
||||
worldRenderer.Screen3DPosition(map.CenterOfCell(cell)) + sprite.Offset - 0.5f * sprite.Size;
|
||||
|
||||
Update(cell.ToMPos(map.Grid.Type), sprite, xyz);
|
||||
Update(cell, null, true);
|
||||
}
|
||||
|
||||
public void Update(MPos uv, Sprite sprite, float3 pos)
|
||||
public void Update(CPos cell, ISpriteSequence sequence, int frame)
|
||||
{
|
||||
Update(cell, sequence.GetSprite(frame), sequence.IgnoreWorldTint);
|
||||
}
|
||||
|
||||
public void Update(CPos cell, Sprite sprite, bool ignoreTint)
|
||||
{
|
||||
var xyz = float3.Zero;
|
||||
if (sprite != null)
|
||||
{
|
||||
var cellOrigin = map.CenterOfCell(cell) - new WVec(0, 0, map.Grid.Ramps[map.Ramp[cell]].CenterHeightOffset);
|
||||
xyz = worldRenderer.Screen3DPosition(cellOrigin) + sprite.Offset - 0.5f * sprite.Size;
|
||||
}
|
||||
|
||||
Update(cell.ToMPos(map.Grid.Type), sprite, xyz, ignoreTint);
|
||||
}
|
||||
|
||||
void UpdateTint(MPos uv)
|
||||
{
|
||||
var offset = rowStride * uv.V + 6 * uv.U;
|
||||
if (ignoreTint[offset])
|
||||
{
|
||||
var noTint = float3.Ones;
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
var v = vertices[offset + i];
|
||||
vertices[offset + i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, noTint);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow the terrain tint to vary linearly across the cell to smooth out the staircase effect
|
||||
// This is done by sampling the lighting the corners of the sprite, even though those pixels are
|
||||
// transparent for isometric tiles
|
||||
var tl = worldRenderer.TerrainLighting;
|
||||
var pos = map.CenterOfCell(uv.ToCPos(map));
|
||||
var step = map.Grid.Type == MapGridType.RectangularIsometric ? 724 : 512;
|
||||
var weights = new[]
|
||||
{
|
||||
tl.TintAt(pos + new WVec(-step, -step, 0)),
|
||||
tl.TintAt(pos + new WVec(step, -step, 0)),
|
||||
tl.TintAt(pos + new WVec(step, step, 0)),
|
||||
tl.TintAt(pos + new WVec(-step, step, 0))
|
||||
};
|
||||
|
||||
// Apply tint directly to the underlying vertices
|
||||
// This saves us from having to re-query the sprite information, which has not changed
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
var v = vertices[offset + i];
|
||||
vertices[offset + i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, weights[CornerVertexMap[i]]);
|
||||
}
|
||||
|
||||
dirtyRows.Add(uv.V);
|
||||
}
|
||||
|
||||
public void Update(MPos uv, Sprite sprite, float3 pos, bool ignoreTint)
|
||||
{
|
||||
if (sprite != null)
|
||||
{
|
||||
@@ -92,7 +155,13 @@ namespace OpenRA.Graphics
|
||||
return;
|
||||
|
||||
var offset = rowStride * uv.V + 6 * uv.U;
|
||||
Util.FastCreateQuad(vertices, pos, sprite, int2.Zero, palette.TextureIndex, offset, sprite.Size);
|
||||
Util.FastCreateQuad(vertices, pos, sprite, int2.Zero, palette.TextureIndex, offset, sprite.Size, float3.Ones);
|
||||
|
||||
if (worldRenderer.TerrainLighting != null)
|
||||
{
|
||||
this.ignoreTint[offset] = ignoreTint;
|
||||
UpdateTint(uv);
|
||||
}
|
||||
|
||||
dirtyRows.Add(uv.V);
|
||||
}
|
||||
@@ -135,6 +204,9 @@ namespace OpenRA.Graphics
|
||||
public void Dispose()
|
||||
{
|
||||
worldRenderer.PaletteInvalidated -= UpdatePaletteIndices;
|
||||
if (worldRenderer.TerrainLighting != null)
|
||||
worldRenderer.TerrainLighting.CellChanged -= UpdateTint;
|
||||
|
||||
vertexBuffer.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,8 +118,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public Sprite TileSprite(TerrainTile r, int? variant = null)
|
||||
{
|
||||
TheaterTemplate template;
|
||||
if (!templates.TryGetValue(r.Type, out template))
|
||||
if (!templates.TryGetValue(r.Type, out var template))
|
||||
return missingTile;
|
||||
|
||||
if (r.Index >= template.Stride)
|
||||
|
||||
@@ -20,15 +20,18 @@ namespace OpenRA.Graphics
|
||||
// yes, our channel order is nuts.
|
||||
static readonly int[] ChannelMasks = { 2, 1, 0, 3 };
|
||||
|
||||
public static void FastCreateQuad(Vertex[] vertices, float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, float3 size)
|
||||
public static void FastCreateQuad(Vertex[] vertices, float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, float3 size, float3 tint)
|
||||
{
|
||||
var b = new float3(o.X + size.X, o.Y, o.Z);
|
||||
var c = new float3(o.X + size.X, o.Y + size.Y, o.Z + size.Z);
|
||||
var d = new float3(o.X, o.Y + size.Y, o.Z + size.Z);
|
||||
FastCreateQuad(vertices, o, b, c, d, r, samplers, paletteTextureIndex, nv);
|
||||
FastCreateQuad(vertices, o, b, c, d, r, samplers, paletteTextureIndex, tint, nv);
|
||||
}
|
||||
|
||||
public static void FastCreateQuad(Vertex[] vertices, float3 a, float3 b, float3 c, float3 d, Sprite r, int2 samplers, float paletteTextureIndex, int nv)
|
||||
public static void FastCreateQuad(Vertex[] vertices,
|
||||
float3 a, float3 b, float3 c, float3 d,
|
||||
Sprite r, int2 samplers, float paletteTextureIndex,
|
||||
float3 tint, int nv)
|
||||
{
|
||||
float sl = 0;
|
||||
float st = 0;
|
||||
@@ -51,12 +54,12 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
|
||||
var fAttribC = (float)attribC;
|
||||
vertices[nv] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC);
|
||||
vertices[nv + 1] = new Vertex(b, r.Right, r.Top, sr, st, paletteTextureIndex, fAttribC);
|
||||
vertices[nv + 2] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC);
|
||||
vertices[nv + 3] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC);
|
||||
vertices[nv + 4] = new Vertex(d, r.Left, r.Bottom, sl, sb, paletteTextureIndex, fAttribC);
|
||||
vertices[nv + 5] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC);
|
||||
vertices[nv] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC, tint);
|
||||
vertices[nv + 1] = new Vertex(b, r.Right, r.Top, sr, st, paletteTextureIndex, fAttribC, tint);
|
||||
vertices[nv + 2] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC, tint);
|
||||
vertices[nv + 3] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC, tint);
|
||||
vertices[nv + 4] = new Vertex(d, r.Left, r.Bottom, sl, sb, paletteTextureIndex, fAttribC, tint);
|
||||
vertices[nv + 5] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC, tint);
|
||||
}
|
||||
|
||||
public static void FastCopyIntoChannel(Sprite dest, byte[] src)
|
||||
|
||||
@@ -16,17 +16,34 @@ namespace OpenRA.Graphics
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Vertex
|
||||
{
|
||||
public readonly float X, Y, Z, S, T, U, V, P, C;
|
||||
// 3d position
|
||||
public readonly float X, Y, Z;
|
||||
|
||||
// Primary and secondary texture coordinates or RGBA color
|
||||
public readonly float S, T, U, V;
|
||||
|
||||
// Palette and channel flags
|
||||
public readonly float P, C;
|
||||
|
||||
// Color tint
|
||||
public readonly float R, G, B;
|
||||
|
||||
public Vertex(float3 xyz, float s, float t, float u, float v, float p, float c)
|
||||
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c) { }
|
||||
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c, float3.Ones) { }
|
||||
|
||||
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c)
|
||||
public Vertex(float3 xyz, float s, float t, float u, float v, float p, float c, float3 tint)
|
||||
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c, tint.X, tint.Y, tint.Z) { }
|
||||
|
||||
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c, float3 tint)
|
||||
: this(x, y, z, s, t, u, v, p, c, tint.X, tint.Y, tint.Z) { }
|
||||
|
||||
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c, float r, float g, float b)
|
||||
{
|
||||
X = x; Y = y; Z = z;
|
||||
S = s; T = t;
|
||||
U = u; V = v;
|
||||
P = p; C = c;
|
||||
R = r; G = g; B = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,14 @@ namespace OpenRA.Graphics
|
||||
Zoom = (zoom * (float)Math.Exp(dz)).Clamp(unlockMinZoom ? unlockedMinZoom : minZoom, maxZoom);
|
||||
}
|
||||
|
||||
public void AdjustZoom(float dz, int2 center)
|
||||
{
|
||||
var oldCenter = worldRenderer.Viewport.ViewToWorldPx(center);
|
||||
AdjustZoom(dz);
|
||||
var newCenter = worldRenderer.Viewport.ViewToWorldPx(center);
|
||||
CenterLocation += oldCenter - newCenter;
|
||||
}
|
||||
|
||||
public void ToggleZoom()
|
||||
{
|
||||
// Unlocked zooms always reset to the default zoom
|
||||
@@ -254,7 +262,6 @@ namespace OpenRA.Graphics
|
||||
var world = worldRenderer.Viewport.ViewToWorldPx(view);
|
||||
var map = worldRenderer.World.Map;
|
||||
var candidates = CandidateMouseoverCells(world).ToList();
|
||||
var tileSet = worldRenderer.World.Map.Rules.TileSet;
|
||||
|
||||
foreach (var uv in candidates)
|
||||
{
|
||||
@@ -263,18 +270,9 @@ namespace OpenRA.Graphics
|
||||
var s = worldRenderer.ScreenPxPosition(p);
|
||||
if (Math.Abs(s.X - world.X) <= tileSize.Width && Math.Abs(s.Y - world.Y) <= tileSize.Height)
|
||||
{
|
||||
var ramp = 0;
|
||||
if (map.Contains(uv))
|
||||
{
|
||||
var ti = tileSet.GetTileInfo(map.Tiles[uv]);
|
||||
if (ti != null)
|
||||
ramp = ti.RampType;
|
||||
}
|
||||
|
||||
var corners = map.Grid.CellCorners[ramp];
|
||||
var pos = map.CenterOfCell(uv.ToCPos(map));
|
||||
var screen = corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToArray();
|
||||
|
||||
var ramp = map.Grid.Ramps[map.Ramp.Contains(uv) ? map.Ramp[uv] : 0];
|
||||
var pos = map.CenterOfCell(uv.ToCPos(map)) - new WVec(0, 0, ramp.CenterHeightOffset);
|
||||
var screen = ramp.Corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToArray();
|
||||
if (screen.PolygonContains(world))
|
||||
return uv.ToCPos(map);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace OpenRA.Graphics
|
||||
public readonly World World;
|
||||
public readonly Theater Theater;
|
||||
public Viewport Viewport { get; private set; }
|
||||
public readonly ITerrainLighting TerrainLighting;
|
||||
|
||||
public event Action PaletteInvalidated = null;
|
||||
|
||||
@@ -43,6 +44,8 @@ namespace OpenRA.Graphics
|
||||
readonly List<IFinalizedRenderable> preparedOverlayRenderables = new List<IFinalizedRenderable>();
|
||||
readonly List<IFinalizedRenderable> preparedAnnotationRenderables = new List<IFinalizedRenderable>();
|
||||
|
||||
readonly List<IRenderable> renderablesBuffer = new List<IRenderable>();
|
||||
|
||||
bool lastDepthPreviewEnabled;
|
||||
|
||||
internal WorldRenderer(ModData modData, World world)
|
||||
@@ -66,6 +69,7 @@ namespace OpenRA.Graphics
|
||||
palette.Initialize();
|
||||
|
||||
Theater = new Theater(world.Map.Rules.TileSet);
|
||||
TerrainLighting = world.WorldActor.TraitOrDefault<ITerrainLighting>();
|
||||
terrainRenderer = world.WorldActor.TraitOrDefault<IRenderTerrain>();
|
||||
|
||||
debugVis = Exts.Lazy(() => world.WorldActor.TraitOrDefault<DebugVisualizations>());
|
||||
@@ -93,8 +97,8 @@ namespace OpenRA.Graphics
|
||||
var oldHeight = palette.Height;
|
||||
palette.AddPalette(name, pal, allowModifiers);
|
||||
|
||||
if (oldHeight != palette.Height && PaletteInvalidated != null)
|
||||
PaletteInvalidated();
|
||||
if (oldHeight != palette.Height)
|
||||
PaletteInvalidated?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,78 +111,117 @@ namespace OpenRA.Graphics
|
||||
palettes[name].Palette = pal;
|
||||
}
|
||||
|
||||
IEnumerable<IFinalizedRenderable> GenerateRenderables()
|
||||
// PERF: Avoid LINQ.
|
||||
void GenerateRenderables()
|
||||
{
|
||||
var actors = onScreenActors.Append(World.WorldActor);
|
||||
if (World.RenderPlayer != null)
|
||||
actors = actors.Append(World.RenderPlayer.PlayerActor);
|
||||
foreach (var actor in onScreenActors)
|
||||
renderablesBuffer.AddRange(actor.Render(this));
|
||||
|
||||
renderablesBuffer.AddRange(World.WorldActor.Render(this));
|
||||
|
||||
if (World.RenderPlayer != null)
|
||||
renderablesBuffer.AddRange(World.RenderPlayer.PlayerActor.Render(this));
|
||||
|
||||
var worldRenderables = actors.SelectMany(a => a.Render(this));
|
||||
if (World.OrderGenerator != null)
|
||||
worldRenderables = worldRenderables.Concat(World.OrderGenerator.Render(this, World));
|
||||
renderablesBuffer.AddRange(World.OrderGenerator.Render(this, World));
|
||||
|
||||
// Unpartitioned effects
|
||||
worldRenderables = worldRenderables.Concat(World.UnpartitionedEffects.SelectMany(e => e.Render(this)));
|
||||
foreach (var e in World.UnpartitionedEffects)
|
||||
renderablesBuffer.AddRange(e.Render(this));
|
||||
|
||||
// Partitioned, currently on-screen effects
|
||||
var effectRenderables = World.ScreenMap.RenderableEffectsInBox(Viewport.TopLeft, Viewport.BottomRight);
|
||||
worldRenderables = worldRenderables.Concat(effectRenderables.SelectMany(e => e.Render(this)));
|
||||
foreach (var e in World.ScreenMap.RenderableEffectsInBox(Viewport.TopLeft, Viewport.BottomRight))
|
||||
renderablesBuffer.AddRange(e.Render(this));
|
||||
|
||||
worldRenderables = worldRenderables.OrderBy(RenderableScreenZPositionComparisonKey);
|
||||
foreach (var renderable in renderablesBuffer.OrderBy(RenderableScreenZPositionComparisonKey))
|
||||
preparedRenderables.Add(renderable.PrepareRender(this));
|
||||
|
||||
return worldRenderables.Select(r => r.PrepareRender(this));
|
||||
// PERF: Reuse collection to avoid allocations.
|
||||
renderablesBuffer.Clear();
|
||||
}
|
||||
|
||||
IEnumerable<IFinalizedRenderable> GenerateOverlayRenderables()
|
||||
// PERF: Avoid LINQ.
|
||||
void GenerateOverlayRenderables()
|
||||
{
|
||||
var actors = World.ActorsWithTrait<IRenderAboveShroud>()
|
||||
.Where(a => a.Actor.IsInWorld && !a.Actor.Disposed && (!a.Trait.SpatiallyPartitionable || onScreenActors.Contains(a.Actor)))
|
||||
.SelectMany(a => a.Trait.RenderAboveShroud(a.Actor, this));
|
||||
foreach (var a in World.ActorsWithTrait<IRenderAboveShroud>())
|
||||
{
|
||||
if (!a.Actor.IsInWorld || a.Actor.Disposed || (a.Trait.SpatiallyPartitionable && !onScreenActors.Contains(a.Actor)))
|
||||
continue;
|
||||
|
||||
var selected = World.Selection.Actors.Where(a => a.IsInWorld && !a.Disposed)
|
||||
.SelectMany(a => a.TraitsImplementing<IRenderAboveShroudWhenSelected>()
|
||||
.Where(t => !t.SpatiallyPartitionable || onScreenActors.Contains(a))
|
||||
.SelectMany(t => t.RenderAboveShroud(a, this)));
|
||||
foreach (var renderable in a.Trait.RenderAboveShroud(a.Actor, this))
|
||||
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
|
||||
}
|
||||
|
||||
var effects = World.Effects.Select(e => e as IEffectAboveShroud)
|
||||
.Where(e => e != null)
|
||||
.SelectMany(e => e.RenderAboveShroud(this));
|
||||
foreach (var a in World.Selection.Actors)
|
||||
{
|
||||
if (!a.IsInWorld || a.Disposed)
|
||||
continue;
|
||||
|
||||
foreach (var t in a.TraitsImplementing<IRenderAboveShroudWhenSelected>())
|
||||
{
|
||||
if (t.SpatiallyPartitionable && !onScreenActors.Contains(a))
|
||||
continue;
|
||||
|
||||
foreach (var renderable in t.RenderAboveShroud(a, this))
|
||||
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var e in World.Effects)
|
||||
{
|
||||
var ea = e as IEffectAboveShroud;
|
||||
if (ea == null)
|
||||
continue;
|
||||
|
||||
foreach (var renderable in ea.RenderAboveShroud(this))
|
||||
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
|
||||
}
|
||||
|
||||
var orderGenerator = SpriteRenderable.None;
|
||||
if (World.OrderGenerator != null)
|
||||
orderGenerator = World.OrderGenerator.RenderAboveShroud(this, World);
|
||||
|
||||
return actors
|
||||
.Concat(selected)
|
||||
.Concat(effects)
|
||||
.Concat(orderGenerator)
|
||||
.Select(r => r.PrepareRender(this));
|
||||
foreach (var renderable in World.OrderGenerator.RenderAboveShroud(this, World))
|
||||
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
|
||||
}
|
||||
|
||||
IEnumerable<IFinalizedRenderable> GenerateAnnotationRenderables()
|
||||
// PERF: Avoid LINQ.
|
||||
void GenerateAnnotationRenderables()
|
||||
{
|
||||
var actors = World.ActorsWithTrait<IRenderAnnotations>()
|
||||
.Where(a => a.Actor.IsInWorld && !a.Actor.Disposed && (!a.Trait.SpatiallyPartitionable || onScreenActors.Contains(a.Actor)))
|
||||
.SelectMany(a => a.Trait.RenderAnnotations(a.Actor, this));
|
||||
foreach (var a in World.ActorsWithTrait<IRenderAnnotations>())
|
||||
{
|
||||
if (!a.Actor.IsInWorld || a.Actor.Disposed || (a.Trait.SpatiallyPartitionable && !onScreenActors.Contains(a.Actor)))
|
||||
continue;
|
||||
|
||||
var selected = World.Selection.Actors.Where(a => a.IsInWorld && !a.Disposed)
|
||||
.SelectMany(a => a.TraitsImplementing<IRenderAnnotationsWhenSelected>()
|
||||
.Where(t => !t.SpatiallyPartitionable || onScreenActors.Contains(a))
|
||||
.SelectMany(t => t.RenderAnnotations(a, this)));
|
||||
foreach (var renderAnnotation in a.Trait.RenderAnnotations(a.Actor, this))
|
||||
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
|
||||
}
|
||||
|
||||
var effects = World.Effects.Select(e => e as IEffectAnnotation)
|
||||
.Where(e => e != null)
|
||||
.SelectMany(e => e.RenderAnnotation(this));
|
||||
foreach (var a in World.Selection.Actors)
|
||||
{
|
||||
if (!a.IsInWorld || a.Disposed)
|
||||
continue;
|
||||
|
||||
foreach (var t in a.TraitsImplementing<IRenderAnnotationsWhenSelected>())
|
||||
{
|
||||
if (t.SpatiallyPartitionable && !onScreenActors.Contains(a))
|
||||
continue;
|
||||
|
||||
foreach (var renderAnnotation in t.RenderAnnotations(a, this))
|
||||
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var e in World.Effects)
|
||||
{
|
||||
var ea = e as IEffectAnnotation;
|
||||
if (ea == null)
|
||||
continue;
|
||||
|
||||
foreach (var renderAnnotation in ea.RenderAnnotation(this))
|
||||
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
|
||||
}
|
||||
|
||||
var orderGenerator = SpriteRenderable.None;
|
||||
if (World.OrderGenerator != null)
|
||||
orderGenerator = World.OrderGenerator.RenderAnnotations(this, World);
|
||||
|
||||
return actors
|
||||
.Concat(selected)
|
||||
.Concat(effects)
|
||||
.Concat(orderGenerator)
|
||||
.Select(r => r.PrepareRender(this));
|
||||
foreach (var renderAnnotation in World.OrderGenerator.RenderAnnotations(this, World))
|
||||
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
|
||||
}
|
||||
|
||||
public void PrepareRenderables()
|
||||
@@ -190,9 +233,11 @@ namespace OpenRA.Graphics
|
||||
|
||||
// PERF: Reuse collection to avoid allocations.
|
||||
onScreenActors.UnionWith(World.ScreenMap.RenderableActorsInBox(Viewport.TopLeft, Viewport.BottomRight));
|
||||
preparedRenderables.AddRange(GenerateRenderables());
|
||||
preparedOverlayRenderables.AddRange(GenerateOverlayRenderables());
|
||||
preparedAnnotationRenderables.AddRange(GenerateAnnotationRenderables());
|
||||
|
||||
GenerateRenderables();
|
||||
GenerateOverlayRenderables();
|
||||
GenerateAnnotationRenderables();
|
||||
|
||||
onScreenActors.Clear();
|
||||
}
|
||||
|
||||
@@ -213,8 +258,7 @@ namespace OpenRA.Graphics
|
||||
if (enableDepthBuffer)
|
||||
Game.Renderer.Context.EnableDepthBuffer();
|
||||
|
||||
if (terrainRenderer != null)
|
||||
terrainRenderer.RenderTerrain(this, Viewport);
|
||||
terrainRenderer?.RenderTerrain(this, Viewport);
|
||||
|
||||
Game.Renderer.Flush();
|
||||
|
||||
@@ -279,9 +323,12 @@ namespace OpenRA.Graphics
|
||||
|
||||
foreach (var b in World.ScreenMap.MouseBounds(World.RenderPlayer))
|
||||
{
|
||||
var points = b.Vertices
|
||||
.Select(p => Viewport.WorldToViewPx(p).ToFloat2())
|
||||
.ToArray();
|
||||
var points = new float2[b.Vertices.Length];
|
||||
for (var index = 0; index < b.Vertices.Length; index++)
|
||||
{
|
||||
var vertex = b.Vertices[index];
|
||||
points[index] = Viewport.WorldToViewPx(vertex).ToFloat2();
|
||||
}
|
||||
|
||||
Game.Renderer.RgbaColorRenderer.DrawPolygon(points, 1, Color.OrangeRed);
|
||||
}
|
||||
|
||||
@@ -50,8 +50,7 @@ namespace OpenRA
|
||||
return () => keys[name];
|
||||
|
||||
// Try and parse as a hardcoded definition
|
||||
Hotkey key;
|
||||
if (!Hotkey.TryParse(name, out key))
|
||||
if (!Hotkey.TryParse(name, out var key))
|
||||
key = Hotkey.Invalid;
|
||||
|
||||
return () => key;
|
||||
@@ -59,8 +58,7 @@ namespace OpenRA
|
||||
|
||||
public void Set(string name, Hotkey value)
|
||||
{
|
||||
HotkeyDefinition definition;
|
||||
if (!definitions.TryGetValue(name, out definition))
|
||||
if (!definitions.TryGetValue(name, out var definition))
|
||||
return;
|
||||
|
||||
keys[name] = value;
|
||||
|
||||
@@ -32,11 +32,9 @@ namespace OpenRA
|
||||
|
||||
var parts = s.Split(' ');
|
||||
|
||||
Keycode key;
|
||||
if (!Enum<Keycode>.TryParse(parts[0], true, out key))
|
||||
if (!Enum<Keycode>.TryParse(parts[0], true, out var key))
|
||||
{
|
||||
int c;
|
||||
if (!int.TryParse(parts[0], out c))
|
||||
if (!int.TryParse(parts[0], out var c))
|
||||
return false;
|
||||
key = (Keycode)c;
|
||||
}
|
||||
|
||||
@@ -498,8 +498,7 @@ namespace OpenRA
|
||||
|
||||
public static string DisplayString(Keycode k)
|
||||
{
|
||||
string ret;
|
||||
if (!KeyNames.TryGetValue(k, out ret))
|
||||
if (!KeyNames.TryGetValue(k, out var ret))
|
||||
return k.ToString();
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -14,7 +14,6 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
@@ -35,9 +34,9 @@ namespace OpenRA
|
||||
mods = GetInstalledMods(searchPaths, explicitPaths);
|
||||
}
|
||||
|
||||
static IEnumerable<Pair<string, string>> GetCandidateMods(IEnumerable<string> searchPaths)
|
||||
static IEnumerable<(string Id, string Path)> GetCandidateMods(IEnumerable<string> searchPaths)
|
||||
{
|
||||
var mods = new List<Pair<string, string>>();
|
||||
var mods = new List<(string, string)>();
|
||||
foreach (var path in searchPaths)
|
||||
{
|
||||
try
|
||||
@@ -48,7 +47,7 @@ namespace OpenRA
|
||||
|
||||
var directory = new DirectoryInfo(resolved);
|
||||
foreach (var subdir in directory.EnumerateDirectories())
|
||||
mods.Add(Pair.New(subdir.Name, subdir.FullName));
|
||||
mods.Add((subdir.Name, subdir.FullName));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -79,8 +78,7 @@ namespace OpenRA
|
||||
Log.Write("debug", "Load mod '{0}': {1}".F(path, e));
|
||||
}
|
||||
|
||||
if (package != null)
|
||||
package.Dispose();
|
||||
package?.Dispose();
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -89,13 +87,13 @@ namespace OpenRA
|
||||
{
|
||||
var ret = new Dictionary<string, Manifest>();
|
||||
var candidates = GetCandidateMods(searchPaths)
|
||||
.Concat(explicitPaths.Select(p => Pair.New(Path.GetFileNameWithoutExtension(p), p)));
|
||||
.Concat(explicitPaths.Select(p => (Id: Path.GetFileNameWithoutExtension(p), Path: p)));
|
||||
|
||||
foreach (var pair in candidates)
|
||||
{
|
||||
var mod = LoadMod(pair.First, pair.Second);
|
||||
var mod = LoadMod(pair.Id, pair.Path);
|
||||
if (mod != null)
|
||||
ret[pair.First] = mod;
|
||||
ret[pair.Id] = mod;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -80,8 +80,6 @@ namespace OpenRA
|
||||
{
|
||||
try
|
||||
{
|
||||
innerState = LinkState.Unlinked;
|
||||
|
||||
if (i.Error != null)
|
||||
{
|
||||
innerState = LinkState.ConnectionFailed;
|
||||
@@ -100,6 +98,8 @@ namespace OpenRA
|
||||
else
|
||||
innerState = LinkState.Linked;
|
||||
}
|
||||
else
|
||||
innerState = LinkState.Unlinked;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -108,8 +108,7 @@ namespace OpenRA
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (onComplete != null)
|
||||
onComplete();
|
||||
onComplete?.Invoke();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
/// <summary> Describes what is to be loaded in order to run a mod. </summary>
|
||||
public class Manifest
|
||||
public class Manifest : IDisposable
|
||||
{
|
||||
public readonly string Id;
|
||||
public readonly IReadOnlyPackage Package;
|
||||
@@ -96,8 +96,7 @@ namespace OpenRA
|
||||
// TODO: Use fieldloader
|
||||
MapFolders = YamlDictionary(yaml, "MapFolders");
|
||||
|
||||
MiniYaml packages;
|
||||
if (yaml.TryGetValue("Packages", out packages))
|
||||
if (yaml.TryGetValue("Packages", out var packages))
|
||||
Packages = packages.ToDictionary(x => x.Value).AsReadOnly();
|
||||
|
||||
Rules = YamlList(yaml, "Rules");
|
||||
@@ -217,9 +216,8 @@ namespace OpenRA
|
||||
/// </summary>
|
||||
public T Get<T>(ObjectCreator oc) where T : IGlobalModData
|
||||
{
|
||||
MiniYaml data;
|
||||
var t = typeof(T);
|
||||
if (!yaml.TryGetValue(t.Name, out data))
|
||||
if (!yaml.TryGetValue(t.Name, out var data))
|
||||
{
|
||||
// Lazily create the default values if not explicitly defined.
|
||||
return (T)oc.CreateBasic(t);
|
||||
@@ -241,5 +239,14 @@ namespace OpenRA
|
||||
|
||||
return (T)module;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var module in modules)
|
||||
{
|
||||
var disposableModule = module as IDisposable;
|
||||
disposableModule?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,17 +9,29 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public interface IActorInitializer
|
||||
{
|
||||
World World { get; }
|
||||
T Get<T>() where T : IActorInit;
|
||||
U Get<T, U>() where T : IActorInit<U>;
|
||||
bool Contains<T>() where T : IActorInit;
|
||||
T GetOrDefault<T>(TraitInfo info) where T : ActorInit;
|
||||
T Get<T>(TraitInfo info) where T : ActorInit;
|
||||
U GetValue<T, U>(TraitInfo info) where T : ValueActorInit<U>;
|
||||
U GetValue<T, U>(TraitInfo info, U fallback) where T : ValueActorInit<U>;
|
||||
bool Contains<T>(TraitInfo info) where T : ActorInit;
|
||||
|
||||
T GetOrDefault<T>() where T : ActorInit, ISingleInstanceInit;
|
||||
T Get<T>() where T : ActorInit, ISingleInstanceInit;
|
||||
U GetValue<T, U>() where T : ValueActorInit<U>, ISingleInstanceInit;
|
||||
U GetValue<T, U>(U fallback) where T : ValueActorInit<U>, ISingleInstanceInit;
|
||||
bool Contains<T>() where T : ActorInit, ISingleInstanceInit;
|
||||
}
|
||||
|
||||
public class ActorInitializer : IActorInitializer
|
||||
@@ -35,50 +47,228 @@ namespace OpenRA
|
||||
Dict = dict;
|
||||
}
|
||||
|
||||
public T Get<T>() where T : IActorInit { return Dict.Get<T>(); }
|
||||
public U Get<T, U>() where T : IActorInit<U> { return Dict.Get<T>().Value(World); }
|
||||
public bool Contains<T>() where T : IActorInit { return Dict.Contains<T>(); }
|
||||
}
|
||||
|
||||
public interface IActorInit { }
|
||||
|
||||
public interface IActorInit<T> : IActorInit
|
||||
{
|
||||
T Value(World world);
|
||||
}
|
||||
|
||||
public class LocationInit : IActorInit<CPos>
|
||||
{
|
||||
[FieldFromYamlKey]
|
||||
readonly CPos value = CPos.Zero;
|
||||
|
||||
public LocationInit() { }
|
||||
public LocationInit(CPos init) { value = init; }
|
||||
public CPos Value(World world) { return value; }
|
||||
}
|
||||
|
||||
public class OwnerInit : IActorInit<Player>
|
||||
{
|
||||
[FieldFromYamlKey]
|
||||
public readonly string PlayerName = "Neutral";
|
||||
|
||||
Player player;
|
||||
|
||||
public OwnerInit() { }
|
||||
public OwnerInit(string playerName) { PlayerName = playerName; }
|
||||
|
||||
public OwnerInit(Player player)
|
||||
public T GetOrDefault<T>(TraitInfo info) where T : ActorInit
|
||||
{
|
||||
this.player = player;
|
||||
PlayerName = player.InternalName;
|
||||
var inits = Dict.WithInterface<T>();
|
||||
|
||||
// Traits tagged with an instance name prefer inits with the same name.
|
||||
// If a more specific init is not available, fall back to an unnamed init.
|
||||
// If duplicate inits are defined, take the last to match standard yaml override expectations
|
||||
if (info != null && !string.IsNullOrEmpty(info.InstanceName))
|
||||
return inits.LastOrDefault(i => i.InstanceName == info.InstanceName) ??
|
||||
inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||
|
||||
// Untagged traits will only use untagged inits
|
||||
return inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||
}
|
||||
|
||||
public T Get<T>(TraitInfo info) where T : ActorInit
|
||||
{
|
||||
var init = GetOrDefault<T>(info);
|
||||
if (init == null)
|
||||
throw new InvalidOperationException("TypeDictionary does not contain instance of type `{0}`".F(typeof(T)));
|
||||
|
||||
return init;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(TraitInfo info) where T : ValueActorInit<U>
|
||||
{
|
||||
return Get<T>(info).Value;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(TraitInfo info, U fallback) where T : ValueActorInit<U>
|
||||
{
|
||||
var init = GetOrDefault<T>(info);
|
||||
return init != null ? init.Value : fallback;
|
||||
}
|
||||
|
||||
public bool Contains<T>(TraitInfo info) where T : ActorInit { return GetOrDefault<T>(info) != null; }
|
||||
|
||||
public T GetOrDefault<T>() where T : ActorInit, ISingleInstanceInit
|
||||
{
|
||||
return Dict.GetOrDefault<T>();
|
||||
}
|
||||
|
||||
public T Get<T>() where T : ActorInit, ISingleInstanceInit
|
||||
{
|
||||
return Dict.Get<T>();
|
||||
}
|
||||
|
||||
public U GetValue<T, U>() where T : ValueActorInit<U>, ISingleInstanceInit
|
||||
{
|
||||
return Get<T>().Value;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(U fallback) where T : ValueActorInit<U>, ISingleInstanceInit
|
||||
{
|
||||
var init = GetOrDefault<T>();
|
||||
return init != null ? init.Value : fallback;
|
||||
}
|
||||
|
||||
public bool Contains<T>() where T : ActorInit, ISingleInstanceInit { return GetOrDefault<T>() != null; }
|
||||
}
|
||||
|
||||
/*
|
||||
* Things to be aware of when writing ActorInits:
|
||||
*
|
||||
* - ActorReference and ActorGlobal can dynamically create objects without calling a constructor.
|
||||
* The object will be allocated directly then the best matching Initialize() method will be called to set valid state.
|
||||
* - ActorReference will always attempt to call Initialize(MiniYaml). ActorGlobal will use whichever one it first
|
||||
* finds with an argument type that matches the given LuaValue.
|
||||
* - Most ActorInits will want to inherit either ValueActorInit<T> or CompositeActorInit which hide the low-level plumbing.
|
||||
* - Inits that reference actors should use ActorInitActorReference which allows actors to be referenced by name in map.yaml
|
||||
* - Inits that should only have a single instance defined on an actor should implement ISingleInstanceInit to allow
|
||||
* direct queries and runtime enforcement.
|
||||
* - Inits that aren't ISingleInstanceInit should expose a ctor that accepts a TraitInfo to allow per-trait targeting.
|
||||
*/
|
||||
public abstract class ActorInit
|
||||
{
|
||||
[FieldLoader.Ignore]
|
||||
public readonly string InstanceName;
|
||||
|
||||
protected ActorInit(string instanceName)
|
||||
{
|
||||
InstanceName = instanceName;
|
||||
}
|
||||
|
||||
protected ActorInit() { }
|
||||
|
||||
public abstract MiniYaml Save();
|
||||
}
|
||||
|
||||
public interface ISingleInstanceInit { }
|
||||
|
||||
public abstract class ValueActorInit<T> : ActorInit
|
||||
{
|
||||
protected readonly T value;
|
||||
|
||||
protected ValueActorInit(TraitInfo info, T value)
|
||||
: base(info.InstanceName) { this.value = value; }
|
||||
|
||||
protected ValueActorInit(string instanceName, T value)
|
||||
: base(instanceName) { this.value = value; }
|
||||
|
||||
protected ValueActorInit(T value) { this.value = value; }
|
||||
|
||||
public virtual T Value { get { return value; } }
|
||||
|
||||
public virtual void Initialize(MiniYaml yaml)
|
||||
{
|
||||
Initialize((T)FieldLoader.GetValue("value", typeof(T), yaml.Value));
|
||||
}
|
||||
|
||||
public virtual void Initialize(T value)
|
||||
{
|
||||
var field = GetType().GetField("value", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (field != null)
|
||||
field.SetValue(this, value);
|
||||
}
|
||||
|
||||
public override MiniYaml Save()
|
||||
{
|
||||
return new MiniYaml(FieldSaver.FormatValue(value));
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class CompositeActorInit : ActorInit
|
||||
{
|
||||
protected CompositeActorInit(TraitInfo info)
|
||||
: base(info.InstanceName) { }
|
||||
|
||||
protected CompositeActorInit()
|
||||
: base() { }
|
||||
|
||||
public virtual void Initialize(MiniYaml yaml)
|
||||
{
|
||||
FieldLoader.Load(this, yaml);
|
||||
}
|
||||
|
||||
public virtual void Initialize(Dictionary<string, object> values)
|
||||
{
|
||||
foreach (var field in GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
|
||||
{
|
||||
var sa = field.GetCustomAttributes<FieldLoader.SerializeAttribute>(false).DefaultIfEmpty(FieldLoader.SerializeAttribute.Default).First();
|
||||
if (!sa.Serialize)
|
||||
continue;
|
||||
|
||||
if (values.TryGetValue(field.Name, out var value))
|
||||
field.SetValue(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Dictionary<string, Type> InitializeArgs()
|
||||
{
|
||||
var dict = new Dictionary<string, Type>();
|
||||
foreach (var field in GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
|
||||
{
|
||||
var sa = field.GetCustomAttributes<FieldLoader.SerializeAttribute>(false).DefaultIfEmpty(FieldLoader.SerializeAttribute.Default).First();
|
||||
if (!sa.Serialize)
|
||||
continue;
|
||||
|
||||
dict[field.Name] = field.FieldType;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
public override MiniYaml Save()
|
||||
{
|
||||
return FieldSaver.Save(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class LocationInit : ValueActorInit<CPos>, ISingleInstanceInit
|
||||
{
|
||||
public LocationInit(CPos value)
|
||||
: base(value) { }
|
||||
}
|
||||
|
||||
public class OwnerInit : ActorInit, ISingleInstanceInit
|
||||
{
|
||||
public readonly string InternalName;
|
||||
protected readonly Player value;
|
||||
|
||||
public OwnerInit(Player value)
|
||||
{
|
||||
this.value = value;
|
||||
InternalName = value.InternalName;
|
||||
}
|
||||
|
||||
public OwnerInit(string value)
|
||||
{
|
||||
InternalName = value;
|
||||
}
|
||||
|
||||
public Player Value(World world)
|
||||
{
|
||||
if (player != null)
|
||||
return player;
|
||||
return value ?? world.Players.First(x => x.InternalName == InternalName);
|
||||
}
|
||||
|
||||
return world.Players.First(x => x.InternalName == PlayerName);
|
||||
public void Initialize(MiniYaml yaml)
|
||||
{
|
||||
var field = GetType().GetField("InternalName", BindingFlags.Public | BindingFlags.Instance);
|
||||
if (field != null)
|
||||
field.SetValue(this, yaml.Value);
|
||||
}
|
||||
|
||||
public void Initialize(Player player)
|
||||
{
|
||||
var field = GetType().GetField("value", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (field != null)
|
||||
field.SetValue(this, player);
|
||||
}
|
||||
|
||||
public override MiniYaml Save()
|
||||
{
|
||||
return new MiniYaml(InternalName);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class RuntimeFlagInit : ActorInit, ISuppressInitExport
|
||||
{
|
||||
public override MiniYaml Save()
|
||||
{
|
||||
throw new NotImplementedException("RuntimeFlagInit cannot be saved");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,11 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
@@ -21,13 +25,10 @@ namespace OpenRA
|
||||
public class ActorReference : IEnumerable
|
||||
{
|
||||
public string Type;
|
||||
public TypeDictionary InitDict
|
||||
{
|
||||
get { return initDict.Value; }
|
||||
}
|
||||
|
||||
Lazy<TypeDictionary> initDict;
|
||||
|
||||
internal TypeDictionary InitDict { get { return initDict.Value; } }
|
||||
|
||||
public ActorReference(string type)
|
||||
: this(type, new Dictionary<string, MiniYaml>()) { }
|
||||
|
||||
@@ -38,47 +39,168 @@ namespace OpenRA
|
||||
{
|
||||
var dict = new TypeDictionary();
|
||||
foreach (var i in inits)
|
||||
dict.Add(LoadInit(i.Key, i.Value));
|
||||
{
|
||||
var init = LoadInit(i.Key, i.Value);
|
||||
if (init is ISingleInstanceInit && dict.Contains(init.GetType()))
|
||||
throw new InvalidDataException("Duplicate initializer '{0}'".F(init.GetType().Name));
|
||||
|
||||
dict.Add(init);
|
||||
}
|
||||
|
||||
return dict;
|
||||
});
|
||||
}
|
||||
|
||||
static IActorInit LoadInit(string traitName, MiniYaml my)
|
||||
public ActorReference(string type, TypeDictionary inits)
|
||||
{
|
||||
var info = Game.CreateObject<IActorInit>(traitName + "Init");
|
||||
FieldLoader.Load(info, my);
|
||||
return info;
|
||||
Type = type;
|
||||
initDict = new Lazy<TypeDictionary>(() =>
|
||||
{
|
||||
var dict = new TypeDictionary();
|
||||
foreach (var i in inits)
|
||||
dict.Add(i);
|
||||
return dict;
|
||||
});
|
||||
}
|
||||
|
||||
public MiniYaml Save(Func<object, bool> initFilter = null)
|
||||
static ActorInit LoadInit(string initName, MiniYaml initYaml)
|
||||
{
|
||||
var initInstance = initName.Split(ActorInfo.TraitInstanceSeparator);
|
||||
var type = Game.ModData.ObjectCreator.FindType(initInstance[0] + "Init");
|
||||
if (type == null)
|
||||
throw new InvalidDataException("Unknown initializer type '{0}Init'".F(initInstance[0]));
|
||||
|
||||
var init = (ActorInit)FormatterServices.GetUninitializedObject(type);
|
||||
if (initInstance.Length > 1)
|
||||
type.GetField("InstanceName").SetValue(init, initInstance[1]);
|
||||
|
||||
var loader = type.GetMethod("Initialize", new[] { typeof(MiniYaml) });
|
||||
if (loader == null)
|
||||
throw new InvalidDataException("{0}Init does not define a yaml-assignable type.".F(initInstance[0]));
|
||||
|
||||
loader.Invoke(init, new[] { initYaml });
|
||||
return init;
|
||||
}
|
||||
|
||||
public MiniYaml Save(Func<ActorInit, bool> initFilter = null)
|
||||
{
|
||||
var ret = new MiniYaml(Type);
|
||||
foreach (var init in InitDict)
|
||||
foreach (var o in initDict.Value)
|
||||
{
|
||||
if (init is ISuppressInitExport)
|
||||
var init = o as ActorInit;
|
||||
if (init == null || o is ISuppressInitExport)
|
||||
continue;
|
||||
|
||||
if (initFilter != null && !initFilter(init))
|
||||
continue;
|
||||
|
||||
var initName = init.GetType().Name;
|
||||
ret.Nodes.Add(new MiniYamlNode(initName.Substring(0, initName.Length - 4), FieldSaver.Save(init)));
|
||||
var initTypeName = init.GetType().Name;
|
||||
var initName = initTypeName.Substring(0, initTypeName.Length - 4);
|
||||
if (!string.IsNullOrEmpty(init.InstanceName))
|
||||
initName += ActorInfo.TraitInstanceSeparator + init.InstanceName;
|
||||
|
||||
ret.Nodes.Add(new MiniYamlNode(initName, init.Save()));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// for initialization syntax
|
||||
public void Add(object o) { InitDict.Add(o); }
|
||||
public IEnumerator GetEnumerator() { return InitDict.GetEnumerator(); }
|
||||
public IEnumerator GetEnumerator() { return initDict.Value.GetEnumerator(); }
|
||||
|
||||
public ActorReference Clone()
|
||||
{
|
||||
var clone = new ActorReference(Type);
|
||||
foreach (var init in InitDict)
|
||||
clone.InitDict.Add(init);
|
||||
foreach (var init in initDict.Value)
|
||||
clone.initDict.Value.Add(init);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
public void Add(ActorInit init)
|
||||
{
|
||||
if (init is ISingleInstanceInit && InitDict.Contains(init.GetType()))
|
||||
throw new InvalidDataException("Duplicate initializer '{0}'".F(init.GetType().Name));
|
||||
|
||||
InitDict.Add(init);
|
||||
}
|
||||
|
||||
public void Remove(ActorInit o) { initDict.Value.Remove(o); }
|
||||
|
||||
public int RemoveAll<T>() where T : ActorInit
|
||||
{
|
||||
var removed = 0;
|
||||
foreach (var o in initDict.Value.WithInterface<T>().ToList())
|
||||
{
|
||||
removed++;
|
||||
initDict.Value.Remove(o);
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetAll<T>() where T : ActorInit
|
||||
{
|
||||
return initDict.Value.WithInterface<T>();
|
||||
}
|
||||
|
||||
public T GetOrDefault<T>(TraitInfo info) where T : ActorInit
|
||||
{
|
||||
var inits = initDict.Value.WithInterface<T>();
|
||||
|
||||
// Traits tagged with an instance name prefer inits with the same name.
|
||||
// If a more specific init is not available, fall back to an unnamed init.
|
||||
// If duplicate inits are defined, take the last to match standard yaml override expectations
|
||||
if (info != null && !string.IsNullOrEmpty(info.InstanceName))
|
||||
return inits.LastOrDefault(i => i.InstanceName == info.InstanceName) ??
|
||||
inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||
|
||||
// Untagged traits will only use untagged inits
|
||||
return inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||
}
|
||||
|
||||
public T Get<T>(TraitInfo info) where T : ActorInit
|
||||
{
|
||||
var init = GetOrDefault<T>(info);
|
||||
if (init == null)
|
||||
throw new InvalidOperationException("TypeDictionary does not contain instance of type `{0}`".F(typeof(T)));
|
||||
|
||||
return init;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(TraitInfo info) where T : ValueActorInit<U>
|
||||
{
|
||||
return Get<T>(info).Value;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(TraitInfo info, U fallback) where T : ValueActorInit<U>
|
||||
{
|
||||
var init = GetOrDefault<T>(info);
|
||||
return init != null ? init.Value : fallback;
|
||||
}
|
||||
|
||||
public bool Contains<T>(TraitInfo info) where T : ActorInit { return GetOrDefault<T>(info) != null; }
|
||||
|
||||
public T GetOrDefault<T>() where T : ActorInit, ISingleInstanceInit
|
||||
{
|
||||
return initDict.Value.GetOrDefault<T>();
|
||||
}
|
||||
|
||||
public T Get<T>() where T : ActorInit, ISingleInstanceInit
|
||||
{
|
||||
return initDict.Value.Get<T>();
|
||||
}
|
||||
|
||||
public U GetValue<T, U>() where T : ValueActorInit<U>, ISingleInstanceInit
|
||||
{
|
||||
return Get<T>().Value;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(U fallback) where T : ValueActorInit<U>, ISingleInstanceInit
|
||||
{
|
||||
var init = GetOrDefault<T>();
|
||||
return init != null ? init.Value : fallback;
|
||||
}
|
||||
|
||||
public bool Contains<T>() where T : ActorInit, ISingleInstanceInit { return GetOrDefault<T>() != null; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,42 +10,28 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
// Represents a layer of "something" that covers the map
|
||||
public class CellLayer<T> : IEnumerable<T>
|
||||
public sealed class CellLayer<T> : CellLayerBase<T>
|
||||
{
|
||||
public readonly Size Size;
|
||||
readonly Rectangle bounds;
|
||||
public readonly MapGridType GridType;
|
||||
public event Action<CPos> CellEntryChanged = null;
|
||||
|
||||
readonly T[] entries;
|
||||
|
||||
public CellLayer(Map map)
|
||||
: this(map.Grid.Type, new Size(map.MapSize.X, map.MapSize.Y)) { }
|
||||
: base(map) { }
|
||||
|
||||
public CellLayer(MapGridType gridType, Size size)
|
||||
{
|
||||
Size = size;
|
||||
bounds = new Rectangle(0, 0, Size.Width, Size.Height);
|
||||
GridType = gridType;
|
||||
entries = new T[size.Width * size.Height];
|
||||
}
|
||||
: base(gridType, size) { }
|
||||
|
||||
public void CopyValuesFrom(CellLayer<T> anotherLayer)
|
||||
public override void CopyValuesFrom(CellLayerBase<T> anotherLayer)
|
||||
{
|
||||
if (Size != anotherLayer.Size || GridType != anotherLayer.GridType)
|
||||
throw new ArgumentException(
|
||||
"layers must have a matching size and shape (grid type).", "anotherLayer");
|
||||
if (CellEntryChanged != null)
|
||||
throw new InvalidOperationException(
|
||||
"Cannot copy values when there are listeners attached to the CellEntryChanged event.");
|
||||
Array.Copy(anotherLayer.entries, entries, entries.Length);
|
||||
|
||||
base.CopyValuesFrom(anotherLayer);
|
||||
}
|
||||
|
||||
public static CellLayer<T> CreateInstance(Func<MPos, T> initialCellValueFactory, Size size, MapGridType mapGridType)
|
||||
@@ -87,8 +73,7 @@ namespace OpenRA
|
||||
{
|
||||
entries[Index(cell)] = value;
|
||||
|
||||
if (CellEntryChanged != null)
|
||||
CellEntryChanged(cell);
|
||||
CellEntryChanged?.Invoke(cell);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,28 +89,10 @@ namespace OpenRA
|
||||
{
|
||||
entries[Index(uv)] = value;
|
||||
|
||||
if (CellEntryChanged != null)
|
||||
CellEntryChanged(uv.ToCPos(GridType));
|
||||
CellEntryChanged?.Invoke(uv.ToCPos(GridType));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Clears the layer contents with a known value</summary>
|
||||
public void Clear(T clearValue)
|
||||
{
|
||||
for (var i = 0; i < entries.Length; i++)
|
||||
entries[i] = clearValue;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<T>)entries).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public bool Contains(CPos cell)
|
||||
{
|
||||
// .ToMPos() returns the same result if the X and Y coordinates
|
||||
|
||||
63
OpenRA.Game/Map/CellLayerBase.cs
Normal file
63
OpenRA.Game/Map/CellLayerBase.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
#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;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public abstract class CellLayerBase<T> : IEnumerable<T>
|
||||
{
|
||||
public readonly Size Size;
|
||||
public readonly MapGridType GridType;
|
||||
|
||||
protected readonly T[] entries;
|
||||
protected readonly Rectangle bounds;
|
||||
|
||||
public CellLayerBase(Map map)
|
||||
: this(map.Grid.Type, new Size(map.MapSize.X, map.MapSize.Y)) { }
|
||||
|
||||
public CellLayerBase(MapGridType gridType, Size size)
|
||||
{
|
||||
Size = size;
|
||||
bounds = new Rectangle(0, 0, Size.Width, Size.Height);
|
||||
GridType = gridType;
|
||||
entries = new T[size.Width * size.Height];
|
||||
}
|
||||
|
||||
public virtual void CopyValuesFrom(CellLayerBase<T> anotherLayer)
|
||||
{
|
||||
if (Size != anotherLayer.Size || GridType != anotherLayer.GridType)
|
||||
throw new ArgumentException("Layers must have a matching size and shape (grid type).", "anotherLayer");
|
||||
|
||||
Array.Copy(anotherLayer.entries, entries, entries.Length);
|
||||
}
|
||||
|
||||
/// <summary>Clears the layer contents with a known value</summary>
|
||||
public void Clear(T clearValue)
|
||||
{
|
||||
for (var i = 0; i < entries.Length; i++)
|
||||
entries[i] = clearValue;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<T>)entries).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,7 @@ namespace OpenRA
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public sealed class CellRegionEnumerator : IEnumerator<CPos>
|
||||
public struct CellRegionEnumerator : IEnumerator<CPos>
|
||||
{
|
||||
readonly CellRegion r;
|
||||
|
||||
@@ -128,9 +128,11 @@ namespace OpenRA
|
||||
CPos current;
|
||||
|
||||
public CellRegionEnumerator(CellRegion region)
|
||||
: this()
|
||||
{
|
||||
r = region;
|
||||
Reset();
|
||||
current = new MPos(u, v).ToCPos(r.gridType);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
|
||||
@@ -230,9 +230,10 @@ namespace OpenRA
|
||||
public CellLayer<TerrainTile> Tiles { get; private set; }
|
||||
public CellLayer<ResourceTile> Resources { get; private set; }
|
||||
public CellLayer<byte> Height { get; private set; }
|
||||
public CellLayer<byte> Ramp { get; private set; }
|
||||
public CellLayer<byte> CustomTerrain { get; private set; }
|
||||
|
||||
public ProjectedCellRegion ProjectedCellBounds { get; private set; }
|
||||
public PPos[] ProjectedCells { get; private set; }
|
||||
public CellRegion AllCells { get; private set; }
|
||||
public List<CPos> AllEdgeCells { get; private set; }
|
||||
|
||||
@@ -301,10 +302,12 @@ namespace OpenRA
|
||||
Tiles = new CellLayer<TerrainTile>(Grid.Type, size);
|
||||
Resources = new CellLayer<ResourceTile>(Grid.Type, size);
|
||||
Height = new CellLayer<byte>(Grid.Type, size);
|
||||
Ramp = new CellLayer<byte>(Grid.Type, size);
|
||||
if (Grid.MaximumTerrainHeight > 0)
|
||||
{
|
||||
Height.CellEntryChanged += UpdateProjection;
|
||||
Tiles.CellEntryChanged += UpdateProjection;
|
||||
Tiles.CellEntryChanged += UpdateRamp;
|
||||
}
|
||||
|
||||
Tiles.Clear(tileRef);
|
||||
@@ -336,6 +339,7 @@ namespace OpenRA
|
||||
Tiles = new CellLayer<TerrainTile>(Grid.Type, size);
|
||||
Resources = new CellLayer<ResourceTile>(Grid.Type, size);
|
||||
Height = new CellLayer<byte>(Grid.Type, size);
|
||||
Ramp = new CellLayer<byte>(Grid.Type, size);
|
||||
|
||||
using (var s = Package.GetStream("map.bin"))
|
||||
{
|
||||
@@ -384,6 +388,7 @@ namespace OpenRA
|
||||
|
||||
if (Grid.MaximumTerrainHeight > 0)
|
||||
{
|
||||
Tiles.CellEntryChanged += UpdateRamp;
|
||||
Tiles.CellEntryChanged += UpdateProjection;
|
||||
Height.CellEntryChanged += UpdateProjection;
|
||||
}
|
||||
@@ -422,9 +427,23 @@ namespace OpenRA
|
||||
foreach (var uv in AllCells.MapCoords)
|
||||
CustomTerrain[uv] = byte.MaxValue;
|
||||
|
||||
// Cache initial ramp state
|
||||
var tileset = Rules.TileSet;
|
||||
foreach (var uv in AllCells)
|
||||
{
|
||||
var tile = tileset.GetTileInfo(Tiles[uv]);
|
||||
Ramp[uv] = tile != null ? tile.RampType : (byte)0;
|
||||
}
|
||||
|
||||
AllEdgeCells = UpdateEdgeCells();
|
||||
}
|
||||
|
||||
void UpdateRamp(CPos cell)
|
||||
{
|
||||
var tile = Rules.TileSet.GetTileInfo(Tiles[cell]);
|
||||
Ramp[cell] = tile != null ? tile.RampType : (byte)0;
|
||||
}
|
||||
|
||||
void InitializeCellProjection()
|
||||
{
|
||||
if (initializedCellProjection)
|
||||
@@ -531,12 +550,8 @@ namespace OpenRA
|
||||
return new[] { (PPos)uv };
|
||||
|
||||
// Odd-height ramps get bumped up a level to the next even height layer
|
||||
if ((height & 1) == 1)
|
||||
{
|
||||
var ti = Rules.TileSet.GetTileInfo(Tiles[uv]);
|
||||
if (ti != null && ti.RampType != 0)
|
||||
height += 1;
|
||||
}
|
||||
if ((height & 1) == 1 && Ramp[uv] != 0)
|
||||
height += 1;
|
||||
|
||||
var candidates = new List<PPos>();
|
||||
|
||||
@@ -647,12 +662,40 @@ namespace OpenRA
|
||||
return dataStream.ToArray();
|
||||
}
|
||||
|
||||
public (Color Left, Color Right) GetTerrainColorPair(MPos uv)
|
||||
{
|
||||
Color left, right;
|
||||
var tileset = Rules.TileSet;
|
||||
var type = tileset.GetTileInfo(Tiles[uv]);
|
||||
if (type != null)
|
||||
{
|
||||
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 = 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<Pair<MPos, Color>>();
|
||||
var positions = new List<(MPos Position, Color Color)>();
|
||||
foreach (var actor in actors)
|
||||
{
|
||||
var s = new ActorReference(actor.Value.Value, actor.Value.ToDictionary());
|
||||
@@ -686,24 +729,18 @@ namespace OpenRA
|
||||
var stride = bitmapWidth * 4;
|
||||
var pxStride = 4;
|
||||
var minimapData = new byte[stride * height];
|
||||
Color leftColor, rightColor;
|
||||
(Color Left, Color Right) terrainColor = default((Color, Color));
|
||||
|
||||
for (var y = 0; y < height; y++)
|
||||
{
|
||||
for (var x = 0; x < width; x++)
|
||||
{
|
||||
var uv = new MPos(x + Bounds.Left, y + Bounds.Top);
|
||||
var actorsThere = positions.Where(ap => ap.First == uv);
|
||||
if (actorsThere.Any())
|
||||
{
|
||||
leftColor = rightColor = actorsThere.First().Second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cell contains terrain
|
||||
var type = tileset.GetTileInfo(Tiles[uv]);
|
||||
leftColor = type != null ? type.LeftColor : Color.Black;
|
||||
rightColor = type != null ? type.RightColor : Color.Black;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
@@ -713,28 +750,31 @@ namespace OpenRA
|
||||
if (x + dx > 0)
|
||||
{
|
||||
var z = y * stride + xOffset - pxStride;
|
||||
minimapData[z++] = leftColor.R;
|
||||
minimapData[z++] = leftColor.G;
|
||||
minimapData[z++] = leftColor.B;
|
||||
minimapData[z++] = leftColor.A;
|
||||
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;
|
||||
minimapData[z++] = rightColor.R;
|
||||
minimapData[z++] = rightColor.G;
|
||||
minimapData[z++] = rightColor.B;
|
||||
minimapData[z++] = rightColor.A;
|
||||
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;
|
||||
minimapData[z++] = leftColor.R;
|
||||
minimapData[z++] = leftColor.G;
|
||||
minimapData[z++] = leftColor.B;
|
||||
minimapData[z++] = leftColor.A;
|
||||
var c = actorColor.A == 0 ? terrainColor.Left : actorColor;
|
||||
minimapData[z++] = c.R;
|
||||
minimapData[z++] = c.G;
|
||||
minimapData[z++] = c.B;
|
||||
minimapData[z] = c.A;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -770,8 +810,9 @@ namespace OpenRA
|
||||
|
||||
bool ContainsAllProjectedCellsCovering(MPos uv)
|
||||
{
|
||||
// PERF: Checking the bounds directly here is the same as calling Contains((PPos)uv) but saves an allocation
|
||||
if (Grid.MaximumTerrainHeight == 0)
|
||||
return Contains((PPos)uv);
|
||||
return Bounds.Contains(uv.U, uv.V);
|
||||
|
||||
// If the cell has no valid projection, then we're off the map.
|
||||
var projectedCells = ProjectedCellsCovering(uv);
|
||||
@@ -781,6 +822,7 @@ namespace OpenRA
|
||||
foreach (var puv in projectedCells)
|
||||
if (!Contains(puv))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -805,7 +847,7 @@ namespace OpenRA
|
||||
// (c) u, v coordinates run diagonally to the cell axes, and we define
|
||||
// 1024 as the length projected onto the primary cell axis
|
||||
// - 512 * sqrt(2) = 724
|
||||
var z = Height.Contains(cell) ? 724 * Height[cell] : 0;
|
||||
var z = Height.Contains(cell) ? 724 * Height[cell] + Grid.Ramps[Ramp[cell]].CenterHeightOffset : 0;
|
||||
return new WPos(724 * (cell.X - cell.Y + 1), 724 * (cell.X + cell.Y + 1), z);
|
||||
}
|
||||
|
||||
@@ -813,15 +855,42 @@ namespace OpenRA
|
||||
{
|
||||
var index = (int)subCell;
|
||||
if (index >= 0 && index < Grid.SubCellOffsets.Length)
|
||||
return CenterOfCell(cell) + Grid.SubCellOffsets[index];
|
||||
{
|
||||
var center = CenterOfCell(cell);
|
||||
var offset = Grid.SubCellOffsets[index];
|
||||
var ramp = Ramp.Contains(cell) ? Ramp[cell] : 0;
|
||||
if (ramp != 0)
|
||||
{
|
||||
var r = Grid.Ramps[ramp];
|
||||
offset += new WVec(0, 0, r.HeightOffset(offset.X, offset.Y) - r.CenterHeightOffset);
|
||||
}
|
||||
|
||||
return center + offset;
|
||||
}
|
||||
|
||||
return CenterOfCell(cell);
|
||||
}
|
||||
|
||||
public WDist DistanceAboveTerrain(WPos pos)
|
||||
{
|
||||
if (Grid.Type == MapGridType.Rectangular)
|
||||
return new WDist(pos.Z);
|
||||
|
||||
// Apply ramp offset
|
||||
var cell = CellContaining(pos);
|
||||
var delta = pos - CenterOfCell(cell);
|
||||
return new WDist(delta.Z);
|
||||
var offset = pos - CenterOfCell(cell);
|
||||
|
||||
if (!Ramp.Contains(cell))
|
||||
return new WDist(offset.Z);
|
||||
|
||||
var ramp = Ramp[cell];
|
||||
if (ramp != 0)
|
||||
{
|
||||
var r = Grid.Ramps[ramp];
|
||||
return new WDist(offset.Z + r.CenterHeightOffset - r.HeightOffset(offset.X, offset.Y));
|
||||
}
|
||||
|
||||
return new WDist(offset.Z);
|
||||
}
|
||||
|
||||
public WVec Offset(CVec delta, int dz)
|
||||
@@ -899,13 +968,13 @@ namespace OpenRA
|
||||
return projectedHeight[(MPos)puv];
|
||||
}
|
||||
|
||||
public int FacingBetween(CPos cell, CPos towards, int fallbackfacing)
|
||||
public WAngle FacingBetween(CPos cell, CPos towards, WAngle fallbackfacing)
|
||||
{
|
||||
var delta = CenterOfCell(towards) - CenterOfCell(cell);
|
||||
if (delta.HorizontalLengthSquared == 0)
|
||||
return fallbackfacing;
|
||||
|
||||
return delta.Yaw.Facing;
|
||||
return delta.Yaw;
|
||||
}
|
||||
|
||||
public void Resize(int width, int height)
|
||||
@@ -913,11 +982,13 @@ namespace OpenRA
|
||||
var oldMapTiles = Tiles;
|
||||
var oldMapResources = Resources;
|
||||
var oldMapHeight = Height;
|
||||
var oldMapRamp = Ramp;
|
||||
var newSize = new Size(width, height);
|
||||
|
||||
Tiles = CellLayer.Resize(oldMapTiles, newSize, oldMapTiles[MPos.Zero]);
|
||||
Resources = CellLayer.Resize(oldMapResources, newSize, oldMapResources[MPos.Zero]);
|
||||
Height = CellLayer.Resize(oldMapHeight, newSize, oldMapHeight[MPos.Zero]);
|
||||
Ramp = CellLayer.Resize(oldMapRamp, newSize, oldMapRamp[MPos.Zero]);
|
||||
MapSize = new int2(newSize);
|
||||
|
||||
var tl = new MPos(0, 0);
|
||||
@@ -947,7 +1018,8 @@ namespace OpenRA
|
||||
ProjectedBottomRight = new WPos(br.U * 1024 - 1, (br.V + 1) * 1024 - 1, 0);
|
||||
}
|
||||
|
||||
ProjectedCellBounds = new ProjectedCellRegion(this, tl, br);
|
||||
// PERF: This enumeration isn't going to change during the game
|
||||
ProjectedCells = new ProjectedCellRegion(this, tl, br).ToArray();
|
||||
}
|
||||
|
||||
public void FixOpenAreas()
|
||||
|
||||
@@ -111,8 +111,7 @@ namespace OpenRA
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (mapPackage != null)
|
||||
mapPackage.Dispose();
|
||||
mapPackage?.Dispose();
|
||||
Console.WriteLine("Failed to load map: {0}", map);
|
||||
Console.WriteLine("Details: {0}", e);
|
||||
Log.Write("debug", "Failed to load map: {0}", map);
|
||||
@@ -131,8 +130,7 @@ namespace OpenRA
|
||||
// Enumerate map directories
|
||||
foreach (var kv in modData.Manifest.MapFolders)
|
||||
{
|
||||
MapClassification packageClassification;
|
||||
if (!Enum.TryParse(kv.Value, out packageClassification))
|
||||
if (!Enum.TryParse(kv.Value, out MapClassification packageClassification))
|
||||
continue;
|
||||
|
||||
if (!classification.HasFlag(packageClassification))
|
||||
@@ -193,8 +191,7 @@ namespace OpenRA
|
||||
foreach (var p in maps.Values)
|
||||
p.UpdateRemoteSearch(MapStatus.Unavailable, null);
|
||||
|
||||
if (queryFailed != null)
|
||||
queryFailed();
|
||||
queryFailed?.Invoke();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -214,8 +211,7 @@ namespace OpenRA
|
||||
{
|
||||
Log.Write("debug", "Can't parse remote map search data:\n{0}", data);
|
||||
Log.Write("debug", "Exception: {0}", e);
|
||||
if (queryFailed != null)
|
||||
queryFailed();
|
||||
queryFailed?.Invoke();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -299,8 +295,7 @@ namespace OpenRA
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
// Wait for any existing thread to exit before starting a new one.
|
||||
if (previewLoaderThread != null)
|
||||
previewLoaderThread.Join();
|
||||
previewLoaderThread?.Join();
|
||||
|
||||
previewLoaderThread = new Thread(LoadAsyncInternal)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -20,6 +19,85 @@ namespace OpenRA
|
||||
{
|
||||
public enum MapGridType { Rectangular, RectangularIsometric }
|
||||
|
||||
public enum RampSplit { Flat, X, Y }
|
||||
public enum RampCornerHeight { Low = 0, Half = 1, Full = 2 }
|
||||
|
||||
public struct CellRamp
|
||||
{
|
||||
public readonly int CenterHeightOffset;
|
||||
public readonly WVec[] Corners;
|
||||
public readonly WVec[][] Polygons;
|
||||
|
||||
public CellRamp(MapGridType type, RampCornerHeight tl = RampCornerHeight.Low, RampCornerHeight tr = RampCornerHeight.Low, RampCornerHeight br = RampCornerHeight.Low, RampCornerHeight bl = RampCornerHeight.Low, RampSplit split = RampSplit.Flat)
|
||||
{
|
||||
if (type == MapGridType.RectangularIsometric)
|
||||
{
|
||||
Corners = new[]
|
||||
{
|
||||
new WVec(0, -724, 724 * (int)tl),
|
||||
new WVec(724, 0, 724 * (int)tr),
|
||||
new WVec(0, 724, 724 * (int)br),
|
||||
new WVec(-724, 0, 724 * (int)bl),
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
Corners = new[]
|
||||
{
|
||||
new WVec(-512, -512, 512 * (int)tl),
|
||||
new WVec(512, -512, 512 * (int)tr),
|
||||
new WVec(512, 512, 512 * (int)br),
|
||||
new WVec(-512, 512, 512 * (int)bl)
|
||||
};
|
||||
}
|
||||
|
||||
if (split == RampSplit.X)
|
||||
{
|
||||
Polygons = new[]
|
||||
{
|
||||
new[] { Corners[0], Corners[1], Corners[3] },
|
||||
new[] { Corners[1], Corners[2], Corners[3] }
|
||||
};
|
||||
}
|
||||
else if (split == RampSplit.Y)
|
||||
{
|
||||
Polygons = new[]
|
||||
{
|
||||
new[] { Corners[0], Corners[1], Corners[2] },
|
||||
new[] { Corners[0], Corners[2], Corners[3] }
|
||||
};
|
||||
}
|
||||
else
|
||||
Polygons = new[] { Corners };
|
||||
|
||||
// Initial value must be asigned before HeightOffset can be called
|
||||
CenterHeightOffset = 0;
|
||||
CenterHeightOffset = HeightOffset(0, 0);
|
||||
}
|
||||
|
||||
public int HeightOffset(int dX, int dY)
|
||||
{
|
||||
// Enumerate over the polygons, assuming that they are triangles
|
||||
// If the ramp is not split we will take the first three vertices of the corners as a valid triangle
|
||||
WVec[] p = null;
|
||||
var u = 0;
|
||||
var v = 0;
|
||||
for (var i = 0; i < Polygons.Length; i++)
|
||||
{
|
||||
p = Polygons[i];
|
||||
u = ((p[1].Y - p[2].Y) * (dX - p[2].X) - (p[1].X - p[2].X) * (dY - p[2].Y)) / 1024;
|
||||
v = ((p[0].X - p[2].X) * (dY - p[2].Y) - (p[0].Y - p[2].Y) * (dX - p[2].X)) / 1024;
|
||||
|
||||
// Point is within the triangle if 0 <= u,v <= 1024
|
||||
if (u >= 0 && u <= 1024 && v >= 0 && v <= 1024)
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate w from u,v and interpolate height
|
||||
return (u * p[0].Z + v * p[1].Z + (1024 - u - v) * p[2].Z) / 1024;
|
||||
}
|
||||
}
|
||||
|
||||
public class MapGrid : IGlobalModData
|
||||
{
|
||||
public readonly MapGridType Type = MapGridType.Rectangular;
|
||||
@@ -41,43 +119,7 @@ namespace OpenRA
|
||||
new WVec(256, 256, 0), // bottom right - index 5
|
||||
};
|
||||
|
||||
public WVec[][] CellCorners { get; private set; }
|
||||
|
||||
readonly int[][] cellCornerHalfHeights = new int[][]
|
||||
{
|
||||
// Flat
|
||||
new[] { 0, 0, 0, 0 },
|
||||
|
||||
// Slopes (two corners high)
|
||||
new[] { 0, 0, 1, 1 },
|
||||
new[] { 1, 0, 0, 1 },
|
||||
new[] { 1, 1, 0, 0 },
|
||||
new[] { 0, 1, 1, 0 },
|
||||
|
||||
// Slopes (one corner high)
|
||||
new[] { 0, 0, 0, 1 },
|
||||
new[] { 1, 0, 0, 0 },
|
||||
new[] { 0, 1, 0, 0 },
|
||||
new[] { 0, 0, 1, 0 },
|
||||
|
||||
// Slopes (three corners high)
|
||||
new[] { 1, 0, 1, 1 },
|
||||
new[] { 1, 1, 0, 1 },
|
||||
new[] { 1, 1, 1, 0 },
|
||||
new[] { 0, 1, 1, 1 },
|
||||
|
||||
// Slopes (two corners high, one corner double high)
|
||||
new[] { 1, 0, 1, 2 },
|
||||
new[] { 2, 1, 0, 1 },
|
||||
new[] { 1, 2, 1, 0 },
|
||||
new[] { 0, 1, 2, 1 },
|
||||
|
||||
// Slopes (two corners high, alternating)
|
||||
new[] { 1, 0, 1, 0 },
|
||||
new[] { 0, 1, 0, 1 },
|
||||
new[] { 1, 0, 1, 0 },
|
||||
new[] { 0, 1, 0, 1 }
|
||||
};
|
||||
public CellRamp[] Ramps { get; private set; }
|
||||
|
||||
internal readonly CVec[][] TilesByDistance;
|
||||
|
||||
@@ -96,34 +138,46 @@ namespace OpenRA
|
||||
throw new InvalidDataException("Subcell default index must be a valid index into the offset triples and must be greater than 0 for mods with subcells");
|
||||
}
|
||||
|
||||
var makeCorners = Type == MapGridType.RectangularIsometric ?
|
||||
(Func<int[], WVec[]>)IsometricCellCorners : RectangularCellCorners;
|
||||
CellCorners = cellCornerHalfHeights.Select(makeCorners).ToArray();
|
||||
// Slope types are hardcoded following the convention from the TS and RA2 map format
|
||||
Ramps = new[]
|
||||
{
|
||||
// Flat
|
||||
new CellRamp(Type),
|
||||
|
||||
// Two adjacent corners raised by half a cell
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, br: RampCornerHeight.Half),
|
||||
new CellRamp(Type, br: RampCornerHeight.Half, bl: RampCornerHeight.Half),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, bl: RampCornerHeight.Half),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Half),
|
||||
|
||||
// One corner raised by half a cell
|
||||
new CellRamp(Type, br: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, bl: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
|
||||
// Three corners raised by half a cell
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
|
||||
// Full tile sloped (mid corners raised by half cell, far corner by full cell)
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, br: RampCornerHeight.Full, bl: RampCornerHeight.Half),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Full),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Full, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Full, br: RampCornerHeight.Half),
|
||||
|
||||
// Two opposite corners raised by half a cell
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.X),
|
||||
};
|
||||
|
||||
TilesByDistance = CreateTilesByDistance();
|
||||
}
|
||||
|
||||
static WVec[] IsometricCellCorners(int[] cornerHeight)
|
||||
{
|
||||
return new WVec[]
|
||||
{
|
||||
new WVec(-724, 0, 724 * cornerHeight[0]),
|
||||
new WVec(0, -724, 724 * cornerHeight[1]),
|
||||
new WVec(724, 0, 724 * cornerHeight[2]),
|
||||
new WVec(0, 724, 724 * cornerHeight[3])
|
||||
};
|
||||
}
|
||||
|
||||
static WVec[] RectangularCellCorners(int[] cornerHeight)
|
||||
{
|
||||
return new WVec[]
|
||||
{
|
||||
new WVec(-512, -512, 512 * cornerHeight[0]),
|
||||
new WVec(512, -512, 512 * cornerHeight[1]),
|
||||
new WVec(512, 512, 512 * cornerHeight[2]),
|
||||
new WVec(-512, 512, 512 * cornerHeight[3])
|
||||
};
|
||||
}
|
||||
|
||||
CVec[][] CreateTilesByDistance()
|
||||
{
|
||||
var ts = new List<CVec>[MaximumTileSearchRange + 1];
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace OpenRA
|
||||
public bool DefinesUnsafeCustomRules { get; private set; }
|
||||
public bool RulesLoaded { get; private set; }
|
||||
|
||||
public void SetRulesetGenerator(ModData modData, Func<Pair<Ruleset, bool>> generator)
|
||||
public void SetRulesetGenerator(ModData modData, Func<(Ruleset Ruleset, bool DefinesUnsafeCustomRules)> generator)
|
||||
{
|
||||
InvalidCustomRules = false;
|
||||
RulesLoaded = false;
|
||||
@@ -106,8 +106,8 @@ namespace OpenRA
|
||||
try
|
||||
{
|
||||
var ret = generator();
|
||||
DefinesUnsafeCustomRules = ret.Second;
|
||||
return ret.First;
|
||||
DefinesUnsafeCustomRules = ret.DefinesUnsafeCustomRules;
|
||||
return ret.Ruleset;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -233,8 +233,7 @@ namespace OpenRA
|
||||
newData.GridType = gridType;
|
||||
newData.Class = classification;
|
||||
|
||||
MiniYaml temp;
|
||||
if (yaml.TryGetValue("MapFormat", out temp))
|
||||
if (yaml.TryGetValue("MapFormat", out var temp))
|
||||
{
|
||||
var format = FieldLoader.GetValue<int>("MapFormat", temp.Value);
|
||||
if (format != Map.SupportedMapFormat)
|
||||
@@ -269,14 +268,13 @@ namespace OpenRA
|
||||
try
|
||||
{
|
||||
// Actor definitions may change if the map format changes
|
||||
MiniYaml actorDefinitions;
|
||||
if (yaml.TryGetValue("Actors", out actorDefinitions))
|
||||
if (yaml.TryGetValue("Actors", out var actorDefinitions))
|
||||
{
|
||||
var spawns = new List<CPos>();
|
||||
foreach (var kv in actorDefinitions.Nodes.Where(d => d.Value.Value == "mpspawn"))
|
||||
{
|
||||
var s = new ActorReference(kv.Value.Value, kv.Value.ToDictionary());
|
||||
spawns.Add(s.InitDict.Get<LocationInit>().Value(null));
|
||||
spawns.Add(s.Get<LocationInit>().Value);
|
||||
}
|
||||
|
||||
newData.SpawnPoints = spawns.ToArray();
|
||||
@@ -293,8 +291,7 @@ namespace OpenRA
|
||||
try
|
||||
{
|
||||
// Player definitions may change if the map format changes
|
||||
MiniYaml playerDefinitions;
|
||||
if (yaml.TryGetValue("Players", out playerDefinitions))
|
||||
if (yaml.TryGetValue("Players", out var playerDefinitions))
|
||||
{
|
||||
newData.Players = new MapPlayers(playerDefinitions.Nodes);
|
||||
newData.PlayerCount = newData.Players.Players.Count(x => x.Value.Playable);
|
||||
@@ -318,7 +315,7 @@ namespace OpenRA
|
||||
voiceDefinitions, notificationDefinitions, musicDefinitions, sequenceDefinitions, modelSequenceDefinitions);
|
||||
var flagged = Ruleset.DefinesUnsafeCustomRules(modData, this, ruleDefinitions,
|
||||
weaponDefinitions, voiceDefinitions, notificationDefinitions, sequenceDefinitions);
|
||||
return Pair.New(rules, flagged);
|
||||
return (rules, flagged);
|
||||
});
|
||||
|
||||
if (p.Contains("map.png"))
|
||||
@@ -331,8 +328,7 @@ namespace OpenRA
|
||||
|
||||
MiniYaml LoadRuleSection(Dictionary<string, MiniYaml> yaml, string section)
|
||||
{
|
||||
MiniYaml node;
|
||||
if (!yaml.TryGetValue(section, out node))
|
||||
if (!yaml.TryGetValue(section, out var node))
|
||||
return null;
|
||||
|
||||
return node;
|
||||
@@ -402,7 +398,7 @@ namespace OpenRA
|
||||
voiceDefinitions, notificationDefinitions, musicDefinitions, sequenceDefinitions, modelSequenceDefinitions);
|
||||
var flagged = Ruleset.DefinesUnsafeCustomRules(modData, this, ruleDefinitions,
|
||||
weaponDefinitions, voiceDefinitions, notificationDefinitions, sequenceDefinitions);
|
||||
return Pair.New(rules, flagged);
|
||||
return (rules, flagged);
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -416,8 +412,7 @@ namespace OpenRA
|
||||
if (innerData.Preview != null)
|
||||
cache.CacheMinimap(this);
|
||||
|
||||
if (parseMetadata != null)
|
||||
parseMetadata(this);
|
||||
parseMetadata?.Invoke(this);
|
||||
}
|
||||
|
||||
// Update the status and class unconditionally
|
||||
@@ -526,9 +521,7 @@ namespace OpenRA
|
||||
public void Delete()
|
||||
{
|
||||
Invalidate();
|
||||
var deleteFromPackage = parentPackage as IReadWritePackage;
|
||||
if (deleteFromPackage != null)
|
||||
deleteFromPackage.Delete(Package.Name);
|
||||
(parentPackage as IReadWritePackage)?.Delete(Package.Name);
|
||||
}
|
||||
|
||||
Stream IReadOnlyFileSystem.Open(string filename)
|
||||
|
||||
49
OpenRA.Game/Map/ProjectedCellLayer.cs
Normal file
49
OpenRA.Game/Map/ProjectedCellLayer.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public sealed class ProjectedCellLayer<T> : CellLayerBase<T>
|
||||
{
|
||||
public ProjectedCellLayer(Map map)
|
||||
: base(map) { }
|
||||
|
||||
public ProjectedCellLayer(MapGridType gridType, Size size)
|
||||
: base(gridType, size) { }
|
||||
|
||||
// Resolve an array index from map coordinates.
|
||||
int Index(PPos uv)
|
||||
{
|
||||
return uv.V * Size.Width + uv.U;
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets the layer contents using projected map coordinates.</summary>
|
||||
public T this[PPos uv]
|
||||
{
|
||||
get
|
||||
{
|
||||
return entries[Index(uv)];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
entries[Index(uv)] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(PPos uv)
|
||||
{
|
||||
return bounds.Contains(uv.U, uv.V);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ namespace OpenRA
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public sealed class ProjectedCellRegionEnumerator : IEnumerator<PPos>
|
||||
public struct ProjectedCellRegionEnumerator : IEnumerator<PPos>
|
||||
{
|
||||
readonly ProjectedCellRegion r;
|
||||
|
||||
@@ -86,9 +86,11 @@ namespace OpenRA
|
||||
PPos current;
|
||||
|
||||
public ProjectedCellRegionEnumerator(ProjectedCellRegion region)
|
||||
: this()
|
||||
{
|
||||
r = region;
|
||||
Reset();
|
||||
current = new PPos(u, v);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
|
||||
@@ -24,9 +24,8 @@ namespace OpenRA
|
||||
public readonly byte TerrainType = byte.MaxValue;
|
||||
public readonly byte Height;
|
||||
public readonly byte RampType;
|
||||
public readonly Color LeftColor;
|
||||
public readonly Color RightColor;
|
||||
|
||||
public readonly Color MinColor;
|
||||
public readonly Color MaxColor;
|
||||
public readonly float ZOffset = 0.0f;
|
||||
public readonly float ZRamp = 1.0f;
|
||||
}
|
||||
@@ -73,8 +72,7 @@ namespace OpenRA
|
||||
tileInfo = new TerrainTileInfo[Size.X * Size.Y];
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
int key;
|
||||
if (!int.TryParse(node.Key, out key) || key < 0 || key >= tileInfo.Length)
|
||||
if (!int.TryParse(node.Key, out var key) || key < 0 || key >= tileInfo.Length)
|
||||
throw new InvalidDataException("Invalid tile key '{0}' on template '{1}' of tileset '{2}'.".F(node.Key, Id, tileSet.Id));
|
||||
|
||||
tileInfo[key] = LoadTileInfo(tileSet, node.Value);
|
||||
@@ -87,8 +85,7 @@ namespace OpenRA
|
||||
var i = 0;
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
int key;
|
||||
if (!int.TryParse(node.Key, out key) || key != i++)
|
||||
if (!int.TryParse(node.Key, out var key) || key != i++)
|
||||
throw new InvalidDataException("Invalid tile key '{0}' on template '{1}' of tileset '{2}'.".F(node.Key, Id, tileSet.Id));
|
||||
|
||||
tileInfo[key] = LoadTileInfo(tileSet, node.Value);
|
||||
@@ -106,11 +103,11 @@ namespace OpenRA
|
||||
|
||||
// Fall back to the terrain-type color if necessary
|
||||
var overrideColor = tileSet.TerrainInfo[tile.TerrainType].Color;
|
||||
if (tile.LeftColor == default(Color))
|
||||
tile.GetType().GetField("LeftColor").SetValue(tile, overrideColor);
|
||||
if (tile.MinColor == default(Color))
|
||||
tile.GetType().GetField("MinColor").SetValue(tile, overrideColor);
|
||||
|
||||
if (tile.RightColor == default(Color))
|
||||
tile.GetType().GetField("RightColor").SetValue(tile, overrideColor);
|
||||
if (tile.MaxColor == default(Color))
|
||||
tile.GetType().GetField("MaxColor").SetValue(tile, overrideColor);
|
||||
|
||||
return tile;
|
||||
}
|
||||
@@ -139,6 +136,8 @@ namespace OpenRA
|
||||
public readonly string[] EditorTemplateOrder;
|
||||
public readonly bool IgnoreTileSpriteOffsets;
|
||||
public readonly bool EnableDepth = false;
|
||||
public readonly float MinHeightColorBrightness = 1.0f;
|
||||
public readonly float MaxHeightColorBrightness = 1.0f;
|
||||
|
||||
[FieldLoader.Ignore]
|
||||
public readonly IReadOnlyDictionary<ushort, TerrainTemplateInfo> Templates;
|
||||
@@ -215,8 +214,7 @@ namespace OpenRA
|
||||
|
||||
public byte GetTerrainIndex(string type)
|
||||
{
|
||||
byte index;
|
||||
if (terrainIndexByType.TryGetValue(type, out index))
|
||||
if (terrainIndexByType.TryGetValue(type, out var index))
|
||||
return index;
|
||||
|
||||
throw new InvalidDataException("Tileset '{0}' lacks terrain type '{1}'".F(Id, type));
|
||||
@@ -224,8 +222,7 @@ namespace OpenRA
|
||||
|
||||
public byte GetTerrainIndex(TerrainTile r)
|
||||
{
|
||||
TerrainTemplateInfo tpl;
|
||||
if (!Templates.TryGetValue(r.Type, out tpl))
|
||||
if (!Templates.TryGetValue(r.Type, out var tpl))
|
||||
return defaultWalkableTerrainIndex;
|
||||
|
||||
if (tpl.Contains(r.Index))
|
||||
@@ -240,8 +237,7 @@ namespace OpenRA
|
||||
|
||||
public TerrainTileInfo GetTileInfo(TerrainTile r)
|
||||
{
|
||||
TerrainTemplateInfo tpl;
|
||||
if (!Templates.TryGetValue(r.Type, out tpl))
|
||||
if (!Templates.TryGetValue(r.Type, out var tpl))
|
||||
return null;
|
||||
|
||||
return tpl.Contains(r.Index) ? tpl[r.Index] : null;
|
||||
|
||||
@@ -339,8 +339,7 @@ namespace OpenRA
|
||||
{
|
||||
if (n.Key == "Inherits" || n.Key.StartsWith("Inherits@", StringComparison.Ordinal))
|
||||
{
|
||||
MiniYaml parent;
|
||||
if (!tree.TryGetValue(n.Value.Value, out parent))
|
||||
if (!tree.TryGetValue(n.Value.Value, out var parent))
|
||||
throw new YamlException(
|
||||
"{0}: Parent type `{1}` not found".F(n.Location, n.Value.Value));
|
||||
|
||||
@@ -428,9 +427,8 @@ namespace OpenRA
|
||||
|
||||
foreach (var key in allKeys)
|
||||
{
|
||||
MiniYamlNode existingNode, overrideNode;
|
||||
existingDict.TryGetValue(key, out existingNode);
|
||||
overrideDict.TryGetValue(key, out overrideNode);
|
||||
existingDict.TryGetValue(key, out var existingNode);
|
||||
overrideDict.TryGetValue(key, out var overrideNode);
|
||||
|
||||
var loc = overrideNode == null ? default(MiniYamlNode.SourceLocation) : overrideNode.Location;
|
||||
var comment = (overrideNode ?? existingNode).Comment;
|
||||
|
||||
@@ -177,8 +177,7 @@ namespace OpenRA
|
||||
|
||||
public Map PrepareMap(string uid)
|
||||
{
|
||||
if (LoadScreen != null)
|
||||
LoadScreen.Display();
|
||||
LoadScreen?.Display();
|
||||
|
||||
if (MapCache[uid].Status != MapStatus.Available)
|
||||
throw new InvalidDataException("Invalid map uid: {0}".F(uid));
|
||||
@@ -202,12 +201,12 @@ namespace OpenRA
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (LoadScreen != null)
|
||||
LoadScreen.Dispose();
|
||||
LoadScreen?.Dispose();
|
||||
MapCache.Dispose();
|
||||
|
||||
if (ObjectCreator != null)
|
||||
ObjectCreator.Dispose();
|
||||
ObjectCreator?.Dispose();
|
||||
|
||||
Manifest.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -174,23 +174,21 @@ namespace OpenRA.Network
|
||||
foreach (var p in packets)
|
||||
{
|
||||
packetFn(p.FromClient, p.Data);
|
||||
if (Recorder != null)
|
||||
Recorder.Receive(p.FromClient, p.Data);
|
||||
Recorder?.Receive(p.FromClient, p.Data);
|
||||
}
|
||||
}
|
||||
|
||||
public void StartRecording(Func<string> chooseFilename)
|
||||
{
|
||||
// If we have a previous recording then save/dispose it and start a new one.
|
||||
if (Recorder != null)
|
||||
Recorder.Dispose();
|
||||
Recorder?.Dispose();
|
||||
Recorder = new ReplayRecorder(chooseFilename);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && Recorder != null)
|
||||
Recorder.Dispose();
|
||||
if (disposing)
|
||||
Recorder?.Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -372,8 +370,7 @@ namespace OpenRA.Network
|
||||
|
||||
// Closing the stream will cause any reads on the receiving thread to throw.
|
||||
// This will mark the connection as no longer connected and the thread will terminate cleanly.
|
||||
if (tcp != null)
|
||||
tcp.Close();
|
||||
tcp?.Close();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
@@ -167,28 +167,22 @@ namespace OpenRA.Network
|
||||
|
||||
// Games advertised using the old API calculated the play time locally
|
||||
if (State == 2 && PlayTime < 0)
|
||||
{
|
||||
DateTime startTime;
|
||||
if (DateTime.TryParse(Started, out startTime))
|
||||
if (DateTime.TryParse(Started, out var startTime))
|
||||
PlayTime = (int)(DateTime.UtcNow - startTime).TotalSeconds;
|
||||
}
|
||||
|
||||
ExternalMod external;
|
||||
var externalKey = ExternalMod.MakeKey(Mod, Version);
|
||||
if (Game.ExternalMods.TryGetValue(externalKey, out external) && external.Version == Version)
|
||||
if (Game.ExternalMods.TryGetValue(externalKey, out var external) && external.Version == Version)
|
||||
IsCompatible = true;
|
||||
|
||||
// Games advertised using the old API used local mod metadata
|
||||
if (string.IsNullOrEmpty(ModTitle))
|
||||
{
|
||||
Manifest mod;
|
||||
|
||||
if (external != null && external.Version == Version)
|
||||
{
|
||||
// Use external mod registration to populate the section header
|
||||
ModTitle = external.Title;
|
||||
}
|
||||
else if (Game.Mods.TryGetValue(Mod, out mod))
|
||||
else if (Game.Mods.TryGetValue(Mod, out var mod))
|
||||
{
|
||||
// Use internal mod data to populate the section header, but
|
||||
// on-connect switching must use the external mod plumbing.
|
||||
|
||||
@@ -107,8 +107,7 @@ namespace OpenRA
|
||||
{
|
||||
case TargetType.Actor:
|
||||
{
|
||||
Actor targetActor;
|
||||
if (world != null && TryGetActorFromUInt(world, r.ReadUInt32(), out targetActor))
|
||||
if (world != null && TryGetActorFromUInt(world, r.ReadUInt32(), out var targetActor))
|
||||
target = Target.FromActor(targetActor);
|
||||
break;
|
||||
}
|
||||
@@ -118,8 +117,7 @@ namespace OpenRA
|
||||
var playerActorID = r.ReadUInt32();
|
||||
var frozenActorID = r.ReadUInt32();
|
||||
|
||||
Actor playerActor;
|
||||
if (world == null || !TryGetActorFromUInt(world, playerActorID, out playerActor))
|
||||
if (world == null || !TryGetActorFromUInt(world, playerActorID, out var playerActor))
|
||||
break;
|
||||
|
||||
if (playerActor.Owner.FrozenActorLayer == null)
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace OpenRA.Network
|
||||
Connection.SendImmediate(localImmediateOrders.Select(o => o.Serialize()));
|
||||
localImmediateOrders.Clear();
|
||||
|
||||
var immediatePackets = new List<Pair<int, byte[]>>();
|
||||
var immediatePackets = new List<(int ClientId, byte[] Packet)>();
|
||||
|
||||
Connection.Receive(
|
||||
(clientId, packet) =>
|
||||
@@ -126,16 +126,16 @@ namespace OpenRA.Network
|
||||
else if (packet.Length >= 5 && packet[4] == (byte)OrderType.SyncHash)
|
||||
CheckSync(packet);
|
||||
else if (frame == 0)
|
||||
immediatePackets.Add(Pair.New(clientId, packet));
|
||||
immediatePackets.Add((clientId, packet));
|
||||
else
|
||||
frameData.AddFrameOrders(clientId, frame, packet);
|
||||
});
|
||||
|
||||
foreach (var p in immediatePackets)
|
||||
{
|
||||
foreach (var o in p.Second.ToOrderList(World))
|
||||
foreach (var o in p.Packet.ToOrderList(World))
|
||||
{
|
||||
UnitOrders.ProcessOrder(this, World, p.First, o);
|
||||
UnitOrders.ProcessOrder(this, World, p.ClientId, o);
|
||||
|
||||
// A mod switch or other event has pulled the ground from beneath us
|
||||
if (disposed)
|
||||
@@ -149,8 +149,7 @@ namespace OpenRA.Network
|
||||
void CheckSync(byte[] packet)
|
||||
{
|
||||
var frame = BitConverter.ToInt32(packet, 0);
|
||||
byte[] existingSync;
|
||||
if (syncForFrame.TryGetValue(frame, out existingSync))
|
||||
if (syncForFrame.TryGetValue(frame, out var existingSync))
|
||||
{
|
||||
if (packet.Length != existingSync.Length)
|
||||
OutOfSync(frame);
|
||||
@@ -165,14 +164,14 @@ namespace OpenRA.Network
|
||||
|
||||
public bool IsReadyForNextFrame
|
||||
{
|
||||
get { return NetFrameNumber >= 1 && frameData.IsReadyForFrame(NetFrameNumber); }
|
||||
get { return GameStarted && frameData.IsReadyForFrame(NetFrameNumber); }
|
||||
}
|
||||
|
||||
public IEnumerable<Session.Client> GetClientsNotReadyForNextFrame
|
||||
{
|
||||
get
|
||||
{
|
||||
return NetFrameNumber >= 1
|
||||
return GameStarted
|
||||
? frameData.ClientsNotReadyForFrame(NetFrameNumber)
|
||||
.Select(a => LobbyInfo.ClientWithIndex(a))
|
||||
: NoClients;
|
||||
@@ -207,8 +206,7 @@ namespace OpenRA.Network
|
||||
public void Dispose()
|
||||
{
|
||||
disposed = true;
|
||||
if (Connection != null)
|
||||
Connection.Dispose();
|
||||
Connection?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
@@ -23,7 +22,7 @@ namespace OpenRA.Network
|
||||
class Chunk
|
||||
{
|
||||
public int Frame;
|
||||
public Pair<int, byte[]>[] Packets;
|
||||
public (int ClientId, byte[] Packet)[] Packets;
|
||||
}
|
||||
|
||||
Queue<Chunk> chunks = new Queue<Chunk>();
|
||||
@@ -55,7 +54,7 @@ namespace OpenRA.Network
|
||||
// to avoid issues with all immediate orders being resolved on the first tick.
|
||||
using (var rs = File.OpenRead(replayFilename))
|
||||
{
|
||||
var packets = new List<Pair<int, byte[]>>();
|
||||
var packets = new List<(int ClientId, byte[] Packet)>();
|
||||
|
||||
var chunk = new Chunk();
|
||||
|
||||
@@ -67,7 +66,7 @@ namespace OpenRA.Network
|
||||
var packetLen = rs.ReadInt32();
|
||||
var packet = rs.ReadBytes(packetLen);
|
||||
var frame = BitConverter.ToInt32(packet, 0);
|
||||
packets.Add(Pair.New(client, packet));
|
||||
packets.Add((client, packet));
|
||||
|
||||
if (frame != int.MaxValue &&
|
||||
(!lastClientsFrame.ContainsKey(client) || frame > lastClientsFrame[client]))
|
||||
@@ -111,13 +110,13 @@ namespace OpenRA.Network
|
||||
{
|
||||
foreach (var tmpPacketPair in tmpChunk.Packets)
|
||||
{
|
||||
var client = tmpPacketPair.First;
|
||||
var client = tmpPacketPair.ClientId;
|
||||
|
||||
// Don't replace the final disconnection packet - we still want this to end the replay.
|
||||
if (client == lastClientToDisconnect)
|
||||
continue;
|
||||
|
||||
var packet = tmpPacketPair.Second;
|
||||
var packet = tmpPacketPair.Packet;
|
||||
if (packet.Length == 5 && packet[4] == (byte)OrderType.Disconnect)
|
||||
{
|
||||
var lastClientFrame = lastClientsFrame[client];
|
||||
@@ -156,7 +155,7 @@ namespace OpenRA.Network
|
||||
|
||||
while (chunks.Count != 0 && chunks.Peek().Frame <= ordersFrame)
|
||||
foreach (var o in chunks.Dequeue().Packets)
|
||||
packetFn(o.First, o.Second);
|
||||
packetFn(o.ClientId, o.Packet);
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
@@ -100,8 +100,7 @@ namespace OpenRA.Network
|
||||
Metadata.Write(writer);
|
||||
}
|
||||
|
||||
if (preStartBuffer != null)
|
||||
preStartBuffer.Dispose();
|
||||
preStartBuffer?.Dispose();
|
||||
writer.Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,8 +250,7 @@ namespace OpenRA.Network
|
||||
|
||||
public bool OptionOrDefault(string id, bool def)
|
||||
{
|
||||
LobbyOptionState option;
|
||||
if (LobbyOptions.TryGetValue(id, out option))
|
||||
if (LobbyOptions.TryGetValue(id, out var option))
|
||||
return option.IsEnabled;
|
||||
|
||||
return def;
|
||||
@@ -259,8 +258,7 @@ namespace OpenRA.Network
|
||||
|
||||
public string OptionOrDefault(string id, string def)
|
||||
{
|
||||
LobbyOptionState option;
|
||||
if (LobbyOptions.TryGetValue(id, out option))
|
||||
if (LobbyOptions.TryGetValue(id, out var option))
|
||||
return option.Value;
|
||||
|
||||
return def;
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace OpenRA.Network
|
||||
readonly Report[] syncReports = new Report[NumSyncReports];
|
||||
int curIndex = 0;
|
||||
|
||||
static Pair<string[], Values> DumpSyncTrait(ISync sync)
|
||||
static (string[] Names, Values Values) DumpSyncTrait(ISync sync)
|
||||
{
|
||||
var type = sync.GetType();
|
||||
TypeInfo typeInfo;
|
||||
@@ -41,7 +41,7 @@ namespace OpenRA.Network
|
||||
foreach (var func in typeInfo.SerializableCopyOfMemberFunctions)
|
||||
values[index++] = func(sync);
|
||||
|
||||
return Pair.New(typeInfo.Names, values);
|
||||
return (typeInfo.Names, values);
|
||||
}
|
||||
|
||||
public SyncReport(OrderManager orderManager)
|
||||
@@ -120,9 +120,9 @@ namespace OpenRA.Network
|
||||
Log.Write("sync", "\t {0} {1} {2} {3} ({4})".F(a.ActorID, a.Type, a.Owner, a.Trait, a.Hash));
|
||||
|
||||
var nvp = a.NamesValues;
|
||||
for (int i = 0; i < nvp.First.Length; i++)
|
||||
if (nvp.Second[i] != null)
|
||||
Log.Write("sync", "\t\t {0}: {1}".F(nvp.First[i], nvp.Second[i]));
|
||||
for (int i = 0; i < nvp.Names.Length; i++)
|
||||
if (nvp.Values[i] != null)
|
||||
Log.Write("sync", "\t\t {0}: {1}".F(nvp.Names[i], nvp.Values[i]));
|
||||
}
|
||||
|
||||
Log.Write("sync", "Synced Effects:");
|
||||
@@ -131,9 +131,9 @@ namespace OpenRA.Network
|
||||
Log.Write("sync", "\t {0} ({1})", e.Name, e.Hash);
|
||||
|
||||
var nvp = e.NamesValues;
|
||||
for (int i = 0; i < nvp.First.Length; i++)
|
||||
if (nvp.Second[i] != null)
|
||||
Log.Write("sync", "\t\t {0}: {1}".F(nvp.First[i], nvp.Second[i]));
|
||||
for (int i = 0; i < nvp.Names.Length; i++)
|
||||
if (nvp.Values[i] != null)
|
||||
Log.Write("sync", "\t\t {0}: {1}".F(nvp.Names[i], nvp.Values[i]));
|
||||
}
|
||||
|
||||
Log.Write("sync", "Orders Issued:");
|
||||
@@ -163,14 +163,14 @@ namespace OpenRA.Network
|
||||
public string Owner;
|
||||
public string Trait;
|
||||
public int Hash;
|
||||
public Pair<string[], Values> NamesValues;
|
||||
public (string[] Names, Values Values) NamesValues;
|
||||
}
|
||||
|
||||
struct EffectReport
|
||||
{
|
||||
public string Name;
|
||||
public int Hash;
|
||||
public Pair<string[], Values> NamesValues;
|
||||
public (string[] Names, Values Values) NamesValues;
|
||||
}
|
||||
|
||||
struct TypeInfo
|
||||
|
||||
@@ -9,10 +9,8 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Server;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -147,8 +145,7 @@ namespace OpenRA.Network
|
||||
var data = MiniYaml.FromString(order.TargetString)[0];
|
||||
var traitIndex = int.Parse(data.Key);
|
||||
|
||||
if (world != null)
|
||||
world.AddGameSaveTraitData(traitIndex, data.Value);
|
||||
world?.AddGameSaveTraitData(traitIndex, data.Value);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -192,9 +189,8 @@ namespace OpenRA.Network
|
||||
var request = HandshakeRequest.Deserialize(order.TargetString);
|
||||
|
||||
var externalKey = ExternalMod.MakeKey(request.Mod, request.Version);
|
||||
ExternalMod external;
|
||||
if ((request.Mod != mod.Id || request.Version != mod.Metadata.Version)
|
||||
&& Game.ExternalMods.TryGetValue(externalKey, out external))
|
||||
if ((request.Mod != mod.Id || request.Version != mod.Metadata.Version) &&
|
||||
Game.ExternalMods.TryGetValue(externalKey, out var external))
|
||||
{
|
||||
// The ConnectionFailedLogic will prompt the user to switch mods
|
||||
orderManager.ServerExternalMod = external;
|
||||
@@ -306,7 +302,10 @@ namespace OpenRA.Network
|
||||
{
|
||||
var strings = node.Key.Split('@');
|
||||
if (strings[0] == "GlobalSettings")
|
||||
{
|
||||
orderManager.LobbyInfo.GlobalSettings = Session.Global.Deserialize(node.Value);
|
||||
orderManager.IssueOrder(Order.Command("state {0}".F(Session.ClientState.NotReady)));
|
||||
}
|
||||
}
|
||||
|
||||
SetOrderLag(orderManager);
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace OpenRA
|
||||
|
||||
readonly Cache<string, Type> typeCache;
|
||||
readonly Cache<Type, ConstructorInfo> ctorCache;
|
||||
readonly Pair<Assembly, string>[] assemblies;
|
||||
readonly (Assembly Assembly, string Namespace)[] assemblies;
|
||||
|
||||
public ObjectCreator(Manifest manifest, InstalledMods mods)
|
||||
{
|
||||
@@ -48,8 +48,7 @@ namespace OpenRA
|
||||
// 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));
|
||||
|
||||
Assembly assembly;
|
||||
if (!ResolvedAssemblies.TryGetValue(hash, out assembly))
|
||||
if (!ResolvedAssemblies.TryGetValue(hash, out var assembly))
|
||||
{
|
||||
assembly = Assembly.LoadFile(resolvedPath);
|
||||
ResolvedAssemblies.Add(hash, assembly);
|
||||
@@ -59,7 +58,7 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
|
||||
assemblies = assemblyList.SelectMany(asm => asm.GetNamespaces().Select(ns => Pair.New(asm, ns))).ToArray();
|
||||
assemblies = assemblyList.SelectMany(asm => asm.GetNamespaces().Select(ns => (asm, ns))).ToArray();
|
||||
}
|
||||
|
||||
Assembly ResolveAssembly(object sender, ResolveEventArgs e)
|
||||
@@ -68,10 +67,7 @@ namespace OpenRA
|
||||
if (a.FullName == e.Name)
|
||||
return a;
|
||||
|
||||
if (assemblies == null)
|
||||
return null;
|
||||
|
||||
return assemblies.Select(a => a.First).FirstOrDefault(a => a.FullName == e.Name);
|
||||
return assemblies?.Select(a => a.Assembly).FirstOrDefault(a => a.FullName == e.Name);
|
||||
}
|
||||
|
||||
// Only used by the linter to prevent exceptions from being thrown during a lint run
|
||||
@@ -106,7 +102,7 @@ namespace OpenRA
|
||||
public Type FindType(string className)
|
||||
{
|
||||
return assemblies
|
||||
.Select(pair => pair.First.GetType(pair.Second + "." + className, false))
|
||||
.Select(pair => pair.Assembly.GetType(pair.Namespace + "." + className, false))
|
||||
.FirstOrDefault(t => t != null);
|
||||
}
|
||||
|
||||
@@ -146,7 +142,7 @@ namespace OpenRA
|
||||
|
||||
public IEnumerable<Type> GetTypes()
|
||||
{
|
||||
return assemblies.Select(ma => ma.First).Distinct()
|
||||
return assemblies.Select(ma => ma.Assembly).Distinct()
|
||||
.SelectMany(ma => ma.GetTypes());
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Optimize>true</Optimize>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<LangVersion>5</LangVersion>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
|
||||
@@ -55,7 +55,7 @@
|
||||
<PackageReference Include="OpenRA-Eluant" Version="1.0.17" />
|
||||
<PackageReference Include="OpenRA-Open.NAT" Version="1.0.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
|
||||
<AdditionalFiles Include="../stylecop.json" />
|
||||
<AdditionalFiles Include="Properties/launchSettings.json" />
|
||||
|
||||
@@ -18,13 +18,14 @@ namespace OpenRA.Orders
|
||||
public class GenericSelectTarget : UnitOrderGenerator
|
||||
{
|
||||
public readonly string OrderName;
|
||||
protected readonly IEnumerable<Actor> Subjects;
|
||||
protected readonly string Cursor;
|
||||
protected readonly MouseButton ExpectedButton;
|
||||
|
||||
protected IEnumerable<Actor> subjects;
|
||||
|
||||
public GenericSelectTarget(IEnumerable<Actor> subjects, string order, string cursor, MouseButton button)
|
||||
{
|
||||
Subjects = subjects;
|
||||
this.subjects = subjects;
|
||||
OrderName = order;
|
||||
Cursor = cursor;
|
||||
ExpectedButton = button;
|
||||
@@ -53,7 +54,7 @@ namespace OpenRA.Orders
|
||||
world.CancelInputMode();
|
||||
|
||||
var queued = mi.Modifiers.HasModifier(Modifiers.Shift);
|
||||
yield return new Order(OrderName, null, Target.FromCell(world, cell), queued, null, Subjects.ToArray());
|
||||
yield return new Order(OrderName, null, Target.FromCell(world, cell), queued, null, subjects.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +69,11 @@ namespace OpenRA.Orders
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void SelectionChanged(World world, IEnumerable<Actor> selected)
|
||||
{
|
||||
subjects = selected;
|
||||
}
|
||||
|
||||
public override bool ClearSelectionOnLeftClick { get { return false; } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,5 +24,6 @@ namespace OpenRA
|
||||
string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi);
|
||||
void Deactivate();
|
||||
bool HandleKeyPress(KeyInput e);
|
||||
void SelectionChanged(World world, IEnumerable<Actor> selected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +124,8 @@ namespace OpenRA.Orders
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual void SelectionChanged(World world, IEnumerable<Actor> selected) { }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the most appropriate order for a given actor and target.
|
||||
/// First priority is given to orders that interact with the given actors.
|
||||
|
||||
@@ -14,7 +14,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Scripting;
|
||||
@@ -37,6 +36,9 @@ namespace OpenRA
|
||||
|
||||
public class Player : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding
|
||||
{
|
||||
public const string PlayerActorType = "Player";
|
||||
public const string EditorPlayerActorType = "EditorPlayer";
|
||||
|
||||
struct StanceColors
|
||||
{
|
||||
public Color Self;
|
||||
@@ -164,13 +166,22 @@ namespace OpenRA
|
||||
if (!Spectating)
|
||||
PlayerMask = new LongBitSet<PlayerBitMask>(InternalName);
|
||||
|
||||
var playerActorType = world.Type == WorldType.Editor ? "EditorPlayer" : "Player";
|
||||
PlayerActor = world.CreateActor(playerActorType, new TypeDictionary { new OwnerInit(this) });
|
||||
// Set this property before running any Created callbacks on the player actor
|
||||
IsBot = BotType != null;
|
||||
|
||||
// Special case handling is required for the Player actor:
|
||||
// Since Actor.Created would be called before PlayerActor is assigned here
|
||||
// querying player traits in INotifyCreated.Created would crash.
|
||||
// Therefore assign the uninitialized actor and run the Created callbacks
|
||||
// by calling Initialize ourselves.
|
||||
var playerActorType = world.Type == WorldType.Editor ? EditorPlayerActorType : PlayerActorType;
|
||||
PlayerActor = new Actor(world, playerActorType, new TypeDictionary { new OwnerInit(this) });
|
||||
PlayerActor.Initialize(true);
|
||||
|
||||
Shroud = PlayerActor.Trait<Shroud>();
|
||||
FrozenActorLayer = PlayerActor.TraitOrDefault<FrozenActorLayer>();
|
||||
|
||||
// Enable the bot logic on the host
|
||||
IsBot = BotType != null;
|
||||
if (IsBot && Game.IsHost)
|
||||
{
|
||||
var logic = PlayerActor.TraitsImplementing<IBot>().FirstOrDefault(b => b.Info.Type == BotType);
|
||||
@@ -242,8 +253,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
Player a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out Player a) || !right.TryGetClrValue(out Player b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -33,7 +32,7 @@ namespace OpenRA
|
||||
SheetBuilder sheetBuilder;
|
||||
|
||||
[FieldLoader.Ignore]
|
||||
Cache<Pair<PlayerBadge, int>, Sprite> iconCache;
|
||||
Cache<(PlayerBadge, int), Sprite> iconCache;
|
||||
|
||||
Sprite LoadSprite(string url, int density)
|
||||
{
|
||||
@@ -84,15 +83,15 @@ namespace OpenRA
|
||||
{
|
||||
sheetBuilder = new SheetBuilder(SheetType.BGRA, CreateSheet);
|
||||
|
||||
iconCache = new Cache<Pair<PlayerBadge, int>, Sprite>(p =>
|
||||
iconCache = new Cache<(PlayerBadge Badge, int Density), Sprite>(p =>
|
||||
{
|
||||
if (p.Second > 2 && !string.IsNullOrEmpty(p.First.Icon3x))
|
||||
return LoadSprite(p.First.Icon3x, 3);
|
||||
if (p.Density > 2 && !string.IsNullOrEmpty(p.Badge.Icon3x))
|
||||
return LoadSprite(p.Badge.Icon3x, 3);
|
||||
|
||||
if (p.Second > 1 && !string.IsNullOrEmpty(p.First.Icon2x))
|
||||
return LoadSprite(p.First.Icon2x, 2);
|
||||
if (p.Density > 1 && !string.IsNullOrEmpty(p.Badge.Icon2x))
|
||||
return LoadSprite(p.Badge.Icon2x, 2);
|
||||
|
||||
return LoadSprite(p.First.Icon, 1);
|
||||
return LoadSprite(p.Badge.Icon, 1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -114,7 +113,7 @@ namespace OpenRA
|
||||
{
|
||||
var ws = Game.Renderer.WindowScale;
|
||||
var density = ws > 2 ? 3 : ws > 1 ? 2 : 1;
|
||||
return iconCache[Pair.New(badge, density)];
|
||||
return iconCache[(badge, density)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,14 +47,9 @@ namespace OpenRA.Primitives
|
||||
BitSetIndex bits = 0;
|
||||
|
||||
lock (Bits)
|
||||
{
|
||||
foreach (var value in values)
|
||||
{
|
||||
BitSetIndex valueBit;
|
||||
if (Bits.TryGetValue(value, out valueBit))
|
||||
if (Bits.TryGetValue(value, out var valueBit))
|
||||
bits |= valueBit;
|
||||
}
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
@@ -124,10 +124,10 @@ namespace OpenRA.Primitives
|
||||
if (value.Length != 6 && value.Length != 8)
|
||||
return false;
|
||||
|
||||
byte red, green, blue, alpha = 255;
|
||||
if (!byte.TryParse(value.Substring(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out red)
|
||||
|| !byte.TryParse(value.Substring(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out green)
|
||||
|| !byte.TryParse(value.Substring(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out blue))
|
||||
byte alpha = 255;
|
||||
if (!byte.TryParse(value.Substring(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var red)
|
||||
|| !byte.TryParse(value.Substring(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var green)
|
||||
|| !byte.TryParse(value.Substring(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var blue))
|
||||
return false;
|
||||
|
||||
if (value.Length == 8
|
||||
|
||||
@@ -50,14 +50,9 @@ namespace OpenRA.Primitives
|
||||
long bits = 0;
|
||||
|
||||
lock (Bits)
|
||||
{
|
||||
foreach (var value in values)
|
||||
{
|
||||
long valueBit;
|
||||
if (Bits.TryGetValue(value, out valueBit))
|
||||
if (Bits.TryGetValue(value, out var valueBit))
|
||||
bits |= valueBit;
|
||||
}
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
@@ -1,70 +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;
|
||||
|
||||
namespace OpenRA.Primitives
|
||||
{
|
||||
public struct Pair<T, U> : IEquatable<Pair<T, U>>
|
||||
{
|
||||
public T First;
|
||||
public U Second;
|
||||
|
||||
public Pair(T first, U second)
|
||||
{
|
||||
First = first;
|
||||
Second = second;
|
||||
}
|
||||
|
||||
internal static IEqualityComparer<T> Tcomparer = EqualityComparer<T>.Default;
|
||||
internal static IEqualityComparer<U> Ucomparer = EqualityComparer<U>.Default;
|
||||
|
||||
public static bool operator ==(Pair<T, U> a, Pair<T, U> b)
|
||||
{
|
||||
return Tcomparer.Equals(a.First, b.First) && Ucomparer.Equals(a.Second, b.Second);
|
||||
}
|
||||
|
||||
public static bool operator !=(Pair<T, U> a, Pair<T, U> b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
public override int GetHashCode() { return First.GetHashCode() ^ Second.GetHashCode(); }
|
||||
|
||||
public bool Equals(Pair<T, U> other) { return this == other; }
|
||||
public override bool Equals(object obj) { return obj is Pair<T, U> && Equals((Pair<T, U>)obj); }
|
||||
|
||||
public Pair<T, U> WithFirst(T t) { return new Pair<T, U>(t, Second); }
|
||||
public Pair<T, U> WithSecond(U u) { return new Pair<T, U>(First, u); }
|
||||
|
||||
public static T AsFirst(Pair<T, U> p) { return p.First; }
|
||||
public static U AsSecond(Pair<T, U> p) { return p.Second; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "({0},{1})".F(First, Second);
|
||||
}
|
||||
|
||||
class PairEqualityComparer : IEqualityComparer<Pair<T, U>>
|
||||
{
|
||||
public bool Equals(Pair<T, U> x, Pair<T, U> y) { return x == y; }
|
||||
public int GetHashCode(Pair<T, U> obj) { return obj.GetHashCode(); }
|
||||
}
|
||||
|
||||
public static IEqualityComparer<Pair<T, U>> EqualityComparer { get { return new PairEqualityComparer(); } }
|
||||
}
|
||||
|
||||
public static class Pair
|
||||
{
|
||||
public static Pair<T, U> New<T, U>(T t, U u) { return new Pair<T, U>(t, u); }
|
||||
}
|
||||
}
|
||||
@@ -120,8 +120,7 @@ namespace OpenRA.Primitives
|
||||
/// <param name="count">The length of the segment.</param>
|
||||
public static Stream CreateWithoutOwningStream(Stream stream, long offset, int count)
|
||||
{
|
||||
Stream parentStream;
|
||||
var nestedOffset = offset + GetOverallNestedOffset(stream, out parentStream);
|
||||
var nestedOffset = offset + GetOverallNestedOffset(stream, out var parentStream);
|
||||
|
||||
// Special case FileStream - instead of creating an in-memory copy,
|
||||
// just reference the portion of the on-disk file that we need to save memory.
|
||||
|
||||
@@ -52,8 +52,7 @@ namespace OpenRA.Primitives
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
Rectangle bounds;
|
||||
if (!itemBounds.TryGetValue(item, out bounds))
|
||||
if (!itemBounds.TryGetValue(item, out var bounds))
|
||||
return false;
|
||||
|
||||
MutateBins(item, bounds, removeItem);
|
||||
@@ -91,8 +90,7 @@ namespace OpenRA.Primitives
|
||||
|
||||
void MutateBins(T actor, Rectangle bounds, Action<Dictionary<T, Rectangle>, T, Rectangle> action)
|
||||
{
|
||||
int minRow, maxRow, minCol, maxCol;
|
||||
BoundsToBinRowsAndCols(bounds, out minRow, out maxRow, out minCol, out maxCol);
|
||||
BoundsToBinRowsAndCols(bounds, out var minRow, out var maxRow, out var minCol, out var maxCol);
|
||||
|
||||
for (var row = minRow; row < maxRow; row++)
|
||||
for (var col = minCol; col < maxCol; col++)
|
||||
@@ -110,8 +108,7 @@ namespace OpenRA.Primitives
|
||||
|
||||
public IEnumerable<T> InBox(Rectangle box)
|
||||
{
|
||||
int minRow, maxRow, minCol, maxCol;
|
||||
BoundsToBinRowsAndCols(box, out minRow, out maxRow, out minCol, out maxCol);
|
||||
BoundsToBinRowsAndCols(box, out var minRow, out var maxRow, out var minCol, out var maxCol);
|
||||
|
||||
// We want to return any items intersecting the box.
|
||||
// If the box covers multiple bins, we must handle items that are contained in multiple bins and avoid
|
||||
|
||||
@@ -41,6 +41,11 @@ namespace OpenRA.Primitives
|
||||
return data.ContainsKey(typeof(T));
|
||||
}
|
||||
|
||||
public bool Contains(Type t)
|
||||
{
|
||||
return data.ContainsKey(t);
|
||||
}
|
||||
|
||||
public T Get<T>()
|
||||
{
|
||||
return (T)Get(typeof(T), true);
|
||||
@@ -56,8 +61,7 @@ namespace OpenRA.Primitives
|
||||
|
||||
object Get(Type t, bool throwsIfMissing)
|
||||
{
|
||||
List<object> ret;
|
||||
if (!data.TryGetValue(t, out ret))
|
||||
if (!data.TryGetValue(t, out var ret))
|
||||
{
|
||||
if (throwsIfMissing)
|
||||
throw new InvalidOperationException("TypeDictionary does not contain instance of type `{0}`".F(t));
|
||||
@@ -71,8 +75,7 @@ namespace OpenRA.Primitives
|
||||
|
||||
public IEnumerable<T> WithInterface<T>()
|
||||
{
|
||||
List<object> objs;
|
||||
if (data.TryGetValue(typeof(T), out objs))
|
||||
if (data.TryGetValue(typeof(T), out var objs))
|
||||
return objs.Cast<T>();
|
||||
return new T[0];
|
||||
}
|
||||
@@ -89,8 +92,7 @@ namespace OpenRA.Primitives
|
||||
|
||||
void InnerRemove(Type t, object val)
|
||||
{
|
||||
List<object> objs;
|
||||
if (!data.TryGetValue(t, out objs))
|
||||
if (!data.TryGetValue(t, out var objs))
|
||||
return;
|
||||
objs.Remove(val);
|
||||
if (objs.Count == 0)
|
||||
|
||||
@@ -62,5 +62,6 @@ namespace OpenRA
|
||||
public override string ToString() { return "{0},{1},{2}".F(X, Y, Z); }
|
||||
|
||||
public static readonly float3 Zero = new float3(0, 0, 0);
|
||||
public static readonly float3 Ones = new float3(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,8 +115,7 @@ namespace OpenRA
|
||||
font.Dispose();
|
||||
using (new PerfTimer("SpriteFonts"))
|
||||
{
|
||||
if (fontSheetBuilder != null)
|
||||
fontSheetBuilder.Dispose();
|
||||
fontSheetBuilder?.Dispose();
|
||||
fontSheetBuilder = new SheetBuilder(SheetType.BGRA, 512);
|
||||
Fonts = modData.Manifest.Get<Fonts>().FontList.ToDictionary(x => x.Key,
|
||||
x => new SpriteFont(x.Value.Font, modData.DefaultFileSystem.Open(x.Value.Font).ReadAllBytes(),
|
||||
@@ -156,8 +155,7 @@ namespace OpenRA
|
||||
|
||||
if (screenSprite == null || screenSprite.Sheet.Size != surfaceBufferSize)
|
||||
{
|
||||
if (screenBuffer != null)
|
||||
screenBuffer.Dispose();
|
||||
screenBuffer?.Dispose();
|
||||
|
||||
// Render the screen into a frame buffer to simplify reading back screenshots
|
||||
screenBuffer = Context.CreateFrameBuffer(surfaceBufferSize, Color.FromArgb(0xFF, 0, 0, 0));
|
||||
@@ -195,8 +193,7 @@ namespace OpenRA
|
||||
var worldBufferSize = worldViewport.Size.NextPowerOf2();
|
||||
if (worldSprite == null || worldSprite.Sheet.Size != worldBufferSize)
|
||||
{
|
||||
if (worldBuffer != null)
|
||||
worldBuffer.Dispose();
|
||||
worldBuffer?.Dispose();
|
||||
|
||||
// Render the world into a framebuffer at 1:1 scaling to allow the depth buffer to match the artwork at all zoom levels
|
||||
worldBuffer = Context.CreateFrameBuffer(worldBufferSize);
|
||||
@@ -329,8 +326,7 @@ namespace OpenRA
|
||||
{
|
||||
if (currentBatchRenderer == value)
|
||||
return;
|
||||
if (currentBatchRenderer != null)
|
||||
currentBatchRenderer.Flush();
|
||||
currentBatchRenderer?.Flush();
|
||||
currentBatchRenderer = value;
|
||||
}
|
||||
}
|
||||
@@ -460,8 +456,7 @@ namespace OpenRA
|
||||
{
|
||||
WorldModelRenderer.Dispose();
|
||||
tempBuffer.Dispose();
|
||||
if (fontSheetBuilder != null)
|
||||
fontSheetBuilder.Dispose();
|
||||
fontSheetBuilder?.Dispose();
|
||||
if (Fonts != null)
|
||||
foreach (var font in Fonts.Values)
|
||||
font.Dispose();
|
||||
|
||||
@@ -267,8 +267,7 @@ namespace OpenRA.Scripting
|
||||
return;
|
||||
|
||||
disposed = true;
|
||||
if (runtime != null)
|
||||
runtime.Dispose();
|
||||
runtime?.Dispose();
|
||||
}
|
||||
|
||||
static IEnumerable<Type> ExtractRequiredTypes(Type t)
|
||||
|
||||
@@ -28,8 +28,7 @@ namespace OpenRA.Scripting
|
||||
|
||||
public static string LuaDocString(this Type t)
|
||||
{
|
||||
string ret;
|
||||
if (!LuaTypeNameReplacements.TryGetValue(t.Name, out ret))
|
||||
if (!LuaTypeNameReplacements.TryGetValue(t.Name, out var ret))
|
||||
ret = t.Name;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -111,8 +111,7 @@ namespace OpenRA.Scripting
|
||||
if (IsSetProperty)
|
||||
{
|
||||
var pi = (PropertyInfo)Member;
|
||||
object clrValue;
|
||||
if (!value.TryGetClrValue(pi.PropertyType, out clrValue))
|
||||
if (!value.TryGetClrValue(pi.PropertyType, out var clrValue))
|
||||
throw new LuaException("Unable to convert '{0}' to Clr type '{1}'".F(value.WrappedClrType().Name, pi.PropertyType));
|
||||
|
||||
pi.SetValue(Target, clrValue, null);
|
||||
|
||||
@@ -51,8 +51,7 @@ namespace OpenRA.Scripting
|
||||
get
|
||||
{
|
||||
var name = keyValue.ToString();
|
||||
ScriptMemberWrapper wrapper;
|
||||
if (!members.TryGetValue(name, out wrapper))
|
||||
if (!members.TryGetValue(name, out var wrapper))
|
||||
throw new LuaException(MemberNotFoundError(name));
|
||||
|
||||
return wrapper.Get(runtime);
|
||||
@@ -61,8 +60,7 @@ namespace OpenRA.Scripting
|
||||
set
|
||||
{
|
||||
var name = keyValue.ToString();
|
||||
ScriptMemberWrapper wrapper;
|
||||
if (!members.TryGetValue(name, out wrapper))
|
||||
if (!members.TryGetValue(name, out var wrapper))
|
||||
throw new LuaException(MemberNotFoundError(name));
|
||||
|
||||
wrapper.Set(runtime, value);
|
||||
|
||||
@@ -18,8 +18,7 @@ namespace OpenRA.Scripting
|
||||
{
|
||||
public static Type WrappedClrType(this LuaValue value)
|
||||
{
|
||||
object inner;
|
||||
if (value.TryGetClrObject(out inner))
|
||||
if (value.TryGetClrObject(out var inner))
|
||||
return inner.GetType();
|
||||
|
||||
return value.GetType();
|
||||
@@ -27,16 +26,13 @@ namespace OpenRA.Scripting
|
||||
|
||||
public static bool TryGetClrValue<T>(this LuaValue value, out T clrObject)
|
||||
{
|
||||
object temp;
|
||||
var ret = value.TryGetClrValue(typeof(T), out temp);
|
||||
var ret = value.TryGetClrValue(typeof(T), out object temp);
|
||||
clrObject = ret ? (T)temp : default(T);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static bool TryGetClrValue(this LuaValue value, Type t, out object clrObject)
|
||||
{
|
||||
object temp;
|
||||
|
||||
// Is t a nullable?
|
||||
// If yes, get the underlying type
|
||||
var nullable = Nullable.GetUnderlyingType(t);
|
||||
@@ -44,7 +40,7 @@ namespace OpenRA.Scripting
|
||||
t = nullable;
|
||||
|
||||
// Value wraps a CLR object
|
||||
if (value.TryGetClrObject(out temp))
|
||||
if (value.TryGetClrObject(out var temp))
|
||||
{
|
||||
if (temp.GetType() == t)
|
||||
{
|
||||
@@ -172,8 +168,7 @@ namespace OpenRA.Scripting
|
||||
{
|
||||
// Object needs additional notification / context
|
||||
var notify = obj as IScriptNotifyBind;
|
||||
if (notify != null)
|
||||
notify.OnScriptBind(context);
|
||||
notify?.OnScriptBind(context);
|
||||
|
||||
return new LuaCustomClrObject(obj);
|
||||
}
|
||||
|
||||
@@ -99,8 +99,7 @@ namespace OpenRA.Server
|
||||
// Non-blocking sends are free to send only part of the data
|
||||
while (start < length)
|
||||
{
|
||||
SocketError error;
|
||||
var sent = s.Send(data, start, length - start, SocketFlags.None, out error);
|
||||
var sent = s.Send(data, start, length - start, SocketFlags.None, out var error);
|
||||
if (error == SocketError.WouldBlock)
|
||||
{
|
||||
Log.Write("server", "Non-blocking send of {0} bytes failed. Falling back to blocking send.", length - start);
|
||||
@@ -245,8 +244,7 @@ namespace OpenRA.Server
|
||||
}
|
||||
|
||||
var conn = Conns.SingleOrDefault(c => c.Socket == s);
|
||||
if (conn != null)
|
||||
conn.ReadData(this);
|
||||
conn?.ReadData(this);
|
||||
}
|
||||
|
||||
delayedActions.PerformActions(0);
|
||||
@@ -716,8 +714,7 @@ namespace OpenRA.Server
|
||||
break;
|
||||
case "Pong":
|
||||
{
|
||||
long pingSent;
|
||||
if (!OpenRA.Exts.TryParseInt64Invariant(o.TargetString, out pingSent))
|
||||
if (!OpenRA.Exts.TryParseInt64Invariant(o.TargetString, out var pingSent))
|
||||
{
|
||||
Log.Write("server", "Invalid order pong payload: {0}", o.TargetString);
|
||||
break;
|
||||
@@ -1016,7 +1013,7 @@ namespace OpenRA.Server
|
||||
|
||||
foreach (var c in Conns)
|
||||
foreach (var d in Conns)
|
||||
DispatchOrdersToClient(c, d.PlayerIndex, 0x7FFFFFFF, new[] { (byte)OrderType.Disconnect });
|
||||
DispatchOrdersToClient(c, d.PlayerIndex, int.MaxValue, new[] { (byte)OrderType.Disconnect });
|
||||
|
||||
if (GameSave == null && LobbyInfo.GlobalSettings.GameSavesEnabled)
|
||||
GameSave = new GameSave();
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace OpenRA
|
||||
public bool DiscoverNatDevices = false;
|
||||
|
||||
[Desc("Time in milliseconds to search for UPnP enabled NAT devices.")]
|
||||
public int NatDiscoveryTimeout = 1000;
|
||||
public int NatDiscoveryTimeout = 5000;
|
||||
|
||||
[Desc("Starts the game with a default map. Input as hash that can be obtained by the utility.")]
|
||||
public string Map = null;
|
||||
@@ -295,8 +295,7 @@ namespace OpenRA
|
||||
yamlCache = MiniYaml.FromFile(settingsFile, false);
|
||||
foreach (var yamlSection in yamlCache)
|
||||
{
|
||||
object settingsSection;
|
||||
if (yamlSection.Key != null && Sections.TryGetValue(yamlSection.Key, out settingsSection))
|
||||
if (yamlSection.Key != null && Sections.TryGetValue(yamlSection.Key, out var settingsSection))
|
||||
LoadSectionYaml(yamlSection.Value, settingsSection);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user