From a20f5981e25e5056e73137f9708ba56a632f8b51 Mon Sep 17 00:00:00 2001 From: Gustas Date: Sun, 26 May 2024 16:27:25 +0300 Subject: [PATCH 01/13] Fix TSVeinRenderer not having tooltip translated --- OpenRA.Mods.Cnc/Traits/World/TSVeinsRenderer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenRA.Mods.Cnc/Traits/World/TSVeinsRenderer.cs b/OpenRA.Mods.Cnc/Traits/World/TSVeinsRenderer.cs index a518860c2b90..1c18d69598f2 100644 --- a/OpenRA.Mods.Cnc/Traits/World/TSVeinsRenderer.cs +++ b/OpenRA.Mods.Cnc/Traits/World/TSVeinsRenderer.cs @@ -370,10 +370,10 @@ string IResourceRenderer.GetRenderedResourceType(CPos cell) string IResourceRenderer.GetRenderedResourceTooltip(CPos cell) { - if (renderIndices[cell] != null) - return info.Name; + if (renderIndices[cell] != null || borders[cell] != Adjacency.None) + return TranslationProvider.GetString(info.Name); - return borders[cell] != Adjacency.None ? info.Name : null; + return null; } IEnumerable IResourceRenderer.RenderUIPreview(WorldRenderer wr, string resourceType, int2 origin, float scale) From bc29707e875ecaf700d56f257f085dddb13311bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Mail=C3=A4nder?= Date: Mon, 20 Sep 2021 16:41:38 +0200 Subject: [PATCH 02/13] Mention infrastructure Patreon account. --- .github/FUNDING.yml | 1 + packaging/linux/openra.metainfo.xml.in | 1 + 2 files changed, 2 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000000..ca8fb2bbb10b --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +patreon: orahosting diff --git a/packaging/linux/openra.metainfo.xml.in b/packaging/linux/openra.metainfo.xml.in index 72928a8a4bc8..1d8126f1d73f 100644 --- a/packaging/linux/openra.metainfo.xml.in +++ b/packaging/linux/openra.metainfo.xml.in @@ -45,6 +45,7 @@ https://www.openra.net https://github.com/OpenRA/OpenRA/issues + https://www.patreon.com/orahosting paul_at_chote.net The OpenRA Developers From f337450348d4d2b73a406433f2c9dd30f0956c99 Mon Sep 17 00:00:00 2001 From: Gustas Date: Sun, 26 May 2024 16:11:42 +0300 Subject: [PATCH 03/13] Link no longer exists --- OpenRA.Mods.Cnc/Traits/World/VoxelNormalsPalette.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenRA.Mods.Cnc/Traits/World/VoxelNormalsPalette.cs b/OpenRA.Mods.Cnc/Traits/World/VoxelNormalsPalette.cs index 1311f4151173..8de0bc71cfd7 100644 --- a/OpenRA.Mods.Cnc/Traits/World/VoxelNormalsPalette.cs +++ b/OpenRA.Mods.Cnc/Traits/World/VoxelNormalsPalette.cs @@ -59,7 +59,7 @@ public void LoadPalettes(WorldRenderer wr) wr.AddPalette(info.Name, new ImmutablePalette(data)); } - // Normal vector tables from http://www.sleipnirstuff.com/forum/viewtopic.php?t=8048 + // Normal vector tables from https://web.archive.org/web/20041022134721/https://www.sleipnirstuff.com/forum/viewtopic.php?t=8048 static readonly float[] TSNormals = { 0.671214f, 0.198492f, -0.714194f, From 64b2bd47353e5fb5a0f090d28478c03a1dbdfb66 Mon Sep 17 00:00:00 2001 From: Gustas Date: Sun, 26 May 2024 16:19:06 +0300 Subject: [PATCH 04/13] Update to HTTPS --- .editorconfig | 2 +- AUTHORS | 2 +- CODE_OF_CONDUCT.md | 6 +++--- CONTRIBUTING.md | 2 +- COPYING | 8 ++++---- INSTALL.md | 4 ++-- OpenRA.Game/Primitives/Color.cs | 4 ++-- OpenRA.Mods.Cnc/FileFormats/LZOCompression.cs | 4 ++-- OpenRA.Mods.Common/Pathfinder/PathSearch.cs | 4 ++-- OpenRA.Mods.Common/UtilityCommands/CreateManPage.cs | 2 +- configure-system-libraries.sh | 2 +- fetch-geoip.sh | 2 +- glsl/combined.frag | 4 ++-- launch-game.sh | 2 +- mods/cnc/installer/downloads.yaml | 2 +- mods/d2k/installer/downloads.yaml | 6 +++--- mods/ra/installer/downloads.yaml | 8 ++++---- mods/ts/installer/downloads.yaml | 6 +++--- packaging/functions.sh | 2 +- packaging/linux/openra-mimeinfo.xml.discord.in | 2 +- packaging/linux/openra-mimeinfo.xml.in | 2 +- packaging/linux/openra.appimage.in | 2 +- packaging/linux/openra.in | 2 +- packaging/macos/Info.plist.in | 2 +- packaging/macos/buildpackage.sh | 2 +- packaging/macos/entitlements.plist | 2 +- packaging/windows/OpenRA.nsi | 4 ++-- 27 files changed, 45 insertions(+), 45 deletions(-) diff --git a/.editorconfig b/.editorconfig index 83c30f627c3d..b75919d69aab 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -; Top-most http://editorconfig.org/ file +; Top-most https://editorconfig.org/ file root = true charset=utf-8 diff --git a/AUTHORS b/AUTHORS index c93fd0257b23..c74d276cfaaf 100644 --- a/AUTHORS +++ b/AUTHORS @@ -216,7 +216,7 @@ Using Linguini by the Space Station 14 team licensed under Apache and MIT terms. This site or product includes IP2Location LITE data -available from http://www.ip2location.com. +available from https://www.ip2location.com. Finally, special thanks goes to the original teams at Westwood Studios and EA for creating the classic diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 67894ae9aa7e..3a7d532a0f92 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -70,7 +70,7 @@ members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] +available at [https://contributor-covenant.org/version/1/4][version] -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +[homepage]: https://contributor-covenant.org +[version]: https://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 43c4bf12d4c5..bd9ae96ba56e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,7 +16,7 @@ Help us keep OpenRA open and inclusive. Please read and follow our [Code of Cond * [Coding standard](https://github.com/OpenRA/OpenRA/wiki/Coding-Standard) * [Branches and Releases](https://github.com/OpenRA/OpenRA/wiki/Branches-and-Releases) -* [Licensing](http://www.gnu.org/licenses/quick-guide-gplv3.html) +* [Licensing](https://www.gnu.org/licenses/quick-guide-gplv3.html) Please `git rebase` to the latest revision of the bleed branch. diff --git a/COPYING b/COPYING index 94a9ed024d38..e60008693e01 100644 --- a/COPYING +++ b/COPYING @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/INSTALL.md b/INSTALL.md index 3c2315e056d3..aac1b179d700 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -7,7 +7,7 @@ Windows ======= Compiling OpenRA requires the following dependencies: -* [Windows PowerShell >= 4.0](http://microsoft.com/powershell) (included by default in recent Windows 10 versions) +* [Windows PowerShell >= 4.0](https://microsoft.com/powershell) (included by default in recent Windows 10 versions) * [.NET 6 SDK](https://dotnet.microsoft.com/download/dotnet/6.0) (or via Visual Studio) To compile OpenRA, open the `OpenRA.sln` solution in the main folder, build it from the command-line with `dotnet` or use the Makefile analogue command `make all` scripted in PowerShell syntax. @@ -25,7 +25,7 @@ To compile OpenRA, run `make` from the command line (or `make RUNTIME=mono` if u 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`. -If you choose to use system libraries, or your system is not x86_64, you will need to install [SDL 2](https://www.libsdl.org/download-2.0.php), [FreeType](http://gnuwin32.sourceforge.net/packages/freetype.htm), [OpenAL](https://openal-soft.org/), and [liblua 5.1](http://luabinaries.sourceforge.net/download.html) before compiling OpenRA. +If you choose to use system libraries, or your system is not x86_64, you will need to install [SDL 2](https://www.libsdl.org/download-2.0.php), [FreeType](https://gnuwin32.sourceforge.net/packages/freetype.htm), [OpenAL](https://openal-soft.org/), and [liblua 5.1](https://luabinaries.sourceforge.net/download.html) before compiling OpenRA. These can be installed using your package manager on various distros: diff --git a/OpenRA.Game/Primitives/Color.cs b/OpenRA.Game/Primitives/Color.cs index 3d54074baa19..58c7e7aa37b5 100644 --- a/OpenRA.Game/Primitives/Color.cs +++ b/OpenRA.Game/Primitives/Color.cs @@ -77,13 +77,13 @@ public static Color FromArgb(uint argb) static float SrgbToLinear(float c) { - // Standard gamma conversion equation: see e.g. http://entropymine.com/imageworsener/srgbformula/ + // Standard gamma conversion equation: see e.g. https://entropymine.com/imageworsener/srgbformula/ return c <= 0.04045f ? c / 12.92f : (float)Math.Pow((c + 0.055f) / 1.055f, 2.4f); } static float LinearToSrgb(float c) { - // Standard gamma conversion equation: see e.g. http://entropymine.com/imageworsener/srgbformula/ + // Standard gamma conversion equation: see e.g. https://entropymine.com/imageworsener/srgbformula/ return c <= 0.0031308f ? c * 12.92f : 1.055f * (float)Math.Pow(c, 1.0f / 2.4f) - 0.055f; } diff --git a/OpenRA.Mods.Cnc/FileFormats/LZOCompression.cs b/OpenRA.Mods.Cnc/FileFormats/LZOCompression.cs index a7e0037c027b..aec0c80d49f8 100644 --- a/OpenRA.Mods.Cnc/FileFormats/LZOCompression.cs +++ b/OpenRA.Mods.Cnc/FileFormats/LZOCompression.cs @@ -11,7 +11,7 @@ #region Additional Copyright & License Information /* * C# port of the crude minilzo source version 2.06 by Frank Razenberg - * The full LZO package can be found at http://www.oberhumer.com/opensource/lzo/ + * The full LZO package can be found at https://www.oberhumer.com/opensource/lzo/ * * Beware, you should never want to see C# code like this. You were warned. * I simply ran the MSVC preprocessor on the original source, changed the datatypes @@ -61,7 +61,7 @@ * * Markus F.X.J. Oberhumer * - * http://www.oberhumer.com/opensource/lzo/ + * https://www.oberhumer.com/opensource/lzo/ */ #endregion diff --git a/OpenRA.Mods.Common/Pathfinder/PathSearch.cs b/OpenRA.Mods.Common/Pathfinder/PathSearch.cs index c514a4d1ef35..24cb0258f2fa 100644 --- a/OpenRA.Mods.Common/Pathfinder/PathSearch.cs +++ b/OpenRA.Mods.Common/Pathfinder/PathSearch.cs @@ -123,7 +123,7 @@ public static PathSearch ToTargetCellOverGraph( /// /// Default: Diagonal distance heuristic. More information: - /// http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html + /// https://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html /// Layers are ignored and incur no additional cost. /// /// Locomotor used to provide terrain costs. @@ -137,7 +137,7 @@ public static Func DefaultCostEstimator(Locomotor locomotor, CP /// /// Default: Diagonal distance heuristic. More information: - /// http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html + /// https://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html /// Layers are ignored and incur no additional cost. /// /// Locomotor used to provide terrain costs. diff --git a/OpenRA.Mods.Common/UtilityCommands/CreateManPage.cs b/OpenRA.Mods.Common/UtilityCommands/CreateManPage.cs index 63ef62ec9c42..9866a2edde3e 100644 --- a/OpenRA.Mods.Common/UtilityCommands/CreateManPage.cs +++ b/OpenRA.Mods.Common/UtilityCommands/CreateManPage.cs @@ -65,7 +65,7 @@ void IUtilityCommand.Run(Utility utility, string[] args) Console.WriteLine(".SH FILES"); Console.WriteLine("Settings are stored in the ~/.openra user folder."); Console.WriteLine(".SH BUGS"); - Console.WriteLine("Known issues are tracked at http://bugs.openra.net"); + Console.WriteLine("Known issues are tracked at https://bugs.openra.net"); Console.WriteLine(".SH COPYRIGHT"); Console.WriteLine("Copyright (c) The OpenRA Developers and Contributors"); Console.WriteLine("This manual is part of OpenRA, which is free software. It is GNU GPL v3 licensed. See COPYING for details."); diff --git a/configure-system-libraries.sh b/configure-system-libraries.sh index 0c9e3f35ea5e..1c2ceb1c3f49 100755 --- a/configure-system-libraries.sh +++ b/configure-system-libraries.sh @@ -3,7 +3,7 @@ #### # This file must stay /bin/sh and POSIX compliant for macOS and BSD portability. -# Copy-paste the entire script into http://shellcheck.net to check. +# Copy-paste the entire script into https://shellcheck.net to check. #### set -o errexit || exit $? diff --git a/fetch-geoip.sh b/fetch-geoip.sh index 2e121277678a..54a5c32acc97 100755 --- a/fetch-geoip.sh +++ b/fetch-geoip.sh @@ -3,7 +3,7 @@ #### # This file must stay /bin/sh and POSIX compliant for macOS and BSD portability. -# Copy-paste the entire script into http://shellcheck.net to check. +# Copy-paste the entire script into https://shellcheck.net to check. #### set -o errexit || exit $? diff --git a/glsl/combined.frag b/glsl/combined.frag index 30fb5017f099..cedc5d846cf1 100644 --- a/glsl/combined.frag +++ b/glsl/combined.frag @@ -51,7 +51,7 @@ vec3 hsv2rgb(vec3 c) float srgb2linear(float c) { - // Standard gamma conversion equation: see e.g. http://entropymine.com/imageworsener/srgbformula/ + // Standard gamma conversion equation: see e.g. https://entropymine.com/imageworsener/srgbformula/ return c <= 0.04045f ? c / 12.92f : pow((c + 0.055f) / 1.055f, 2.4f); } @@ -63,7 +63,7 @@ vec4 srgb2linear(vec4 c) float linear2srgb(float c) { - // Standard gamma conversion equation: see e.g. http://entropymine.com/imageworsener/srgbformula/ + // Standard gamma conversion equation: see e.g. https://entropymine.com/imageworsener/srgbformula/ return c <= 0.0031308 ? c * 12.92f : 1.055f * pow(c, 1.0f / 2.4f) - 0.055f; } diff --git a/launch-game.sh b/launch-game.sh index bef79bb19a17..c9ed268a3656 100755 --- a/launch-game.sh +++ b/launch-game.sh @@ -50,7 +50,7 @@ if [ "${rc}" != 0 ] && [ "${rc}" != 1 ]; then if [ -d Support/Logs ]; then LOGS="${PWD}/Support/Logs" fi - ERROR_MESSAGE=$(printf "%s has encountered a fatal error.\nPlease refer to the crash logs and FAQ for more information.\n\nLog files are located in %s\nThe FAQ is available at http://wiki.openra.net/FAQ" "OpenRA" "${LOGS}") + ERROR_MESSAGE=$(printf "%s has encountered a fatal error.\nPlease refer to the crash logs and FAQ for more information.\n\nLog files are located in %s\nThe FAQ is available at https://wiki.openra.net/FAQ" "OpenRA" "${LOGS}") if command -v zenity > /dev/null; then zenity --no-wrap --error --title "OpenRA" --no-markup --text "${ERROR_MESSAGE}" 2> /dev/null || : elif command -v kdialog > /dev/null; then diff --git a/mods/cnc/installer/downloads.yaml b/mods/cnc/installer/downloads.yaml index 4e80766a9cf5..c1f544d6d28c 100644 --- a/mods/cnc/installer/downloads.yaml +++ b/mods/cnc/installer/downloads.yaml @@ -1,7 +1,7 @@ basefiles: Base Freeware Content Type: ZipFile SHA1: 72f337464963fa37d3688eb03e80eefd33669a3d - MirrorList: http://www.openra.net/packages/cnc-mirrors.txt + MirrorList: https://www.openra.net/packages/cnc-mirrors.txt Extract: ^SupportDir|Content/cnc/conquer.mix: conquer.mix ^SupportDir|Content/cnc/desert.mix: desert.mix diff --git a/mods/d2k/installer/downloads.yaml b/mods/d2k/installer/downloads.yaml index 788ca2cad460..d0ff698e2e00 100644 --- a/mods/d2k/installer/downloads.yaml +++ b/mods/d2k/installer/downloads.yaml @@ -1,7 +1,7 @@ quickinstall: Quick Install Package Type: ZipFile SHA1: 7bcaa55408a5e6995b43a0d02862c4e8574cf861 - MirrorList: http://www.openra.net/packages/d2k-quickinstall-v3-mirrors.txt + MirrorList: https://www.openra.net/packages/d2k-quickinstall-v3-mirrors.txt Extract: ^SupportDir|Content/d2k/v3/BLOXBASE.R16: v3/BLOXBASE.R16 ^SupportDir|Content/d2k/v3/BLOXBAT.R16: v3/BLOXBAT.R16 @@ -269,7 +269,7 @@ quickinstall: Quick Install Package basefiles: Base Content Type: ZipFile SHA1: 399b9a6d5293298204d15ebe61145db155ed963c - MirrorList: http://www.openra.net/packages/d2k-base-v3-mirrors.txt + MirrorList: https://www.openra.net/packages/d2k-base-v3-mirrors.txt Extract: ^SupportDir|Content/d2k/v3/BLOXBASE.R16: v3/BLOXBASE.R16 ^SupportDir|Content/d2k/v3/BLOXBAT.R16: v3/BLOXBAT.R16 @@ -535,7 +535,7 @@ basefiles: Base Content patch106: Patch 1.06 Content Type: ZipFile SHA1: 86f3e468bdea6775d5744737ba5f375e5296e47c - MirrorList: http://www.openra.net/packages/d2k-patch106-v3-mirrors.txt + MirrorList: https://www.openra.net/packages/d2k-patch106-v3-mirrors.txt Extract: ^SupportDir|Content/d2k/v3/BLOXXMAS.R8: v3/BLOXXMAS.R8 ^SupportDir|Content/d2k/v3/DATA.R16: v3/DATA.R16 diff --git a/mods/ra/installer/downloads.yaml b/mods/ra/installer/downloads.yaml index 96f5d24f1f2f..60db7323498c 100644 --- a/mods/ra/installer/downloads.yaml +++ b/mods/ra/installer/downloads.yaml @@ -1,7 +1,7 @@ quickinstall: Quick Install Package Type: ZipFile SHA1: 44241f68e69db9511db82cf83c174737ccda300b - MirrorList: http://www.openra.net/packages/ra-quickinstall-mirrors.txt + MirrorList: https://www.openra.net/packages/ra-quickinstall-mirrors.txt Extract: ^SupportDir|Content/ra/v2/allies.mix: allies.mix ^SupportDir|Content/ra/v2/conquer.mix: conquer.mix @@ -46,7 +46,7 @@ quickinstall: Quick Install Package basefiles: Base Freeware Content Type: ZipFile SHA1: aa022b208a3b45b4a45c00fdae22ccf3c6de3e5c - MirrorList: http://www.openra.net/packages/ra-base-mirrors.txt + MirrorList: https://www.openra.net/packages/ra-base-mirrors.txt Extract: ^SupportDir|Content/ra/v2/allies.mix: allies.mix ^SupportDir|Content/ra/v2/conquer.mix: conquer.mix @@ -63,7 +63,7 @@ basefiles: Base Freeware Content aftermath: Aftermath Expansion Files Type: ZipFile SHA1: d511d4363b485e11c63eecf96d4365d42ec4ef5e - MirrorList: http://www.openra.net/packages/ra-aftermath-mirrors.txt + MirrorList: https://www.openra.net/packages/ra-aftermath-mirrors.txt Extract: ^SupportDir|Content/ra/v2/expand/chrotnk1.aud: expand/chrotnk1.aud ^SupportDir|Content/ra/v2/expand/expand2.mix: expand/expand2.mix @@ -96,6 +96,6 @@ aftermath: Aftermath Expansion Files cncdesert: C&C Desert Tileset Type: ZipFile SHA1: 039849f16e39e4722e8c838a393c8a0d6529fd59 - MirrorList: http://www.openra.net/packages/ra-cncdesert-mirrors.txt + MirrorList: https://www.openra.net/packages/ra-cncdesert-mirrors.txt Extract: ^SupportDir|Content/ra/v2/cnc/desert.mix: cnc/desert.mix diff --git a/mods/ts/installer/downloads.yaml b/mods/ts/installer/downloads.yaml index 96f79ba54a7f..eab6dfcef056 100644 --- a/mods/ts/installer/downloads.yaml +++ b/mods/ts/installer/downloads.yaml @@ -1,7 +1,7 @@ basefiles: Base Freeware Content Type: ZipFile SHA1: 824df30de0004ad13fac29cf16450caafee9fb1b - MirrorList: http://www.openra.net/packages/ts-mirrors.txt + MirrorList: https://www.openra.net/packages/ts-mirrors.txt Extract: ^SupportDir|Content/ts/cache.mix: cache.mix ^SupportDir|Content/ts/conquer.mix: conquer.mix @@ -21,7 +21,7 @@ basefiles: Base Freeware Content fstorm: Expansion Freeware Content Type: ZipFile SHA1: 8bff90870a9348b72cbe91314aec7d3a50311aa9 - MirrorList: http://www.openra.net/packages/fs-mirrors.txt + MirrorList: https://www.openra.net/packages/fs-mirrors.txt Extract: ^SupportDir|Content/ts/firestorm/c_kodiak.shp: firestorm/c_kodiak.shp ^SupportDir|Content/ts/firestorm/coremk.shp: firestorm/coremk.shp @@ -205,7 +205,7 @@ fstorm: Expansion Freeware Content quickinstall: Quick Install Package Type: ZipFile SHA1: d9339e7b6ecf624ac6ca91d25c58b88fb88a49d2 - MirrorList: http://www.openra.net/packages/ts-quickinstall-mirrors.txt + MirrorList: https://www.openra.net/packages/ts-quickinstall-mirrors.txt Extract: ^SupportDir|Content/ts/cache.mix: cache.mix ^SupportDir|Content/ts/conquer.mix: conquer.mix diff --git a/packaging/functions.sh b/packaging/functions.sh index 7f8e42cf0a2d..b346d0e78db6 100755 --- a/packaging/functions.sh +++ b/packaging/functions.sh @@ -3,7 +3,7 @@ #### # This file must stay /bin/sh and POSIX compliant for macOS and BSD portability. -# Copy-paste the entire script into http://shellcheck.net to check. +# Copy-paste the entire script into https://shellcheck.net to check. #### # Compile and publish the core engine and specified mod assemblies to the target directory diff --git a/packaging/linux/openra-mimeinfo.xml.discord.in b/packaging/linux/openra-mimeinfo.xml.discord.in index fd8e5ddf43db..a87d1dc54e6a 100644 --- a/packaging/linux/openra-mimeinfo.xml.discord.in +++ b/packaging/linux/openra-mimeinfo.xml.discord.in @@ -1,5 +1,5 @@ - + diff --git a/packaging/linux/openra-mimeinfo.xml.in b/packaging/linux/openra-mimeinfo.xml.in index f4b7e3780536..2240980c0ac5 100644 --- a/packaging/linux/openra-mimeinfo.xml.in +++ b/packaging/linux/openra-mimeinfo.xml.in @@ -1,5 +1,5 @@ - + diff --git a/packaging/linux/openra.appimage.in b/packaging/linux/openra.appimage.in index ff21037dce34..f5dcaf59f505 100755 --- a/packaging/linux/openra.appimage.in +++ b/packaging/linux/openra.appimage.in @@ -58,7 +58,7 @@ if [ "${rc}" != 0 ] && [ "${rc}" != 1 ]; then if [ -d Support/Logs ]; then LOGS="${PWD}/Support/Logs" fi - ERROR_MESSAGE=$(printf "%s has encountered a fatal error.\nPlease refer to the crash logs and FAQ for more information.\n\nLog files are located in %s\nThe FAQ is available at http://wiki.openra.net/FAQ" "{MODNAME}" "${LOGS}") + ERROR_MESSAGE=$(printf "%s has encountered a fatal error.\nPlease refer to the crash logs and FAQ for more information.\n\nLog files are located in %s\nThe FAQ is available at https://wiki.openra.net/FAQ" "{MODNAME}" "${LOGS}") if command -v zenity > /dev/null; then zenity --no-wrap --error --title "{MODNAME}" --no-markup --text "${ERROR_MESSAGE}" 2> /dev/null || : elif command -v kdialog > /dev/null; then diff --git a/packaging/linux/openra.in b/packaging/linux/openra.in index dc7474c1745a..3a02e3e87bbe 100755 --- a/packaging/linux/openra.in +++ b/packaging/linux/openra.in @@ -29,7 +29,7 @@ if [ "${rc}" != 0 ] && [ "${rc}" != 1 ]; then if [ -d Support/Logs ]; then LOGS="${PWD}/Support/Logs" fi - ERROR_MESSAGE=$(printf "%s has encountered a fatal error.\nPlease refer to the crash logs and FAQ for more information.\n\nLog files are located in %s\nThe FAQ is available at http://wiki.openra.net/FAQ" "{MODNAME}" "${LOGS}") + ERROR_MESSAGE=$(printf "%s has encountered a fatal error.\nPlease refer to the crash logs and FAQ for more information.\n\nLog files are located in %s\nThe FAQ is available at https://wiki.openra.net/FAQ" "{MODNAME}" "${LOGS}") if command -v zenity > /dev/null; then zenity --no-wrap --error --title "{MODNAME}" --no-markup --text "${ERROR_MESSAGE}" 2> /dev/null || : elif command -v kdialog > /dev/null; then diff --git a/packaging/macos/Info.plist.in b/packaging/macos/Info.plist.in index 7897c7414fc8..337f8c992a7b 100644 --- a/packaging/macos/Info.plist.in +++ b/packaging/macos/Info.plist.in @@ -1,5 +1,5 @@ - + CFBundleDevelopmentRegion diff --git a/packaging/macos/buildpackage.sh b/packaging/macos/buildpackage.sh index c25657df1f2e..7a06b9b62e1e 100755 --- a/packaging/macos/buildpackage.sh +++ b/packaging/macos/buildpackage.sh @@ -121,7 +121,7 @@ mkdir -p "${TEMPLATE_DIR}/Contents/MacOS/arm64" echo "APPL????" > "${TEMPLATE_DIR}/Contents/PkgInfo" cp Info.plist.in "${TEMPLATE_DIR}/Contents/Info.plist" modify_plist "{DEV_VERSION}" "${TAG}" "${TEMPLATE_DIR}/Contents/Info.plist" -modify_plist "{FAQ_URL}" "http://wiki.openra.net/FAQ" "${TEMPLATE_DIR}/Contents/Info.plist" +modify_plist "{FAQ_URL}" "https://wiki.openra.net/FAQ" "${TEMPLATE_DIR}/Contents/Info.plist" modify_plist "{MINIMUM_SYSTEM_VERSION}" "10.11" "${TEMPLATE_DIR}/Contents/Info.plist" # Compile universal (x86_64 + arm64) arch-specific apphosts diff --git a/packaging/macos/entitlements.plist b/packaging/macos/entitlements.plist index dcfe14ccd438..570ad63c260b 100644 --- a/packaging/macos/entitlements.plist +++ b/packaging/macos/entitlements.plist @@ -1,5 +1,5 @@ - + com.apple.security.cs.allow-jit diff --git a/packaging/windows/OpenRA.nsi b/packaging/windows/OpenRA.nsi index 84380e548493..b45180828135 100644 --- a/packaging/windows/OpenRA.nsi +++ b/packaging/windows/OpenRA.nsi @@ -12,7 +12,7 @@ ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License -; along with OpenRA. If not, see . +; along with OpenRA. If not, see . !include "MUI2.nsh" !include "FileFunc.nsh" @@ -193,7 +193,7 @@ Section "-Uninstaller" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenRA${SUFFIX}" "InstallLocation" "$INSTDIR" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenRA${SUFFIX}" "DisplayIcon" "$INSTDIR\ra.ico" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenRA${SUFFIX}" "Publisher" "OpenRA developers" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenRA${SUFFIX}" "URLInfoAbout" "http://openra.net" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenRA${SUFFIX}" "URLInfoAbout" "https://openra.net" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenRA${SUFFIX}" "DisplayVersion" "${TAG}" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenRA${SUFFIX}" "NoModify" "1" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenRA${SUFFIX}" "NoRepair" "1" From 0c572a862cc2037b2b8cadd2c7f3232220d87a9e Mon Sep 17 00:00:00 2001 From: "N.N" Date: Sat, 1 Jun 2024 10:36:23 +0200 Subject: [PATCH 05/13] Fix actorSpawnManager use only one spawnpoint at the moment --- OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs b/OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs index a8a067b7d025..a691dd93635e 100644 --- a/OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs +++ b/OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs @@ -114,6 +114,9 @@ void ITick.Tick(Actor self) // Always spawn at least one actor, plus // however many needed to reach the minimum. SpawnActor(self, spawnPoint); + + // choose new random SpawnPoint for each actor + spawnPoint = GetRandomSpawnPoint(self.World, self.World.SharedRandom); } while (actorsPresent < info.Minimum); } From 616046179fa74bbb5b9031f3be112764b8692af5 Mon Sep 17 00:00:00 2001 From: "N.N" Date: Sat, 1 Jun 2024 09:32:45 +0200 Subject: [PATCH 06/13] Fix engineer cannot restore ally vehicles --- mods/d2k/rules/defaults.yaml | 4 ++-- mods/d2k/rules/infantry.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml index 4f9bbe08dc67..f05220f8937a 100644 --- a/mods/d2k/rules/defaults.yaml +++ b/mods/d2k/rules/defaults.yaml @@ -308,7 +308,7 @@ AllowedTerrain: Sand, Rock, Transition, Concrete, Spice, SpiceSand, SpiceBlobs, Dune Locomotor: tank Targetable: - TargetTypes: Ground, Vehicle + TargetTypes: Ground, Vehicle, Husk RequiresForceFire: true WithColoredOverlay@husk: Color: 00000060 @@ -344,7 +344,7 @@ TransformOnCapture: ForceHealthPercentage: 20 InfiltrateForTransform: - Types: husk + Types: Husk ForceHealthPercentage: 20 ^AircraftHusk: diff --git a/mods/d2k/rules/infantry.yaml b/mods/d2k/rules/infantry.yaml index c832b3e0d2ce..8d5258f64b77 100644 --- a/mods/d2k/rules/infantry.yaml +++ b/mods/d2k/rules/infantry.yaml @@ -56,7 +56,7 @@ engineer: CaptureTypes: husk PlayerExperience: 10 Infiltrates: - Types: husk + Types: Husk ValidRelationships: Ally Captures@Cliff: CaptureTypes: cliff From 9302bac6199fbc925a85fd7a08fc2ba4b9317d16 Mon Sep 17 00:00:00 2001 From: "N.N" Date: Fri, 15 Mar 2024 11:52:04 +0100 Subject: [PATCH 07/13] Replace BLOXMAS.R8 with R16 eqivalent - file BLOXXMAS.R8 removed from mod content as its no longer needed --- mods/d2k/bits/customtiles.r16 | Bin 1661600 -> 1661600 bytes mods/d2k/mod.yaml | 2 +- mods/d2k/tilesets/arrakis.yaml | 192 ++++++++++++++++----------------- 3 files changed, 97 insertions(+), 97 deletions(-) diff --git a/mods/d2k/bits/customtiles.r16 b/mods/d2k/bits/customtiles.r16 index 4f11e417db5ecd81587d55228634c2a553355d5a..0d80eafb8d2cd0eb8524ff4868723f159e2265c2 100644 GIT binary patch delta 131432 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 Date: Fri, 14 Jun 2024 15:32:14 +0100 Subject: [PATCH 08/13] Fix map editor copy-paste for isometric maps. When dealing with isometric maps, the copy paste region needs to copy the CellCoords area covered by the CellRegion. This is equivalent to the selected rectangle on screen. Using the cell region itself invokes cell->map->cell conversion that doesn't roundtrip and thus some of the selected cells don't get copied. Also when pasting terrain + actors, we need to fix the sequencing to clear actors on the current terrain, before adjusting the height and pasting in new actors. The current sequencing means we are clearing actors after having adjusted the terrain height, and affects the wrong area. --- .../EditorBrushes/EditorCopyPasteBrush.cs | 28 +++++++++++-------- .../Traits/World/EditorResourceLayer.cs | 2 +- .../Logic/Editor/MapEditorSelectionLogic.cs | 2 +- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/OpenRA.Mods.Common/EditorBrushes/EditorCopyPasteBrush.cs b/OpenRA.Mods.Common/EditorBrushes/EditorCopyPasteBrush.cs index c0e0a0b4c02e..1c5bc12052b3 100644 --- a/OpenRA.Mods.Common/EditorBrushes/EditorCopyPasteBrush.cs +++ b/OpenRA.Mods.Common/EditorBrushes/EditorCopyPasteBrush.cs @@ -165,7 +165,7 @@ EditorClipboard CopySelectionContents() tiles.Add(cell, new ClipboardTile(mapTiles[cell], mapResources[cell], resourceLayerContents, mapHeight[cell])); if (copyFilters.HasFlag(MapCopyFilters.Actors)) - foreach (var preview in selection.SelectMany(editorActorLayer.PreviewsAt).Distinct()) + foreach (var preview in selection.CellCoords.SelectMany(editorActorLayer.PreviewsAt).Distinct()) previews.TryAdd(preview.ID, preview); } @@ -182,6 +182,15 @@ public void Do() var sourcePos = clipboard.CellRegion.TopLeft; var pasteVec = new CVec(pastePosition.X - sourcePos.X, pastePosition.Y - sourcePos.Y); + if (copyFilters.HasFlag(MapCopyFilters.Actors)) + { + // Clear any existing actors in the paste cells. + var selectionSize = clipboard.CellRegion.BottomRight - clipboard.CellRegion.TopLeft; + var pasteRegion = new CellRegion(map.Grid.Type, pastePosition, pastePosition + selectionSize); + foreach (var regionActor in pasteRegion.CellCoords.SelectMany(editorActorLayer.PreviewsAt).ToHashSet()) + editorActorLayer.Remove(regionActor); + } + foreach (var tileKeyValuePair in clipboard.Tiles) { var position = tileKeyValuePair.Key + pasteVec; @@ -209,12 +218,6 @@ public void Do() if (copyFilters.HasFlag(MapCopyFilters.Actors)) { - // Clear any existing actors in the paste cells. - var selectionSize = clipboard.CellRegion.BottomRight - clipboard.CellRegion.TopLeft; - var pasteRegion = new CellRegion(map.Grid.Type, pastePosition, pastePosition + selectionSize); - foreach (var regionActor in pasteRegion.SelectMany(editorActorLayer.PreviewsAt).ToHashSet()) - editorActorLayer.Remove(regionActor); - // Now place actors. foreach (var actorKeyValuePair in clipboard.Actors) { @@ -238,6 +241,13 @@ public void Do() public void Undo() { + if (copyFilters.HasFlag(MapCopyFilters.Actors)) + { + // Clear existing actors. + foreach (var regionActor in undoClipboard.CellRegion.CellCoords.SelectMany(editorActorLayer.PreviewsAt).Distinct().ToList()) + editorActorLayer.Remove(regionActor); + } + foreach (var tileKeyValuePair in undoClipboard.Tiles) { var position = tileKeyValuePair.Key; @@ -262,10 +272,6 @@ public void Undo() if (copyFilters.HasFlag(MapCopyFilters.Actors)) { - // Clear existing actors. - foreach (var regionActor in undoClipboard.CellRegion.SelectMany(editorActorLayer.PreviewsAt).Distinct().ToList()) - editorActorLayer.Remove(regionActor); - // Place actors back again. foreach (var actor in undoClipboard.Actors.Values) editorActorLayer.Add(actor); diff --git a/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs b/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs index b0b4d063c423..c327d1660d29 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs @@ -185,7 +185,7 @@ void UpdateNetWorth(string oldResourceType, int oldDensity, string newResourceTy public int CalculateRegionValue(CellRegion sourceRegion) { var resourceValueInRegion = 0; - foreach (var cell in sourceRegion) + foreach (var cell in sourceRegion.CellCoords) { var mcell = cell.ToMPos(Map); if (Map.Resources.Contains(mcell) && Map.Resources[mcell].Type != 0) diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorSelectionLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorSelectionLogic.cs index 12b77d38975e..2d7432201b8c 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorSelectionLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorSelectionLogic.cs @@ -123,7 +123,7 @@ EditorClipboard CopySelectionContents() tiles.Add(cell, new ClipboardTile(mapTiles[cell], mapResources[cell], resourceLayer?.GetResource(cell), mapHeight[cell])); if (copyFilters.HasFlag(MapCopyFilters.Actors)) - foreach (var preview in selection.SelectMany(editorActorLayer.PreviewsAt).Distinct()) + foreach (var preview in selection.CellCoords.SelectMany(editorActorLayer.PreviewsAt).Distinct()) previews.TryAdd(preview.ID, preview); } From 34a68cd2cad1106debee0eaabea8daa434db068a Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Sat, 15 Jun 2024 21:29:38 +0100 Subject: [PATCH 09/13] Avoid keeping ActorInitializers in memory. The SupportPowerManager and WithSpriteBody trait captured the ActorInitializer in lambda expressions, which keeps it alive as long as the trait. The lambdas didn't need to capture the ActorInitializer, so rejig them to allow the ActorInitializer to be reclaimed after the traits have been created. As the TypeDictionary in the ActorInitializer can be quite large, this helps reduce memory usage. --- OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs | 12 +++++++----- .../Traits/SupportPowers/SupportPowerManager.cs | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs b/OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs index 91ba850e9c51..feeb08ce5197 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithSpriteBody.cs @@ -72,20 +72,22 @@ public WithSpriteBody(ActorInitializer init, WithSpriteBodyInfo info) protected WithSpriteBody(ActorInitializer init, WithSpriteBodyInfo info, Func baseFacing) : base(info) { - var rs = init.Self.Trait(); + var self = init.Self; + + var rs = self.Trait(); bool Paused() => IsTraitPaused && - DefaultAnimation.CurrentSequence.Name == NormalizeSequence(init.Self, Info.Sequence); + DefaultAnimation.CurrentSequence.Name == NormalizeSequence(self, Info.Sequence); Func subtractDAT = null; if (info.ForceToGround) - subtractDAT = () => new WVec(0, 0, -init.Self.World.Map.DistanceAboveTerrain(init.Self.CenterPosition).Length); + subtractDAT = () => new WVec(0, 0, -self.World.Map.DistanceAboveTerrain(self.CenterPosition).Length); - DefaultAnimation = new Animation(init.World, rs.GetImage(init.Self), baseFacing, Paused); + DefaultAnimation = new Animation(init.World, rs.GetImage(self), baseFacing, Paused); rs.Add(new AnimationWithOffset(DefaultAnimation, subtractDAT, () => IsTraitDisabled), info.Palette, info.IsPlayerPalette); // Cache the bounds from the default sequence to avoid flickering when the animation changes - boundsAnimation = new Animation(init.World, rs.GetImage(init.Self), baseFacing, Paused); + boundsAnimation = new Animation(init.World, rs.GetImage(self), baseFacing, Paused); boundsAnimation.PlayRepeating(info.Sequence); } diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs index 19e46b7a1176..2ee754ebd91f 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs @@ -39,7 +39,7 @@ public SupportPowerManager(ActorInitializer init) Self = init.Self; DevMode = Self.Trait(); TechTree = Self.Trait(); - RadarPings = Exts.Lazy(() => init.World.WorldActor.TraitOrDefault()); + RadarPings = Exts.Lazy(() => Self.World.WorldActor.TraitOrDefault()); init.World.ActorAdded += ActorAdded; init.World.ActorRemoved += ActorRemoved; From cf0e73e75ee8cb92b50faccfac9f25efb0946f9e Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Fri, 14 Jun 2024 16:55:50 +0100 Subject: [PATCH 10/13] Improve performance of copy-paste in map editor. - EditorActorLayer now tracks previews on map with a SpatiallyPartitioned instead of a Dictionary. This allows the copy-paste logic to call an efficient PreviewsInCellRegion method, instead of asking for previews cell-by-cell. - EditorActorPreview subscribes to the CellEntryChanged methods on the map. Previously the preview was refreshed regardless of which cell changed. Now the preview only regenerates if the preview's footprint has been affected. --- OpenRA.Game/Map/CellCoordsRegion.cs | 5 + .../Traits/World/TSEditorResourceLayer.cs | 2 +- .../EditorBrushes/EditorCopyPasteBrush.cs | 6 +- .../EditorBrushes/EditorDefaultBrush.cs | 2 +- .../Traits/World/EditorActorLayer.cs | 94 +++++++++---------- .../Traits/World/EditorActorPreview.cs | 13 ++- .../Logic/Editor/MapEditorSelectionLogic.cs | 3 +- 7 files changed, 63 insertions(+), 62 deletions(-) diff --git a/OpenRA.Game/Map/CellCoordsRegion.cs b/OpenRA.Game/Map/CellCoordsRegion.cs index 6ec0d928b287..2f6914f2c850 100644 --- a/OpenRA.Game/Map/CellCoordsRegion.cs +++ b/OpenRA.Game/Map/CellCoordsRegion.cs @@ -64,6 +64,11 @@ public CellCoordsRegion(CPos topLeft, CPos bottomRight) BottomRight = bottomRight; } + public bool Contains(CPos cell) + { + return cell.X >= TopLeft.X && cell.X <= BottomRight.X && cell.Y >= TopLeft.Y && cell.Y <= BottomRight.Y; + } + public override string ToString() { return $"{TopLeft}->{BottomRight}"; diff --git a/OpenRA.Mods.Cnc/Traits/World/TSEditorResourceLayer.cs b/OpenRA.Mods.Cnc/Traits/World/TSEditorResourceLayer.cs index 3b3e7ed294e2..3bf3b92e9a05 100644 --- a/OpenRA.Mods.Cnc/Traits/World/TSEditorResourceLayer.cs +++ b/OpenRA.Mods.Cnc/Traits/World/TSEditorResourceLayer.cs @@ -46,7 +46,7 @@ bool IsValidVeinNeighbour(CPos cell, CPos neighbour) return false; // Cell is automatically valid if it contains a veinhole actor - if (actorLayer.PreviewsAt(neighbour).Any(a => info.VeinholeActors.Contains(a.Info.Name))) + if (actorLayer.PreviewsAtCell(neighbour).Any(a => info.VeinholeActors.Contains(a.Info.Name))) return true; // Neighbour must be flat or a cardinal slope, unless the resource cell itself is a slope diff --git a/OpenRA.Mods.Common/EditorBrushes/EditorCopyPasteBrush.cs b/OpenRA.Mods.Common/EditorBrushes/EditorCopyPasteBrush.cs index 1c5bc12052b3..23de19fe38c3 100644 --- a/OpenRA.Mods.Common/EditorBrushes/EditorCopyPasteBrush.cs +++ b/OpenRA.Mods.Common/EditorBrushes/EditorCopyPasteBrush.cs @@ -165,7 +165,7 @@ EditorClipboard CopySelectionContents() tiles.Add(cell, new ClipboardTile(mapTiles[cell], mapResources[cell], resourceLayerContents, mapHeight[cell])); if (copyFilters.HasFlag(MapCopyFilters.Actors)) - foreach (var preview in selection.CellCoords.SelectMany(editorActorLayer.PreviewsAt).Distinct()) + foreach (var preview in editorActorLayer.PreviewsInCellRegion(selection.CellCoords)) previews.TryAdd(preview.ID, preview); } @@ -187,7 +187,7 @@ public void Do() // Clear any existing actors in the paste cells. var selectionSize = clipboard.CellRegion.BottomRight - clipboard.CellRegion.TopLeft; var pasteRegion = new CellRegion(map.Grid.Type, pastePosition, pastePosition + selectionSize); - foreach (var regionActor in pasteRegion.CellCoords.SelectMany(editorActorLayer.PreviewsAt).ToHashSet()) + foreach (var regionActor in editorActorLayer.PreviewsInCellRegion(pasteRegion.CellCoords).ToList()) editorActorLayer.Remove(regionActor); } @@ -244,7 +244,7 @@ public void Undo() if (copyFilters.HasFlag(MapCopyFilters.Actors)) { // Clear existing actors. - foreach (var regionActor in undoClipboard.CellRegion.CellCoords.SelectMany(editorActorLayer.PreviewsAt).Distinct().ToList()) + foreach (var regionActor in editorActorLayer.PreviewsInCellRegion(undoClipboard.CellRegion.CellCoords).ToList()) editorActorLayer.Remove(regionActor); } diff --git a/OpenRA.Mods.Common/EditorBrushes/EditorDefaultBrush.cs b/OpenRA.Mods.Common/EditorBrushes/EditorDefaultBrush.cs index 612babc24b41..ba3decbc1227 100644 --- a/OpenRA.Mods.Common/EditorBrushes/EditorDefaultBrush.cs +++ b/OpenRA.Mods.Common/EditorBrushes/EditorDefaultBrush.cs @@ -120,7 +120,7 @@ public bool HandleMouseInput(MouseInput mi) worldPixel = worldRenderer.Viewport.ViewToWorldPx(mi.Location); var cell = worldRenderer.Viewport.ViewToWorld(mi.Location); - var underCursor = editorLayer.PreviewsAt(worldPixel).MinByOrDefault(CalculateActorSelectionPriority); + var underCursor = editorLayer.PreviewsAtWorldPixel(worldPixel).MinByOrDefault(CalculateActorSelectionPriority); var resourceUnderCursor = resourceLayer?.GetResource(cell).Type; if (underCursor != null) diff --git a/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs b/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs index e1acae47be00..0b730c0e45a2 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs @@ -40,8 +40,9 @@ public class EditorActorLayer : IWorldLoaded, ITickRender, IRender, IRadarSignat { readonly EditorActorLayerInfo info; readonly List previews = new(); - readonly Dictionary> cellMap = new(); + int2 cellOffset; + SpatiallyPartitioned cellMap; SpatiallyPartitioned screenMap; WorldRenderer worldRenderer; @@ -74,6 +75,12 @@ public void WorldLoaded(World world, WorldRenderer wr) foreach (var pr in Players.Players.Values) wr.UpdatePalettesForPlayer(pr.Name, pr.Color, false); + cellOffset = new int2(world.Map.AllCells.Min(c => c.X), world.Map.AllCells.Min((c) => c.Y)); + var cellOffsetMax = new int2(world.Map.AllCells.Max(c => c.X), world.Map.AllCells.Max((c) => c.Y)); + var mapCellSize = cellOffsetMax - cellOffset; + cellMap = new SpatiallyPartitioned( + mapCellSize.X, mapCellSize.Y, Exts.IntegerDivisionRoundingAwayFromZero(info.BinSize, world.Map.Grid.TileSize.Width)); + var ts = world.Map.Grid.TileSize; var width = world.Map.MapSize.X * ts.Width; var height = world.Map.MapSize.Y * ts.Height; @@ -102,7 +109,7 @@ public virtual IEnumerable Render(Actor self, WorldRenderer wr) if (wr.World.Type != WorldType.Editor) return NoRenderables; - return PreviewsInBox(wr.Viewport.TopLeft, wr.Viewport.BottomRight) + return PreviewsInScreenBox(wr.Viewport.TopLeft, wr.Viewport.BottomRight) .SelectMany(p => p.Render()); } @@ -117,7 +124,7 @@ public IEnumerable RenderAnnotations(Actor self, WorldRenderer wr) if (wr.World.Type != WorldType.Editor) return NoRenderables; - return PreviewsInBox(wr.Viewport.TopLeft, wr.Viewport.BottomRight) + return PreviewsInScreenBox(wr.Viewport.TopLeft, wr.Viewport.BottomRight) .SelectMany(p => p.RenderAnnotations()); } @@ -147,13 +154,9 @@ public void Add(EditorActorPreview preview, bool initialSetup = false) if (!preview.Bounds.IsEmpty) screenMap.Add(preview, preview.Bounds); - // Fallback to the actor's CenterPosition for the ActorMap if it has no Footprint - var footprint = preview.Footprint.Select(kv => kv.Key).ToArray(); - if (footprint.Length == 0) - footprint = new[] { worldRenderer.World.Map.CellContaining(preview.CenterPosition) }; - - foreach (var cell in footprint) - AddPreviewLocation(preview, cell); + var cellFootprintBounds = Footprint(preview).Select( + cell => new Rectangle(cell.X - cellOffset.X, cell.Y - cellOffset.Y, 1, 1)).Union(); + cellMap.Add(preview, cellFootprintBounds); preview.AddedToEditor(); @@ -166,26 +169,20 @@ public void Add(EditorActorPreview preview, bool initialSetup = false) } } + IEnumerable Footprint(EditorActorPreview preview) + { + // Fallback to the actor's CenterPosition for the ActorMap if it has no Footprint + if (preview.Footprint.Count == 0) + return new[] { worldRenderer.World.Map.CellContaining(preview.CenterPosition) }; + return preview.Footprint.Keys; + } + public void Remove(EditorActorPreview preview) { previews.Remove(preview); screenMap.Remove(preview); - // Fallback to the actor's CenterPosition for the ActorMap if it has no Footprint - var footprint = preview.Footprint.Select(kv => kv.Key).ToArray(); - if (footprint.Length == 0) - footprint = new[] { worldRenderer.World.Map.CellContaining(preview.CenterPosition) }; - - foreach (var cell in footprint) - { - if (!cellMap.TryGetValue(cell, out var list)) - continue; - - list.Remove(preview); - - if (list.Count == 0) - cellMap.Remove(cell); - } + cellMap.Remove(preview); preview.RemovedFromEditor(); UpdateNeighbours(preview.Footprint); @@ -236,49 +233,46 @@ void UpdateNeighbours(IReadOnlyDictionary footprint) { // Include actors inside the footprint too var cells = Util.ExpandFootprint(footprint.Keys, true); - foreach (var p in cells.SelectMany(c => PreviewsAt(c))) + foreach (var p in cells.SelectMany(PreviewsAtCell)) p.ReplaceInit(new RuntimeNeighbourInit(NeighbouringPreviews(p.Footprint))); } - void AddPreviewLocation(EditorActorPreview preview, CPos location) - { - if (!cellMap.TryGetValue(location, out var list)) - { - list = new List(); - cellMap.Add(location, list); - } - - list.Add(preview); - } - Dictionary NeighbouringPreviews(IReadOnlyDictionary footprint) { var cells = Util.ExpandFootprint(footprint.Keys, true).Except(footprint.Keys); - return cells.ToDictionary(c => c, c => PreviewsAt(c).Select(p => p.Info.Name).ToArray()); + return cells.ToDictionary(c => c, c => PreviewsAtCell(c).Select(p => p.Info.Name).ToArray()); } - public IEnumerable PreviewsInBox(int2 a, int2 b) + public IEnumerable PreviewsInScreenBox(int2 a, int2 b) { - return screenMap.InBox(Rectangle.FromLTRB(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Max(a.X, b.X), Math.Max(a.Y, b.Y))); + return PreviewsInScreenBox(Rectangle.FromLTRB(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Max(a.X, b.X), Math.Max(a.Y, b.Y))); } - public IEnumerable PreviewsInBox(Rectangle r) + public IEnumerable PreviewsInScreenBox(Rectangle r) { return screenMap.InBox(r); } - public IEnumerable PreviewsAt(CPos cell) + public IEnumerable PreviewsInCellRegion(CellCoordsRegion region) { - if (cellMap.TryGetValue(cell, out var list)) - return list; + return cellMap.InBox(Rectangle.FromLTRB( + region.TopLeft.X - cellOffset.X, + region.TopLeft.Y - cellOffset.Y, + region.BottomRight.X - cellOffset.X + 1, + region.BottomRight.Y - cellOffset.Y + 1)) + .Where(p => Footprint(p).Any(region.Contains)); + } - return Enumerable.Empty(); + public IEnumerable PreviewsAtCell(CPos cell) + { + return cellMap.At(new int2(cell.X - cellOffset.X, cell.Y - cellOffset.Y)) + .Where(p => Footprint(p).Any(fp => fp == cell)); } public SubCell FreeSubCellAt(CPos cell) { var map = worldRenderer.World.Map; - var previews = PreviewsAt(cell).ToList(); + var previews = PreviewsAtCell(cell).ToList(); if (previews.Count == 0) return map.Grid.DefaultSubCell; @@ -293,7 +287,7 @@ public SubCell FreeSubCellAt(CPos cell) return SubCell.Invalid; } - public IEnumerable PreviewsAt(int2 worldPx) + public IEnumerable PreviewsAtWorldPixel(int2 worldPx) { return screenMap.At(worldPx); } @@ -325,9 +319,9 @@ public List Save() public void PopulateRadarSignatureCells(Actor self, List<(CPos Cell, Color Color)> destinationBuffer) { - foreach (var previewsForCell in cellMap) - foreach (var preview in previewsForCell.Value) - destinationBuffer.Add((previewsForCell.Key, preview.RadarColor)); + foreach (var preview in cellMap.Items) + foreach (var cell in Footprint(preview)) + destinationBuffer.Add((cell, preview.RadarColor)); } public EditorActorPreview this[string id] diff --git a/OpenRA.Mods.Common/Traits/World/EditorActorPreview.cs b/OpenRA.Mods.Common/Traits/World/EditorActorPreview.cs index 0613a45362b6..fcef483161ba 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorActorPreview.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorActorPreview.cs @@ -68,8 +68,8 @@ public EditorActorPreview(WorldRenderer worldRenderer, string id, ActorReference if (!world.Map.Rules.Actors.TryGetValue(reference.Type.ToLowerInvariant(), out Info)) throw new InvalidDataException($"Actor {id} of unknown type {reference.Type.ToLowerInvariant()}"); - UpdateFromCellChange(); GenerateFootprint(); + UpdateFromCellChange(null); tooltip = Info.TraitInfos().FirstOrDefault(info => info.EnabledByDefault) as TooltipInfoBase ?? Info.TraitInfos().FirstOrDefault(info => info.EnabledByDefault); @@ -79,8 +79,7 @@ public EditorActorPreview(WorldRenderer worldRenderer, string id, ActorReference terrainRadarColorInfo = Info.TraitInfoOrDefault(); UpdateRadarColor(); - // TODO: updating all actors on the map is not very efficient. - onCellEntryChanged = _ => UpdateFromCellChange(); + onCellEntryChanged = cell => UpdateFromCellChange(cell); } public EditorActorPreview WithId(string id) @@ -88,8 +87,11 @@ public EditorActorPreview WithId(string id) return new EditorActorPreview(worldRenderer, id, reference.Clone(), Owner); } - void UpdateFromCellChange() + void UpdateFromCellChange(CPos? cellChanged) { + if (cellChanged != null && !Footprint.ContainsKey(cellChanged.Value)) + return; + CenterPosition = PreviewPosition(worldRenderer.World, reference); GeneratePreviews(); GenerateBounds(); @@ -169,8 +171,8 @@ public void AddedToEditor() foreach (var notify in Info.TraitInfos()) editorData[notify] = notify.AddedToEditor(this, worldRenderer.World); - // TODO: this should subscribe to ramp cell map as well. worldRenderer.World.Map.Height.CellEntryChanged += onCellEntryChanged; + worldRenderer.World.Map.Ramp.CellEntryChanged += onCellEntryChanged; } public void RemovedFromEditor() @@ -179,6 +181,7 @@ public void RemovedFromEditor() kv.Key.RemovedFromEditor(this, worldRenderer.World, kv.Value); worldRenderer.World.Map.Height.CellEntryChanged -= onCellEntryChanged; + worldRenderer.World.Map.Ramp.CellEntryChanged -= onCellEntryChanged; } public void AddInit(T init) where T : ActorInit diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorSelectionLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorSelectionLogic.cs index 2d7432201b8c..d2c73a3d2ebf 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorSelectionLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorSelectionLogic.cs @@ -11,7 +11,6 @@ using System; using System.Collections.Generic; -using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.EditorBrushes; using OpenRA.Mods.Common.Traits; @@ -123,7 +122,7 @@ EditorClipboard CopySelectionContents() tiles.Add(cell, new ClipboardTile(mapTiles[cell], mapResources[cell], resourceLayer?.GetResource(cell), mapHeight[cell])); if (copyFilters.HasFlag(MapCopyFilters.Actors)) - foreach (var preview in selection.CellCoords.SelectMany(editorActorLayer.PreviewsAt).Distinct()) + foreach (var preview in editorActorLayer.PreviewsInCellRegion(selection.CellCoords)) previews.TryAdd(preview.ID, preview); } From 6a7159e9a159f67ef1a18d6bcdccd28173089330 Mon Sep 17 00:00:00 2001 From: tjk-ws <67940579+tjk-ws@users.noreply.github.com> Date: Sun, 16 Jun 2024 19:00:28 -0500 Subject: [PATCH 11/13] Fix aircraft that don't rearm stalling over invalid targets --- OpenRA.Mods.Common/Activities/Air/FlyAttack.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs b/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs index f2546fab6274..8b5fe82f9558 100644 --- a/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs +++ b/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs @@ -119,10 +119,14 @@ public override bool Tick(Actor self) if (useLastVisibleTarget && !lastVisibleTarget.IsValidFor(self)) return true; - // If all valid weapons have depleted their ammo and Rearmable trait exists, return to RearmActor to reload - // and resume the activity after reloading if AbortOnResupply is set to 'false' - if (rearmable != null && !useLastVisibleTarget && attackAircraft.Armaments.All(x => x.IsTraitPaused || !x.Weapon.IsValidAgainst(target, self.World, self))) + // If all weapons are invalid against the target + if (!useLastVisibleTarget && attackAircraft.Armaments.All(x => x.IsTraitPaused || !x.Weapon.IsValidAgainst(target, self.World, self))) { + // If Rearmable trait exists, return to RearmActor to reload and resume the activity after + // reloading if AbortOnResupply is set to 'false'. + if (rearmable == null) + return true; + // Attack moves never resupply if (source == AttackSource.AttackMove) return true; From 2a3271b0d0d78932d430fed5503c117e5b38d6a8 Mon Sep 17 00:00:00 2001 From: tjk-ws <67940579+tjk-ws@users.noreply.github.com> Date: Sun, 16 Jun 2024 19:09:54 -0500 Subject: [PATCH 12/13] Fix autotarget not checking all attack traits for targets --- OpenRA.Mods.Common/Traits/AutoTarget.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OpenRA.Mods.Common/Traits/AutoTarget.cs b/OpenRA.Mods.Common/Traits/AutoTarget.cs index 4194c7751aee..38f1123f907c 100644 --- a/OpenRA.Mods.Common/Traits/AutoTarget.cs +++ b/OpenRA.Mods.Common/Traits/AutoTarget.cs @@ -303,7 +303,9 @@ public Target ScanForTarget(Actor self, bool allowMove, bool allowTurn, bool ign if (attackStances != PlayerRelationship.None) { var range = Info.ScanRadius > 0 ? WDist.FromCells(Info.ScanRadius) : ab.GetMaximumRange(); - return ChooseTarget(self, ab, attackStances, range, allowMove, allowTurn); + var target = ChooseTarget(self, ab, attackStances, range, allowMove, allowTurn); + if (target.Type != TargetType.Invalid) + return target; } } } From ccb1bd7a74219c23f413ce1dc53e845c2accc679 Mon Sep 17 00:00:00 2001 From: "N.N" Date: Wed, 10 Jan 2024 15:30:52 +0100 Subject: [PATCH 13/13] Enable paying upfront Fix tab availability on low money Co-Authored-By: Gustas <37534529+PunkPun@users.noreply.github.com> --- .../Traits/Player/ProductionQueue.cs | 94 ++++++++++++++++--- .../Traits/ProductionQueueFromSelection.cs | 2 +- .../Logic/Ingame/ClassicProductionLogic.cs | 2 +- .../Widgets/ProductionPaletteWidget.cs | 7 +- 4 files changed, 90 insertions(+), 15 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs b/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs index 4466b31e6ebd..7f703d89cc77 100644 --- a/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs +++ b/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs @@ -38,6 +38,9 @@ public class ProductionQueueInfo : TraitInfo, IRulesetLoaded [Desc("Should the prerequisite remain enabled if the owner changes?")] public readonly bool Sticky = true; + [Desc("Player must pay for item upfront")] + public readonly bool PayUpFront = false; + [Desc("Should right clicking on the icon instantly cancel the production instead of putting it on hold?")] public readonly bool DisallowPaused = false; @@ -185,7 +188,16 @@ protected void ClearQueue() { // Refund the current item foreach (var item in Queue) + { + if (item.ResourcesPaid > 0) + { + playerResources.GiveResources(item.ResourcesPaid); + item.RemainingCost += item.ResourcesPaid; + } + playerResources.GiveCash(item.TotalCost - item.RemainingCost); + } + Queue.Clear(); } @@ -267,6 +279,11 @@ public virtual bool IsProducing(ProductionItem item) return Queue.Count > 0 && Queue[0] == item; } + public virtual bool IsInQueue(ActorInfo actor) + { + return Queue.Any(i => i.Item == actor.Name); + } + public ProductionItem CurrentItem() { return Queue.ElementAtOrDefault(0); @@ -293,12 +310,22 @@ public virtual IEnumerable BuildableItems() return Enumerable.Empty(); if (!Enabled) return Enumerable.Empty(); - if (developerMode.AllTech) + if (!Info.PayUpFront && developerMode.AllTech) return Producible.Keys; - + if (Info.PayUpFront && developerMode.AllTech) + return Producible.Keys.Where(a => GetProductionCost(a) <= playerResources.GetCashAndResources() || IsInQueue(a)); + if (Info.PayUpFront) + return buildableProducibles.Where(a => GetProductionCost(a) <= playerResources.GetCashAndResources() || IsInQueue(a)); return buildableProducibles; } + public virtual bool AnyItemsToBuild() + { + return Enabled + && (productionTraits.Length <= 0 || productionTraits.Any(p => p.IsTraitDisabled)) + && ((developerMode.AllTech && Producible.Keys.Count != 0) || buildableProducibles.Any()); + } + public bool CanBuild(ActorInfo actor) { if (!Producible.TryGetValue(actor, out var ps)) @@ -352,6 +379,13 @@ protected void CancelUnbuildableItems() if (buildableNames.Contains(Queue[i].Item)) continue; + // Refund spended resources + if (Queue[i].ResourcesPaid > 0) + { + playerResources.GiveResources(Queue[i].ResourcesPaid); + Queue[i].RemainingCost += Queue[i].ResourcesPaid; + } + // Refund what's been paid so far playerResources.GiveCash(Queue[i].TotalCost - Queue[i].RemainingCost); EndProduction(Queue[i]); @@ -369,6 +403,9 @@ public bool CanQueue(ActorInfo actor, out string notificationAudio, out string n if (!developerMode.AllTech) { + if (Info.PayUpFront && actor.TraitInfo().Cost > playerResources.GetCashAndResources()) + return false; + if (Info.QueueLimit > 0 && Queue.Count >= Info.QueueLimit) { notificationAudio = Info.LimitedAudio; @@ -444,6 +481,8 @@ public void ResolveOrder(Actor self, Order order) var amountToBuild = Math.Min(fromLimit, order.ExtraData); for (var n = 0; n < amountToBuild; n++) { + if (Info.PayUpFront && cost > playerResources.GetCashAndResources()) + return; var hasPlayedSound = false; BeginProduction(new ProductionItem(this, order.TargetString, cost, playerPower, () => self.World.AddFrameEndTask(_ => { @@ -540,6 +579,12 @@ protected bool CancelProductionInner(string itemName) else { // Refund what has been paid + if (item.ResourcesPaid > 0) + { + playerResources.GiveResources(item.ResourcesPaid); + item.RemainingCost += item.ResourcesPaid; + } + playerResources.GiveCash(item.TotalCost - item.RemainingCost); EndProduction(item); } @@ -560,9 +605,19 @@ public void EndProduction(ProductionItem item) protected virtual void BeginProduction(ProductionItem item, bool hasPriority) { + if (Info.PayUpFront) + { + if (playerResources.Resources > 0 && playerResources.Resources <= item.TotalCost) + item.ResourcesPaid = playerResources.Resources; + else if (playerResources.Resources > item.TotalCost) + item.ResourcesPaid = item.TotalCost; + + playerResources.TakeCash(item.TotalCost); + item.RemainingCost = 0; + } + if (Queue.Any(i => i.Item == item.Item && i.Infinite)) return; - if (hasPriority && Queue.Count > 1) Queue.Insert(1, item); else @@ -581,6 +636,12 @@ protected virtual void BeginProduction(ProductionItem item, bool hasPriority) for (var i = 1; i < queued.Count; i++) { // Refund what has been paid + if (queued[i].ResourcesPaid > 0) + { + playerResources.GiveResources(queued[i].ResourcesPaid); + queued[i].RemainingCost += queued[i].ResourcesPaid; + } + playerResources.GiveCash(queued[i].TotalCost - queued[i].RemainingCost); EndProduction(queued[i]); } @@ -645,10 +706,10 @@ public class ProductionItem public readonly ProductionQueue Queue; public readonly int TotalCost; public readonly Action OnComplete; - public int TotalTime { get; private set; } public int RemainingTime { get; private set; } - public int RemainingCost { get; private set; } + public int RemainingCost { get; set; } + public int ResourcesPaid { get; set; } public int RemainingTimeActual => (pm == null || pm.PowerState == PowerState.Normal) ? RemainingTime : RemainingTime * Queue.Info.LowPowerModifier / 100; @@ -669,6 +730,7 @@ public ProductionItem(ProductionQueue queue, string item, int cost, PowerManager Item = item; RemainingTime = TotalTime = 1; RemainingCost = TotalCost = cost; + ResourcesPaid = 0; OnComplete = onComplete; Queue = queue; this.pm = pm; @@ -685,7 +747,6 @@ public void Tick(PlayerResources pr) var time = Queue.GetBuildTime(ai, bi); if (time > 0) RemainingTime = TotalTime = time; - Started = true; } @@ -708,12 +769,23 @@ public void Tick(PlayerResources pr) return; } - var expectedRemainingCost = RemainingTime == 1 ? 0 : TotalCost * RemainingTime / Math.Max(1, TotalTime); - var costThisFrame = RemainingCost - expectedRemainingCost; - if (costThisFrame != 0 && !pr.TakeCash(costThisFrame, true)) - return; + if (!Queue.Info.PayUpFront) + { + var expectedRemainingCost = RemainingTime == 1 ? 0 : TotalCost * RemainingTime / Math.Max(1, TotalTime); + var costThisFrame = RemainingCost - expectedRemainingCost; + if (pr.Resources > 0 && pr.Resources <= costThisFrame) + ResourcesPaid += pr.Resources; + else if (pr.Resources > costThisFrame) + ResourcesPaid += costThisFrame; + if (costThisFrame != 0 && !pr.TakeCash(costThisFrame, true)) + { + ResourcesPaid -= pr.Resources; + return; + } + + RemainingCost -= costThisFrame; + } - RemainingCost -= costThisFrame; RemainingTime--; if (RemainingTime > 0) return; diff --git a/OpenRA.Mods.Common/Traits/ProductionQueueFromSelection.cs b/OpenRA.Mods.Common/Traits/ProductionQueueFromSelection.cs index 1c86547be483..c00b85e574a0 100644 --- a/OpenRA.Mods.Common/Traits/ProductionQueueFromSelection.cs +++ b/OpenRA.Mods.Common/Traits/ProductionQueueFromSelection.cs @@ -63,7 +63,7 @@ void INotifySelection.SelectionChanged() .FirstOrDefault(q => q.Enabled && types.Contains(q.Info.Type)); } - if (queue == null || !queue.BuildableItems().Any()) + if (queue == null || !queue.AnyItemsToBuild()) return; if (tabsWidget.Value != null) diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ClassicProductionLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ClassicProductionLogic.cs index b09ce9ded800..f7538b38c6a0 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ClassicProductionLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ClassicProductionLogic.cs @@ -42,7 +42,7 @@ void SelectTab(bool reverse) palette.PickUpCompletedBuilding(); } - button.IsDisabled = () => !queues.Any(q => q.BuildableItems().Any()); + button.IsDisabled = () => !queues.Any(q => q.AnyItemsToBuild()); button.OnMouseUp = mi => SelectTab(mi.Modifiers.HasModifier(Modifiers.Shift)); button.OnKeyPress = e => SelectTab(e.Modifiers.HasModifier(Modifiers.Shift)); button.OnClick = () => SelectTab(false); diff --git a/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs b/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs index b8f3b7e5c749..ad2e8fff21dd 100644 --- a/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs @@ -338,8 +338,11 @@ bool HandleLeftClick(ProductionItem item, ProductionIcon icon, int handleCount, if (buildable != null) { - // Queue a new item + if (CurrentQueue.Info.PayUpFront && currentQueue.GetProductionCost(buildable) > CurrentQueue.Actor.Owner.PlayerActor.Trait().GetCashAndResources()) + return false; Game.Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Sounds", ClickSound, null); + + // Queue a new item var canQueue = CurrentQueue.CanQueue(buildable, out var notification, out var textNotification); if (!CurrentQueue.AllQueued().Any()) @@ -366,7 +369,7 @@ bool HandleRightClick(ProductionItem item, ProductionIcon icon, int handleCount) Game.Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Sounds", ClickSound, null); - if (CurrentQueue.Info.DisallowPaused || item.Paused || item.Done || item.TotalCost == item.RemainingCost) + if (CurrentQueue.Info.DisallowPaused || item.Paused || item.Done || item.TotalCost == item.RemainingCost || !item.Started) { // Instantly cancel items that haven't started, have finished, or if the queue doesn't support pausing Game.Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Speech", CurrentQueue.Info.CancelledAudio, World.LocalPlayer.Faction.InternalName);