From 9de1fe40a5f1f92dfc1907b8b41dfa03dea13ad5 Mon Sep 17 00:00:00 2001 From: Smittytron Date: Thu, 22 Feb 2018 11:40:38 -0600 Subject: [PATCH 01/48] Add difficulty levels to nod06a --- mods/cnc/maps/nod06a/nod06a.lua | 22 +++++++++++++++++++--- mods/cnc/maps/nod06a/rules.yaml | 2 ++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/mods/cnc/maps/nod06a/nod06a.lua b/mods/cnc/maps/nod06a/nod06a.lua index 700109e3c89b..835076fe2306 100644 --- a/mods/cnc/maps/nod06a/nod06a.lua +++ b/mods/cnc/maps/nod06a/nod06a.lua @@ -6,8 +6,20 @@ the License, or (at your option) any later version. For more information, see COPYING. ]] -NodStartUnitsRight = { 'ltnk', 'bike', 'e1', 'e1', 'e3', 'e3' } -NodStartUnitsLeft = { 'ltnk', 'ltnk', 'bggy', 'e1', 'e1', 'e1', 'e1', 'e3', 'e3', 'e3', 'e3' } +NodStartUnitsRight = +{ + tough = { 'ltnk', 'bike', 'e1', 'e1', 'e3', 'e3' }, + hard = { 'ltnk', 'bike', 'e1', 'e1', 'e3', 'e3', 'e3' }, + normal = { 'ltnk', 'bike', 'bike', 'e1', 'e1', 'e1', 'e3', 'e3', 'e3', 'e3' }, + easy = { 'ltnk', 'ltnk', 'bike', 'bike', 'e1', 'e1', 'e1', 'e1', 'e3', 'e3', 'e3', 'e3' } +} +NodStartUnitsLeft = +{ + tough = { 'ltnk', 'ltnk', 'bggy', 'e1', 'e1', 'e1', 'e3', 'e3', 'e3' }, + hard = { 'ltnk', 'ltnk', 'bggy', 'e1', 'e1', 'e1', 'e1', 'e3', 'e3', 'e3' }, + normal = { 'ltnk', 'ltnk', 'bggy', 'bggy', 'e1', 'e1', 'e1', 'e1', 'e3', 'e3', 'e3', 'e3', 'e3' }, + easy = { 'ltnk', 'ltnk', 'ltnk', 'bggy', 'e1', 'e1', 'e1', 'e1', 'e1', 'e1', 'e3', 'e3', 'e3', 'e3', 'e3' } +} Chn1Units = { 'e1', 'e1', 'e1', 'e1', 'e1' } Chn2Units = { 'e2', 'e2', 'e2', 'e2', 'e2' } Obj2Units = { 'ltnk', 'bike', 'e1', 'e1', 'e1' } @@ -120,6 +132,10 @@ MovementAndHunt = function(unit, waypoints) end InsertNodUnits = function() + local difficulty = Map.LobbyOption("difficulty") + NodStartUnitsRight = NodStartUnitsRight[difficulty] + NodStartUnitsLeft = NodStartUnitsLeft[difficulty] + Camera.Position = UnitsRallyRight.CenterPosition Media.PlaySpeechNotification(player, "Reinforce") @@ -168,7 +184,7 @@ WorldLoaded = function() OnAnyDamaged(Atk2ActorTriggerActivator, Atk2TriggerFunction) - if Map.LobbyOption("difficulty") == "hard" then + if Map.LobbyOption("difficulty") == "tough" then Trigger.OnDamaged(Atk3Activator, Atk3TriggerFunction) end diff --git a/mods/cnc/maps/nod06a/rules.yaml b/mods/cnc/maps/nod06a/rules.yaml index 303129bfda5c..06a2b1a3c553 100644 --- a/mods/cnc/maps/nod06a/rules.yaml +++ b/mods/cnc/maps/nod06a/rules.yaml @@ -13,8 +13,10 @@ World: ID: difficulty Label: Difficulty Values: + easy: Easy normal: Normal hard: Hard + tough: Real tough guy Default: normal Player: From 4376dd8c52900256cfecd052e64a06a95960f080 Mon Sep 17 00:00:00 2001 From: ltem Date: Fri, 18 Aug 2017 14:13:34 +0200 Subject: [PATCH 02/48] Add Github issue template --- .github/ISSUE_TEMPLATE.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000000..441d361dca55 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,39 @@ + + +### Issue Summary + +... ... ... + +#### System Information +- **Operating System:** [e.g. Windows 10, Mac OS 10.12, Ubuntu 16.04, ...] +- **.NET / Mono Version:** [e.g. .NET 4.7.1, Mono 4.6.2, ...] +- **OpenRA Version:** [e.g. release-20180218, playtest-20180208, ...] +- **Mod:** [e.g. Red Alert, Tiberian Dawn, Dune2000, ...] + +#### Additional Information: +- Steps to reproduce + 1. Step + 2. Step + 3. ... + +- Logs + + +- OpenRA Replays + + +- Screenshots & Videos + From 851c38012e80690c63ff70b749f75b7c743fb4e2 Mon Sep 17 00:00:00 2001 From: abcdefg30 Date: Sun, 25 Feb 2018 18:05:01 +0100 Subject: [PATCH 03/48] Set 'SecurityProtocol' to 'Tls12' for download dependencies on windows But only for dependencies downloaded from github. --- thirdparty/fetch-thirdparty-deps.ps1 | 32 +++++++++++++++------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/thirdparty/fetch-thirdparty-deps.ps1 b/thirdparty/fetch-thirdparty-deps.ps1 index beba0609e0be..50beafce6266 100644 --- a/thirdparty/fetch-thirdparty-deps.ps1 +++ b/thirdparty/fetch-thirdparty-deps.ps1 @@ -122,6 +122,23 @@ if (!(Test-Path "FuzzyLogicLibrary.dll")) rmdir FuzzyLogicLibrary -Recurse } +if (!(Test-Path "rix0rrr.BeaconLib.dll")) +{ + echo "Fetching rix0rrr.BeaconLib from NuGet." + ./nuget.exe install rix0rrr.BeaconLib -Version 1.0.1 -ExcludeVersion -Verbosity quiet + cp rix0rrr.BeaconLib/lib/net40/rix0rrr.BeaconLib.dll . + rmdir rix0rrr.BeaconLib -Recurse +} + +if (!(Test-Path "GeoLite2-Country.mmdb.gz") -Or (((get-date) - (get-item "GeoLite2-Country.mmdb.gz").LastWriteTime) -gt (new-timespan -days 30))) +{ + echo "Updating GeoIP country database from MaxMind." + $target = Join-Path $pwd.ToString() "GeoLite2-Country.mmdb.gz" + (New-Object System.Net.WebClient).DownloadFile("http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz", $target) +} + +[Net.ServicePointManager]::SecurityProtocol = 'Tls12' + if (!(Test-Path "SDL2-CS.dll")) { echo "Fetching SDL2-CS from GitHub." @@ -143,19 +160,4 @@ if (!(Test-Path "Eluant.dll")) (New-Object System.Net.WebClient).DownloadFile("https://github.com/OpenRA/Eluant/releases/download/20160124/Eluant.dll", $target) } -if (!(Test-Path "GeoLite2-Country.mmdb.gz") -Or (((get-date) - (get-item "GeoLite2-Country.mmdb.gz").LastWriteTime) -gt (new-timespan -days 30))) -{ - echo "Updating GeoIP country database from MaxMind." - $target = Join-Path $pwd.ToString() "GeoLite2-Country.mmdb.gz" - (New-Object System.Net.WebClient).DownloadFile("http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz", $target) -} - -if (!(Test-Path "rix0rrr.BeaconLib.dll")) -{ - echo "Fetching rix0rrr.BeaconLib from NuGet." - ./nuget.exe install rix0rrr.BeaconLib -Version 1.0.1 -ExcludeVersion -Verbosity quiet - cp rix0rrr.BeaconLib/lib/net40/rix0rrr.BeaconLib.dll . - rmdir rix0rrr.BeaconLib -Recurse -} - cd .. From c5f9d6ff2d64889e6ace69317f39226d42059acd Mon Sep 17 00:00:00 2001 From: reaperrr Date: Thu, 28 Dec 2017 08:53:44 +0100 Subject: [PATCH 04/48] Add more configurability to AI MinimumExcessPower logic Allows to scale the targeted minimum excess with building count as well as define a maximum cap to avoid overproducing powerplants. --- OpenRA.Mods.Common/AI/BaseBuilder.cs | 10 +++++++--- OpenRA.Mods.Common/AI/HackyAI.cs | 11 ++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/OpenRA.Mods.Common/AI/BaseBuilder.cs b/OpenRA.Mods.Common/AI/BaseBuilder.cs index 84bac838d36f..aa17421c22c7 100644 --- a/OpenRA.Mods.Common/AI/BaseBuilder.cs +++ b/OpenRA.Mods.Common/AI/BaseBuilder.cs @@ -35,6 +35,7 @@ class BaseBuilder int checkForBasesTicks; int cachedBases; int cachedBuildings; + int minimumExcessPower; enum Water { @@ -54,6 +55,7 @@ public BaseBuilder(HackyAI ai, string category, Player p, PowerManager pm, Playe playerResources = pr; this.category = category; failRetryTicks = ai.Info.StructureProductionResumeDelay; + minimumExcessPower = ai.Info.MinimumExcessPower; } public void Tick() @@ -100,6 +102,8 @@ public void Tick() return; playerBuildings = world.ActorsHavingTrait().Where(a => a.Owner == player).ToArray(); + var excessPowerBonus = ai.Info.ExcessPowerIncrement * (playerBuildings.Count() / ai.Info.ExcessPowerIncreaseThreshold.Clamp(1, int.MaxValue)); + minimumExcessPower = (ai.Info.MinimumExcessPower + excessPowerBonus).Clamp(ai.Info.MinimumExcessPower, ai.Info.MaximumExcessPower); var active = false; foreach (var queue in ai.FindQueues(category)) @@ -197,7 +201,7 @@ ActorInfo GetProducibleBuilding(HashSet actors, IEnumerable b bool HasSufficientPowerForActor(ActorInfo actorInfo) { return (actorInfo.TraitInfos().Where(i => i.EnabledByDefault) - .Sum(p => p.Amount) + playerPower.ExcessPower) >= ai.Info.MinimumExcessPower; + .Sum(p => p.Amount) + playerPower.ExcessPower) >= minimumExcessPower; } ActorInfo ChooseBuildingToBuild(ProductionQueue queue) @@ -209,7 +213,7 @@ ActorInfo ChooseBuildingToBuild(ProductionQueue queue) a => a.TraitInfos().Where(i => i.EnabledByDefault).Sum(p => p.Amount)); // First priority is to get out of a low power situation - if (playerPower.ExcessPower < ai.Info.MinimumExcessPower) + if (playerPower.ExcessPower < minimumExcessPower) { if (power != null && power.TraitInfos().Where(i => i.EnabledByDefault).Sum(p => p.Amount) > 0) { @@ -314,7 +318,7 @@ ActorInfo ChooseBuildingToBuild(ProductionQueue queue) // Will this put us into low power? var actor = world.Map.Rules.Actors[name]; - if (playerPower.ExcessPower < ai.Info.MinimumExcessPower || !HasSufficientPowerForActor(actor)) + if (playerPower.ExcessPower < minimumExcessPower || !HasSufficientPowerForActor(actor)) { // Try building a power plant instead if (power != null && power.TraitInfos().Where(i => i.EnabledByDefault).Sum(pi => pi.Amount) > 0) diff --git a/OpenRA.Mods.Common/AI/HackyAI.cs b/OpenRA.Mods.Common/AI/HackyAI.cs index 649441977d98..136be68b1f1b 100644 --- a/OpenRA.Mods.Common/AI/HackyAI.cs +++ b/OpenRA.Mods.Common/AI/HackyAI.cs @@ -96,6 +96,15 @@ public class BuildingCategories [Desc("Minimum excess power the AI should try to maintain.")] public readonly int MinimumExcessPower = 0; + [Desc("Increase maintained excess power by this amount for every ExcessPowerIncreaseThreshold of base buildings.")] + public readonly int ExcessPowerIncrement = 0; + + [Desc("Increase maintained excess power by ExcessPowerIncrement for every N base buildings.")] + public readonly int ExcessPowerIncreaseThreshold = 1; + + [Desc("The targeted excess power the AI tries to maintain cannot rise above this.")] + public readonly int MaximumExcessPower = 0; + [Desc("Additional delay (in ticks) between structure production checks when there is no active production.", "StructureProductionRandomBonusDelay is added to this.")] public readonly int StructureProductionInactiveDelay = 125; @@ -193,7 +202,7 @@ public class BuildingCategories public readonly UnitCategories UnitsCommonNames; [Desc("Tells the AI what building types fall under the same common name.", - "Possible keys are ConstructionYard, Power, Refinery, Silo , Barracks, Production, VehiclesFactory, NavalProduction.")] + "Possible keys are ConstructionYard, Power, Refinery, Silo, Barracks, Production, VehiclesFactory, NavalProduction.")] [FieldLoader.LoadUsing("LoadBuildingCategories", true)] public readonly BuildingCategories BuildingCommonNames; From de47d570d578a7517b297f38514f2867157ee467 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Thu, 28 Dec 2017 08:55:59 +0100 Subject: [PATCH 05/48] Prevent RA AIs from building too many (advanced) power plants While scaling minimum excess power with base size to make sure they don't build too few, either. --- mods/ra/rules/ai.yaml | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/mods/ra/rules/ai.yaml b/mods/ra/rules/ai.yaml index d936aa3abfe2..52f58786fa90 100644 --- a/mods/ra/rules/ai.yaml +++ b/mods/ra/rules/ai.yaml @@ -2,7 +2,10 @@ Player: HackyAI@RushAI: Name: Rush AI Type: rush - MinimumExcessPower: 40 + MinimumExcessPower: 20 + ExcessPowerIncrement: 40 + ExcessPowerIncreaseThreshold: 4 + MaximumExcessPower: 160 BuildingCommonNames: ConstructionYard: fact Refinery: proc @@ -26,8 +29,6 @@ Player: fix: 1 BuildingFractions: proc: 30% - powr: 15% - apwr: 20% barr: 1% kenn: 0.5% tent: 1% @@ -117,7 +118,10 @@ Player: HackyAI@NormalAI: Name: Normal AI Type: normal - MinimumExcessPower: 60 + MinimumExcessPower: 40 + ExcessPowerIncrement: 40 + ExcessPowerIncreaseThreshold: 4 + MaximumExcessPower: 200 BuildingCommonNames: ConstructionYard: fact Refinery: proc @@ -145,8 +149,6 @@ Player: fix: 1 BuildingFractions: proc: 10% - powr: 1% - apwr: 30% tent: 1% barr: 1% kenn: 0.5% @@ -250,7 +252,10 @@ Player: HackyAI@TurtleAI: Name: Turtle AI Type: turtle - MinimumExcessPower: 100 + MinimumExcessPower: 50 + ExcessPowerIncrement: 50 + ExcessPowerIncreaseThreshold: 4 + MaximumExcessPower: 250 BuildingCommonNames: ConstructionYard: fact Refinery: proc @@ -279,8 +284,6 @@ Player: fix: 1 BuildingFractions: proc: 30% - powr: 1% - apwr: 20% tent: 1% barr: 1% kenn: 0.5% @@ -383,6 +386,10 @@ Player: HackyAI@NavalAI: Name: Naval AI Type: naval + MinimumExcessPower: 20 + ExcessPowerIncrement: 40 + ExcessPowerIncreaseThreshold: 4 + MaximumExcessPower: 200 BuildingCommonNames: ConstructionYard: fact Refinery: proc @@ -410,8 +417,6 @@ Player: fix: 1 BuildingFractions: proc: 29% - powr: 1% - apwr: 24% dome: 1% weap: 1% hpad: 20% From 7c79d2008342b50b5c3d1dd17853f2c2ebefc067 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Thu, 28 Dec 2017 09:08:50 +0100 Subject: [PATCH 06/48] Prevent TD AIs from building too many (advanced) power plants While scaling minimum excess power with base size to make sure they don't build too few, either. --- mods/cnc/rules/ai.yaml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/mods/cnc/rules/ai.yaml b/mods/cnc/rules/ai.yaml index 76e790f6e970..d08e795775ad 100644 --- a/mods/cnc/rules/ai.yaml +++ b/mods/cnc/rules/ai.yaml @@ -2,7 +2,10 @@ Player: HackyAI@Cabal: Name: Cabal Type: cabal - MinimumExcessPower: 60 + MinimumExcessPower: 30 + ExcessPowerIncrement: 30 + ExcessPowerIncreaseThreshold: 5 + MaximumExcessPower: 150 BuildingCommonNames: ConstructionYard: fact Refinery: proc @@ -30,11 +33,9 @@ Player: silo: 1 BuildingFractions: proc: 20% - nuke: 9% pyle: 5% hand: 5% hq: 4% - nuk2: 9% weap: 9% afld: 9% gtwr: 5% @@ -130,7 +131,10 @@ Player: HackyAI@Watson: Name: Watson Type: watson - MinimumExcessPower: 60 + MinimumExcessPower: 30 + ExcessPowerIncrement: 30 + ExcessPowerIncreaseThreshold: 4 + MaximumExcessPower: 150 BuildingCommonNames: ConstructionYard: fact Refinery: proc @@ -158,11 +162,9 @@ Player: silo: 1 BuildingFractions: proc: 17% - nuke: 1% pyle: 2% hand: 2% hq: 1% - nuk2: 18% weap: 5% afld: 5% hpad: 4% @@ -258,7 +260,10 @@ Player: HackyAI@HAL9001: Name: HAL 9001 Type: hal9001 - MinimumExcessPower: 60 + MinimumExcessPower: 30 + ExcessPowerIncrement: 30 + ExcessPowerIncreaseThreshold: 4 + MaximumExcessPower: 210 BuildingCommonNames: ConstructionYard: fact Refinery: proc @@ -286,11 +291,9 @@ Player: silo: 1 BuildingFractions: proc: 17% - nuke: 10% pyle: 7% hand: 9% hq: 1% - nuk2: 18% weap: 8% afld: 6% hpad: 4% From 342114878c0ec8f2fc5ff2f57304c4b0c1ced81e Mon Sep 17 00:00:00 2001 From: reaperrr Date: Thu, 28 Dec 2017 09:09:23 +0100 Subject: [PATCH 07/48] Prevent D2k AIs from building too many wind traps While scaling minimum excess power with base size to make sure they don't build too few, either. --- mods/d2k/rules/ai.yaml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/mods/d2k/rules/ai.yaml b/mods/d2k/rules/ai.yaml index 2cd3d8ca107a..73321cffd377 100644 --- a/mods/d2k/rules/ai.yaml +++ b/mods/d2k/rules/ai.yaml @@ -2,7 +2,10 @@ Player: HackyAI@Omnius: Name: Omnius Type: omnius - MinimumExcessPower: 60 + MinimumExcessPower: 50 + ExcessPowerIncrement: 50 + ExcessPowerIncreaseThreshold: 4 + MaximumExcessPower: 200 BuildingQueues: Building, Upgrade UnitQueues: Infantry, Vehicle, Armor, Starport, Aircraft BuildingCommonNames: @@ -30,7 +33,6 @@ Player: upgrade.heavy: 1 upgrade.hightech: 1 BuildingFractions: - wind_trap: 10% barracks: 0.1% refinery: 20.1% medium_gun_turret: 8% @@ -121,7 +123,10 @@ Player: HackyAI@Vidius: Name: Vidious Type: vidious - MinimumExcessPower: 60 + MinimumExcessPower: 50 + ExcessPowerIncrement: 50 + ExcessPowerIncreaseThreshold: 4 + MaximumExcessPower: 200 BuildingQueues: Building, Upgrade UnitQueues: Infantry, Vehicle, Armor, Starport, Aircraft BuildingCommonNames: @@ -150,7 +155,6 @@ Player: upgrade.heavy: 1 upgrade.hightech: 1 BuildingFractions: - wind_trap: 12% barracks: 0.1% refinery: 20.1% medium_gun_turret: 5% @@ -242,7 +246,10 @@ Player: HackyAI@Gladius: Name: Gladius Type: gladius - MinimumExcessPower: 60 + MinimumExcessPower: 50 + ExcessPowerIncrement: 50 + ExcessPowerIncreaseThreshold: 4 + MaximumExcessPower: 200 BuildingQueues: Building, Upgrade UnitQueues: Infantry, Vehicle, Armor, Starport, Aircraft BuildingCommonNames: @@ -271,7 +278,6 @@ Player: upgrade.heavy: 1 upgrade.hightech: 1 BuildingFractions: - wind_trap: 10% barracks: 0.1% refinery: 20.1% medium_gun_turret: 4% From 700c46d6af90c46b2cdf76a95f3edb246dd6038e Mon Sep 17 00:00:00 2001 From: reaperrr Date: Thu, 28 Dec 2017 09:09:42 +0100 Subject: [PATCH 08/48] Prevent TS AI from building too many (advanced) power plants While scaling minimum excess power with base size to make sure they don't build too few, either. --- mods/ts/rules/ai.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mods/ts/rules/ai.yaml b/mods/ts/rules/ai.yaml index 9840a768feef..e5d824b208b7 100644 --- a/mods/ts/rules/ai.yaml +++ b/mods/ts/rules/ai.yaml @@ -2,7 +2,10 @@ Player: HackyAI@TestAI: Name: Test AI Type: test - MinimumExcessPower: 60 + MinimumExcessPower: 30 + ExcessPowerIncrement: 30 + ExcessPowerIncreaseThreshold: 4 + MaximumExcessPower: 200 BuildingCommonNames: ConstructionYard: gacnst Refinery: proc @@ -35,8 +38,6 @@ Player: nasam: 4 BuildingFractions: proc: 30% - gapowr: 35% - napowr: 35% gapile: 1% nahand: 1% gaweap: 1% From 701675fd4c8327eda024fbee4500191647b718a3 Mon Sep 17 00:00:00 2001 From: Peter Antal <172160+PeterAntal@users.noreply.github.com> Date: Mon, 26 Feb 2018 23:17:47 -0800 Subject: [PATCH 09/48] Add "Slowest" gamespeed with Timestep of 80. Drop "Slowest" latency to 2 frames, to balance out with the particularly long timestep. --- mods/cnc/mod.yaml | 4 ++++ mods/d2k/mod.yaml | 4 ++++ mods/ra/mod.yaml | 4 ++++ mods/ts/mod.yaml | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index a98541388a0d..263b23f8e0e5 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -209,6 +209,10 @@ SpriteSequenceFormat: TilesetSpecificSpriteSequence ModelSequenceFormat: PlaceholderModelSequence GameSpeeds: + slowest: + Name: Slowest + Timestep: 80 + OrderLatency: 2 slower: Name: Slower Timestep: 50 diff --git a/mods/d2k/mod.yaml b/mods/d2k/mod.yaml index fb8f9faa8328..4da711add31b 100644 --- a/mods/d2k/mod.yaml +++ b/mods/d2k/mod.yaml @@ -186,6 +186,10 @@ SpriteSequenceFormat: DefaultSpriteSequence ModelSequenceFormat: PlaceholderModelSequence GameSpeeds: + slowest: + Name: Slowest + Timestep: 80 + OrderLatency: 2 slower: Name: Slower Timestep: 50 diff --git a/mods/ra/mod.yaml b/mods/ra/mod.yaml index 0f5db0112a45..69f65e228481 100644 --- a/mods/ra/mod.yaml +++ b/mods/ra/mod.yaml @@ -211,6 +211,10 @@ SpriteSequenceFormat: TilesetSpecificSpriteSequence ModelSequenceFormat: PlaceholderModelSequence GameSpeeds: + slowest: + Name: Slowest + Timestep: 80 + OrderLatency: 2 slower: Name: Slower Timestep: 50 diff --git a/mods/ts/mod.yaml b/mods/ts/mod.yaml index 38ed1405b400..d5479468f7a9 100644 --- a/mods/ts/mod.yaml +++ b/mods/ts/mod.yaml @@ -247,6 +247,10 @@ SpriteSequenceFormat: TilesetSpecificSpriteSequence ModelSequenceFormat: VoxelModelSequence GameSpeeds: + slowest: + Name: Slowest + Timestep: 80 + OrderLatency: 2 slower: Name: Slower Timestep: 50 From 9ce0bcb0b7d39a9d748e5f22e59b548e1ba5b835 Mon Sep 17 00:00:00 2001 From: Peter Antal <172160+PeterAntal@users.noreply.github.com> Date: Sun, 25 Feb 2018 12:41:54 -0800 Subject: [PATCH 10/48] Mop up active Stylecop nits. Style consistency cleanup. --- OpenRA.Game/Map/MapGrid.cs | 8 ++++++-- OpenRA.Game/Server/Connection.cs | 2 +- OpenRA.Mods.Cnc/FileFormats/BlowfishKeyProvider.cs | 2 +- OpenRA.Mods.Common/FileFormats/AudReader.cs | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/OpenRA.Game/Map/MapGrid.cs b/OpenRA.Game/Map/MapGrid.cs index db35f7cd3081..a48cddc0ef12 100644 --- a/OpenRA.Game/Map/MapGrid.cs +++ b/OpenRA.Game/Map/MapGrid.cs @@ -89,8 +89,12 @@ public MapGrid(MiniYaml yaml) var defaultSubCellIndex = (byte)DefaultSubCell; if (defaultSubCellIndex == byte.MaxValue) DefaultSubCell = (SubCell)(SubCellOffsets.Length / 2); - else if (defaultSubCellIndex < (SubCellOffsets.Length > 1 ? 1 : 0) || defaultSubCellIndex >= SubCellOffsets.Length) - 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"); + else + { + var minSubCellOffset = SubCellOffsets.Length > 1 ? 1 : 0; + if (defaultSubCellIndex < minSubCellOffset || defaultSubCellIndex >= SubCellOffsets.Length) + 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)IsometricCellCorners : RectangularCellCorners; diff --git a/OpenRA.Game/Server/Connection.cs b/OpenRA.Game/Server/Connection.cs index add566b67199..ba60fecaa250 100644 --- a/OpenRA.Game/Server/Connection.cs +++ b/OpenRA.Game/Server/Connection.cs @@ -55,7 +55,7 @@ bool ReadDataInner(Server server) // from `socket.Receive(rx)`. if (!Socket.Poll(0, SelectMode.SelectRead)) break; - if (0 < (len = Socket.Receive(rx))) + if ((len = Socket.Receive(rx)) > 0) Data.AddRange(rx.Take(len)); else { diff --git a/OpenRA.Mods.Cnc/FileFormats/BlowfishKeyProvider.cs b/OpenRA.Mods.Cnc/FileFormats/BlowfishKeyProvider.cs index ad484f2e81ec..8faae3ea2f50 100644 --- a/OpenRA.Mods.Cnc/FileFormats/BlowfishKeyProvider.cs +++ b/OpenRA.Mods.Cnc/FileFormats/BlowfishKeyProvider.cs @@ -385,7 +385,7 @@ void CalcBigNum(uint[] n1, uint[] n2, uint[] n3, uint len) MulBignumWord(esi, globOne, tmp, 2 * len); if ((*edi & 0x8000) == 0) { - if (0 != SubBigNum((uint*)esi, (uint*)esi, g1, 0, (int)len)) + if (SubBigNum((uint*)esi, (uint*)esi, g1, 0, (int)len) != 0) (*edi)--; } } diff --git a/OpenRA.Mods.Common/FileFormats/AudReader.cs b/OpenRA.Mods.Common/FileFormats/AudReader.cs index 06cca2ee0789..8c7d2ed650d3 100644 --- a/OpenRA.Mods.Common/FileFormats/AudReader.cs +++ b/OpenRA.Mods.Common/FileFormats/AudReader.cs @@ -116,8 +116,8 @@ public static float SoundLength(Stream s) var flags = (SoundFlags)s.ReadByte(); var samples = outputSize; - if (0 != (flags & SoundFlags.Stereo)) samples /= 2; - if (0 != (flags & SoundFlags._16Bit)) samples /= 2; + if ((flags & SoundFlags.Stereo) != 0) samples /= 2; + if ((flags & SoundFlags._16Bit) != 0) samples /= 2; return (float)samples / sampleRate; } From 42f1db0d4b84f99bb7262af59607b55e2f180684 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 1 Mar 2018 19:49:55 +0000 Subject: [PATCH 11/48] Rename Stream.Write(byte[]) extension method to fix compatibility with newer mono. --- OpenRA.Game/Network/Connection.cs | 24 ++++++++++++------------ OpenRA.Game/Network/OrderIO.cs | 4 +++- OpenRA.Game/Network/ReplayConnection.cs | 4 ++-- OpenRA.Game/Network/ReplayRecorder.cs | 2 +- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/OpenRA.Game/Network/Connection.cs b/OpenRA.Game/Network/Connection.cs index c8782f4f1415..1e423552733e 100644 --- a/OpenRA.Game/Network/Connection.cs +++ b/OpenRA.Game/Network/Connection.cs @@ -60,26 +60,26 @@ public virtual ConnectionState ConnectionState public virtual void Send(int frame, List orders) { var ms = new MemoryStream(); - ms.Write(BitConverter.GetBytes(frame)); + ms.WriteArray(BitConverter.GetBytes(frame)); foreach (var o in orders) - ms.Write(o); + ms.WriteArray(o); Send(ms.ToArray()); } public virtual void SendImmediate(List orders) { var ms = new MemoryStream(); - ms.Write(BitConverter.GetBytes(0)); + ms.WriteArray(BitConverter.GetBytes(0)); foreach (var o in orders) - ms.Write(o); + ms.WriteArray(o); Send(ms.ToArray()); } public virtual void SendSync(int frame, byte[] syncData) { var ms = new MemoryStream(4 + syncData.Length); - ms.Write(BitConverter.GetBytes(frame)); - ms.Write(syncData); + ms.WriteArray(BitConverter.GetBytes(frame)); + ms.WriteArray(syncData); Send(ms.GetBuffer()); } @@ -198,8 +198,8 @@ void NetworkConnectionReceive(object networkStreamObject) public override void SendSync(int frame, byte[] syncData) { var ms = new MemoryStream(4 + syncData.Length); - ms.Write(BitConverter.GetBytes(frame)); - ms.Write(syncData); + ms.WriteArray(BitConverter.GetBytes(frame)); + ms.WriteArray(syncData); queuedSyncPackets.Add(ms.GetBuffer()); } @@ -210,13 +210,13 @@ protected override void Send(byte[] packet) try { var ms = new MemoryStream(); - ms.Write(BitConverter.GetBytes(packet.Length)); - ms.Write(packet); + ms.WriteArray(BitConverter.GetBytes(packet.Length)); + ms.WriteArray(packet); foreach (var q in queuedSyncPackets) { - ms.Write(BitConverter.GetBytes(q.Length)); - ms.Write(q); + ms.WriteArray(BitConverter.GetBytes(q.Length)); + ms.WriteArray(q); base.Send(q); } diff --git a/OpenRA.Game/Network/OrderIO.cs b/OpenRA.Game/Network/OrderIO.cs index 6fbbe7339a6e..2e5dc6105af1 100644 --- a/OpenRA.Game/Network/OrderIO.cs +++ b/OpenRA.Game/Network/OrderIO.cs @@ -16,7 +16,9 @@ namespace OpenRA.Network { public static class OrderIO { - public static void Write(this Stream s, byte[] buf) + // Note: renamed from Write() to avoid being aliased by + // System.IO.Stream.Write(System.ReadOnlySpan) (which is not implemented in Mono) + public static void WriteArray(this Stream s, byte[] buf) { s.Write(buf, 0, buf.Length); } diff --git a/OpenRA.Game/Network/ReplayConnection.cs b/OpenRA.Game/Network/ReplayConnection.cs index 57d22459bab0..82463abb2422 100644 --- a/OpenRA.Game/Network/ReplayConnection.cs +++ b/OpenRA.Game/Network/ReplayConnection.cs @@ -128,8 +128,8 @@ public ReplayConnection(string replayFilename) public void SendSync(int frame, byte[] syncData) { var ms = new MemoryStream(4 + syncData.Length); - ms.Write(BitConverter.GetBytes(frame)); - ms.Write(syncData); + ms.WriteArray(BitConverter.GetBytes(frame)); + ms.WriteArray(syncData); sync.Add(ms.GetBuffer()); // Store the current frame so Receive() can return the next chunk of orders. diff --git a/OpenRA.Game/Network/ReplayRecorder.cs b/OpenRA.Game/Network/ReplayRecorder.cs index bbe66195e82a..50741388c5e9 100644 --- a/OpenRA.Game/Network/ReplayRecorder.cs +++ b/OpenRA.Game/Network/ReplayRecorder.cs @@ -63,7 +63,7 @@ void StartSavingReplay(byte[] initialContent) catch (IOException) { } } - file.Write(initialContent); + file.WriteArray(initialContent); writer = new BinaryWriter(file); } From f9a45130fa9499ce8b952e7805f0ff89440f91ad Mon Sep 17 00:00:00 2001 From: Smittytron Date: Thu, 22 Feb 2018 17:12:41 -0600 Subject: [PATCH 12/48] Add Bridge to TerrainType --- mods/cnc/rules/defaults.yaml | 3 +++ mods/cnc/tilesets/desert.yaml | 13 +++++++++---- mods/cnc/tilesets/jungle.yaml | 13 +++++++++---- mods/cnc/tilesets/snow.yaml | 13 +++++++++---- mods/cnc/tilesets/temperat.yaml | 13 +++++++++---- mods/cnc/tilesets/winter.yaml | 13 +++++++++---- 6 files changed, 48 insertions(+), 20 deletions(-) diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index 10d46db81ce7..0500ca6b8c10 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -217,6 +217,7 @@ Clear: 80 Rough: 50 Road: 100 + Bridge: 100 Tiberium: 50 BlueTiberium: 50 Beach: 50 @@ -260,6 +261,7 @@ Clear: 80 Rough: 70 Road: 100 + Bridge: 100 Tiberium: 70 BlueTiberium: 70 Beach: 70 @@ -338,6 +340,7 @@ Clear: 90 Rough: 80 Road: 100 + Bridge: 100 Tiberium: 70 PathingCost: 300 BlueTiberium: 70 diff --git a/mods/cnc/tilesets/desert.yaml b/mods/cnc/tilesets/desert.yaml index ee94d8578976..1470d0ae92e8 100644 --- a/mods/cnc/tilesets/desert.yaml +++ b/mods/cnc/tilesets/desert.yaml @@ -16,6 +16,11 @@ Terrain: AcceptsSmudgeType: Crater, Scorch Color: 54FCFC RestrictPlayerColor: true + TerrainType@Bridge: + Type: Bridge + TargetTypes: Ground + AcceptsSmudgeType: Crater, Scorch + Color: 606060 TerrainType@Clear: Type: Clear TargetTypes: Ground @@ -1601,12 +1606,12 @@ Templates: 6: Clear 7: Rock 8: Wall - 9: Road + 9: Bridge 10: Wall 11: Clear 12: River 13: River - 14: Road + 14: Bridge 15: Wall 16: Rock 17: Rock @@ -1659,14 +1664,14 @@ Templates: 5: Clear 6: Clear 7: Wall - 8: Road + 8: Bridge 9: Wall 10: River 11: River 12: Rock 13: River 14: Wall - 15: Road + 15: Bridge 16: Wall 17: Rock 18: River diff --git a/mods/cnc/tilesets/jungle.yaml b/mods/cnc/tilesets/jungle.yaml index 2235b24c923c..ab2ad6c86955 100644 --- a/mods/cnc/tilesets/jungle.yaml +++ b/mods/cnc/tilesets/jungle.yaml @@ -16,6 +16,11 @@ Terrain: AcceptsSmudgeType: Crater, Scorch Color: 54FCFC RestrictPlayerColor: true + TerrainType@Bridge: + Type: Bridge + TargetTypes: Ground, Bridge + AcceptsSmudgeType: Crater, Scorch + Color: 606060 TerrainType@Clear: Type: Clear TargetTypes: Ground @@ -1624,10 +1629,10 @@ Templates: 3: Road 4: River 5: Wall - 6: Road + 6: Bridge 7: Wall 8: River - 9: Road + 9: Bridge 10: Wall 11: River 12: Road @@ -1665,13 +1670,13 @@ Templates: 2: Clear 4: Clear 5: Wall - 6: Road + 6: Bridge 7: Wall 8: River 9: River 10: River 11: Wall - 12: Road + 12: Bridge 13: Wall 14: Clear 15: River diff --git a/mods/cnc/tilesets/snow.yaml b/mods/cnc/tilesets/snow.yaml index 138e8ddf1120..b0edb351e4f3 100644 --- a/mods/cnc/tilesets/snow.yaml +++ b/mods/cnc/tilesets/snow.yaml @@ -16,6 +16,11 @@ Terrain: AcceptsSmudgeType: Crater, Scorch Color: 54FCFC RestrictPlayerColor: true + TerrainType@Bridge: + Type: Bridge + TargetTypes: Ground + AcceptsSmudgeType: Crater, Scorch + Color: 606060 TerrainType@Clear: Type: Clear TargetTypes: Ground @@ -1636,10 +1641,10 @@ Templates: 3: Road 4: River 5: Wall - 6: Road + 6: Bridge 7: Wall 8: River - 9: Road + 9: Bridge 10: Wall 11: River 12: Road @@ -1677,13 +1682,13 @@ Templates: 2: Clear 4: Clear 5: Wall - 6: Road + 6: Bridge 7: Wall 8: River 9: River 10: River 11: Wall - 12: Road + 12: Bridge 13: Wall 14: Clear 15: River diff --git a/mods/cnc/tilesets/temperat.yaml b/mods/cnc/tilesets/temperat.yaml index ce3167efcd02..afb0a93edc83 100644 --- a/mods/cnc/tilesets/temperat.yaml +++ b/mods/cnc/tilesets/temperat.yaml @@ -16,6 +16,11 @@ Terrain: AcceptsSmudgeType: Crater, Scorch Color: 54FCFC RestrictPlayerColor: true + TerrainType@Bridge: + Type: Bridge + TargetTypes: Ground, Bridge + AcceptsSmudgeType: Crater, Scorch + Color: 606060 TerrainType@Clear: Type: Clear TargetTypes: Ground @@ -1624,10 +1629,10 @@ Templates: 3: Road 4: River 5: Wall - 6: Road + 6: Bridge 7: Wall 8: River - 9: Road + 9: Bridge 10: Wall 11: River 12: Road @@ -1665,13 +1670,13 @@ Templates: 2: Clear 4: Clear 5: Wall - 6: Road + 6: Bridge 7: Wall 8: River 9: River 10: River 11: Wall - 12: Road + 12: Bridge 13: Wall 14: Clear 15: River diff --git a/mods/cnc/tilesets/winter.yaml b/mods/cnc/tilesets/winter.yaml index 23031abdf74d..1d53e74f4890 100644 --- a/mods/cnc/tilesets/winter.yaml +++ b/mods/cnc/tilesets/winter.yaml @@ -16,6 +16,11 @@ Terrain: AcceptsSmudgeType: Crater, Scorch Color: 54FCFC RestrictPlayerColor: true + TerrainType@Bridge: + Type: Bridge + TargetTypes: Ground, Bridge + AcceptsSmudgeType: Crater, Scorch + Color: 606060 TerrainType@Clear: Type: Clear TargetTypes: Ground @@ -1608,10 +1613,10 @@ Templates: 3: Road 4: River 5: Wall - 6: Road + 6: Bridge 7: Wall 8: River - 9: Road + 9: Bridge 10: Wall 11: River 12: Road @@ -1649,13 +1654,13 @@ Templates: 2: Clear 4: Clear 5: Wall - 6: Road + 6: Bridge 7: Wall 8: River 9: River 10: River 11: Wall - 12: Road + 12: Bridge 13: Wall 14: Clear 15: River From 1d9ed31f7dbeb83e47d928269349a8f95b1e2074 Mon Sep 17 00:00:00 2001 From: Smittytron Date: Sun, 25 Feb 2018 09:35:51 -0600 Subject: [PATCH 13/48] Add secondary objective and difficulty levels to nod06b --- mods/cnc/maps/nod06b/map.yaml | 46 ++++++++++++++---------- mods/cnc/maps/nod06b/nod06b.lua | 62 ++++++++++++++++++++++++++------- mods/cnc/maps/nod06b/rules.yaml | 9 +++++ 3 files changed, 87 insertions(+), 30 deletions(-) diff --git a/mods/cnc/maps/nod06b/map.yaml b/mods/cnc/maps/nod06b/map.yaml index d1ae79f8175e..5369b5f9d364 100644 --- a/mods/cnc/maps/nod06b/map.yaml +++ b/mods/cnc/maps/nod06b/map.yaml @@ -21,7 +21,6 @@ LockPreview: True Players: PlayerReference@GDI: Name: GDI - Playable: False Faction: gdi Color: F5D378 Enemies: Nod @@ -37,8 +36,8 @@ Players: Enemies: Nod PlayerReference@Nod: Name: Nod - Playable: True AllowBots: False + Playable: True Required: True LockFaction: True Faction: nod @@ -304,52 +303,52 @@ Actors: Actor85: nuke Location: 44,18 Owner: GDI - Actor86: v35 + Obj2Actor1: v35 Location: 36,57 Owner: Civilians - Actor87: v34 + Obj2Actor2: v34 Location: 34,56 Owner: Civilians - Actor88: v33 + Obj2Actor3: v33 Location: 32,56 Owner: Civilians - Actor89: v30 + Obj2Actor4: v30 Location: 36,51 Owner: Civilians - Actor90: v30 + Obj2Actor5: v30 Location: 30,52 Owner: Civilians - Actor91: v29 + Obj2Actor6: v29 Location: 36,52 Owner: Civilians - Actor92: v27 + Obj2Actor7: v27 Location: 24,53 Owner: Civilians - Actor93: v27 + Obj2Actor8: v27 Location: 25,53 Owner: Civilians - Actor94: v27 + Obj2Actor9: v27 Location: 26,53 Owner: Civilians - Actor95: v26 + Obj2Actor10: v26 Location: 31,53 Owner: Civilians - Actor96: v25 + Obj2Actor0: v25 Location: 30,50 Owner: Civilians - Actor97: v24 + Obj2Actor11: v24 Location: 29,52 Owner: Civilians Actor98: v23 Location: 32,57 Owner: Civilians - Actor99: v22 + Obj2Actor12: v22 Location: 35,53 Owner: Civilians - Actor100: v21 + Obj2Actor13: v21 Location: 25,57 Owner: Civilians - Actor101: v20 + Obj2Actor14: v20 Location: 26,54 Owner: Civilians Actor104: silo @@ -560,7 +559,7 @@ Actors: Location: 26,61 Owner: Neutral Detonator: CRATE.plain - Location: 47, 27 + Location: 47,27 Owner: GDI Chn1Actor1: fact Location: 48,18 @@ -585,5 +584,16 @@ Actors: Location: 17,36 Owner: GDI SubCell: 1 + waypoint13: waypoint + Owner: Neutral + Location: 43,58 + Obj2UnitsEntry: waypoint + Owner: Neutral + Location: 45,61 + Actor169: mtnk + Owner: GDI + Location: 33,54 + Facing: 92 + TurretFacing: 92 Rules: cnc|rules/campaign-maprules.yaml, cnc|rules/campaign-tooltips.yaml, cnc|rules/campaign-palettes.yaml, rules.yaml diff --git a/mods/cnc/maps/nod06b/nod06b.lua b/mods/cnc/maps/nod06b/nod06b.lua index d98b5352c6c3..a52df489302f 100644 --- a/mods/cnc/maps/nod06b/nod06b.lua +++ b/mods/cnc/maps/nod06b/nod06b.lua @@ -6,11 +6,36 @@ the License, or (at your option) any later version. For more information, see COPYING. ]] -NodUnitsVehicle1 = { 'bggy', 'bggy', 'bike', 'bike', 'bike' } -NodUnitsVehicle2 = { 'ltnk', 'ltnk', 'ltnk' } -NodUnitsGunner = { 'e1', 'e1', 'e1', 'e1', 'e1', 'e1' } -NodUnitsRocket = { 'e3', 'e3', 'e3', 'e3', 'e3', 'e3' } +NodUnitsVehicle1 = +{ + tough = { 'bggy', 'bike', 'bike' }, + hard = { 'bggy', 'bggy', 'bike', 'bike' }, + normal = { 'bggy', 'bggy', 'bike', 'bike', 'bike' }, + easy = { 'bggy', 'bggy', 'bggy', 'bike', 'bike', 'bike', 'bike' } +} +NodUnitsVehicle2 = +{ + tough = { 'ltnk', 'ltnk' }, + hard = { 'ltnk', 'ltnk', 'ltnk' }, + normal = { 'ltnk', 'ltnk', 'ltnk', 'ltnk' }, + easy = { 'ltnk', 'ltnk', 'ltnk', 'ltnk', 'ltnk' } +} +NodUnitsGunner = +{ + tough = { 'e1', 'e1', 'e1', 'e1' }, + hard = { 'e1', 'e1', 'e1', 'e1', 'e1' }, + normal = { 'e1', 'e1', 'e1', 'e1', 'e1', 'e1', 'e1' }, + easy = { 'e1', 'e1', 'e1', 'e1', 'e1', 'e1', 'e1', 'e1', 'e1', 'e1' } +} +NodUnitsRocket = +{ + tough = { 'e3', 'e3', 'e3', 'e3' }, + hard = { 'e3', 'e3', 'e3', 'e3', 'e3' }, + normal = { 'e3', 'e3', 'e3', 'e3', 'e3', 'e3', 'e3' }, + easy = { 'e3', 'e3', 'e3', 'e3', 'e3', 'e3', 'e3', 'e3', 'e3', 'e3' } +} Gdi1Units = { 'e1', 'e1', 'e2', 'e2', 'e2' } +Obj2Units = { 'ftnk', 'e4', 'e4' } HuntCellTriggerActivator = { CPos.New(61,34), CPos.New(60,34), CPos.New(59,34), CPos.New(58,34), CPos.New(57,34), CPos.New(56,34), CPos.New(55,34), CPos.New(61,33), CPos.New(60,33), CPos.New(59,33), CPos.New(58,33), CPos.New(57,33), CPos.New(56,33) } DzneCellTriggerActivator = { CPos.New(50,30), CPos.New(49,30), CPos.New(48,30), CPos.New(47,30), CPos.New(46,30), CPos.New(45,30), CPos.New(50,29), CPos.New(49,29), CPos.New(48,29), CPos.New(47,29), CPos.New(46,29), CPos.New(45,29), CPos.New(50,28), CPos.New(49,28), CPos.New(48,28), CPos.New(47,28), CPos.New(46,28), CPos.New(45,28), CPos.New(50,27), CPos.New(49,27), CPos.New(46,27), CPos.New(45,27), CPos.New(50,26), CPos.New(49,26), CPos.New(48,26), CPos.New(47,26), CPos.New(46,26), CPos.New(45,26), CPos.New(50,25), CPos.New(49,25), CPos.New(48,25), CPos.New(47,25), CPos.New(46,25), CPos.New(45,25) } @@ -22,6 +47,7 @@ Chn1ActorTriggerActivator = { Chn1Actor1, Chn1Actor2 } Chn2ActorTriggerActivator = { Chn2Actor1 } Atk1ActorTriggerActivator = { Atk1Actor1, Atk1Actor2 } Atk2ActorTriggerActivator = { Atk2Actor1, Atk2Actor2 } +Obj2ActorTriggerActivator = { Obj2Actor0, Obj2Actor1, Obj2Actor2, Obj2Actor3, Obj2Actor4, Obj2Actor5, Obj2Actor6, Obj2Actor7, Obj2Actor8, Obj2Actor9, Obj2Actor10, Obj2Actor11, Obj2Actor12, Obj2Actor13, Obj2Actor14 } Chn1Waypoints = { ChnEntry.Location, waypoint0.Location } Chn2Waypoints = { ChnEntry.Location, waypoint0.Location } @@ -34,11 +60,6 @@ HuntTriggerFunction = function() end) end -Win1TriggerFunction = function() - NodObjective2 = player.AddPrimaryObjective("Move to the evacuation point.") - player.MarkCompletedObjective(NodObjective1) -end - Chn1TriggerFunction = function() if not Chn1Switch then local cargo = Reinforcements.ReinforceWithTransport(enemy, 'tran', Gdi1Units, Chn1Waypoints, { ChnEntry.Location })[2] @@ -83,6 +104,12 @@ Chn2TriggerFunction = function() end end +Obj2TriggerFunction = function() + player.MarkCompletedObjective(NodObjective2) + Media.PlaySpeechNotification(player, "Reinforce") + Reinforcements.Reinforce(player, Obj2Units, { Obj2UnitsEntry.Location, waypoint13.Location }, 15) +end + MoveAndHunt = function(unit, waypoints) if unit ~= nil then Utils.Do(waypoints, function(waypoint) @@ -93,6 +120,12 @@ MoveAndHunt = function(unit, waypoints) end InsertNodUnits = function() + local difficulty = Map.LobbyOption("difficulty") + NodUnitsVehicle1 = NodUnitsVehicle1[difficulty] + NodUnitsVehicle2 = NodUnitsVehicle2[difficulty] + NodUnitsGunner = NodUnitsGunner[difficulty] + NodUnitsRocket = NodUnitsRocket[difficulty] + Media.PlaySpeechNotification(player, "Reinforce") Camera.Position = UnitsRallyVehicle2.CenterPosition @@ -127,6 +160,8 @@ WorldLoaded = function() end) NodObjective1 = player.AddPrimaryObjective("Steal the GDI nuclear detonator.") + NodObjective2 = player.AddSecondaryObjective("Destroy the houses of the GDI supporters\nin the village.") + GDIObjective = enemy.AddPrimaryObjective("Stop the Nod taskforce from escaping with the detonator.") InsertNodUnits() @@ -144,17 +179,20 @@ WorldLoaded = function() Trigger.RemoveFootprintTrigger(id) end end) + + Trigger.OnAllRemovedFromWorld(Obj2ActorTriggerActivator, Obj2TriggerFunction) Trigger.OnEnteredFootprint(Win1CellTriggerActivator, function(a, id) if a.Owner == player then - Win1TriggerFunction() + NodObjective3 = player.AddPrimaryObjective("Move to the evacuation point.") + player.MarkCompletedObjective(NodObjective1) Trigger.RemoveFootprintTrigger(id) end end) Trigger.OnEnteredFootprint(Win2CellTriggerActivator, function(a, id) - if a.Owner == player and NodObjective2 then - player.MarkCompletedObjective(NodObjective2) + if a.Owner == player and NodObjective3 then + player.MarkCompletedObjective(NodObjective3) Trigger.RemoveFootprintTrigger(id) end end) diff --git a/mods/cnc/maps/nod06b/rules.yaml b/mods/cnc/maps/nod06b/rules.yaml index 34b943760f94..034a077c658c 100644 --- a/mods/cnc/maps/nod06b/rules.yaml +++ b/mods/cnc/maps/nod06b/rules.yaml @@ -9,6 +9,15 @@ World: BriefingVideo: nod6.vqa StartVideo: sundial.vqa LossVideo: banner.vqa + ScriptLobbyDropdown@difficulty: + ID: difficulty + Label: Difficulty + Values: + easy: Easy + normal: Normal + hard: Hard + tough: Real tough guy + Default: normal Player: PlayerResources: From fadcfa0828bee9d00fcfdaccca92970be70a4926 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 1 Mar 2018 19:20:56 +0000 Subject: [PATCH 14/48] Bump mono requirement to 4.2 in the deb package. --- packaging/linux/deb/DEBIAN/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/linux/deb/DEBIAN/control b/packaging/linux/deb/DEBIAN/control index 285acf71070e..419b83330daa 100644 --- a/packaging/linux/deb/DEBIAN/control +++ b/packaging/linux/deb/DEBIAN/control @@ -3,7 +3,7 @@ Version: {VERSION} Architecture: all Maintainer: Paul Chote Installed-Size: {SIZE} -Depends: libopenal1, mono-runtime (>= 3.2), libmono-system-core4.0-cil, libmono-system-drawing4.0-cil, libmono-system-data4.0-cil, libmono-system-numerics4.0-cil, libmono-system-runtime-serialization4.0-cil, libmono-system-xml-linq4.0-cil, libmono-i18n4.0-all, libfreetype6, libc6, libasound2, libgl1-mesa-glx, libgl1-mesa-dri, xdg-utils, zenity, libsdl2 | libsdl2-2.0-0, liblua5.1-0 +Depends: libopenal1, mono-runtime (>= 4.2), libmono-system-core4.0-cil, libmono-system-drawing4.0-cil, libmono-system-data4.0-cil, libmono-system-numerics4.0-cil, libmono-system-runtime-serialization4.0-cil, libmono-system-xml-linq4.0-cil, libmono-i18n4.0-all, libfreetype6, libc6, libasound2, libgl1-mesa-glx, libgl1-mesa-dri, xdg-utils, zenity, libsdl2 | libsdl2-2.0-0, liblua5.1-0 Section: games Priority: extra Homepage: http://www.openra.net/ From e69cf4fd5cbbcd72cdbd07b707b063d3cdde502e Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Wed, 7 Mar 2018 20:27:22 +0000 Subject: [PATCH 15/48] Rename another Stream.Write(byte[]) extension method. --- OpenRA.Game/Network/OrderIO.cs | 7 --- OpenRA.Game/StreamExts.cs | 8 ++- OpenRA.Mods.Cnc/FileSystem/BagFile.cs | 62 ++++++++++----------- OpenRA.Mods.Common/FileFormats/VqaReader.cs | 6 +- 4 files changed, 39 insertions(+), 44 deletions(-) diff --git a/OpenRA.Game/Network/OrderIO.cs b/OpenRA.Game/Network/OrderIO.cs index 2e5dc6105af1..0257220e5204 100644 --- a/OpenRA.Game/Network/OrderIO.cs +++ b/OpenRA.Game/Network/OrderIO.cs @@ -16,13 +16,6 @@ namespace OpenRA.Network { public static class OrderIO { - // Note: renamed from Write() to avoid being aliased by - // System.IO.Stream.Write(System.ReadOnlySpan) (which is not implemented in Mono) - public static void WriteArray(this Stream s, byte[] buf) - { - s.Write(buf, 0, buf.Length); - } - public static List ToOrderList(this byte[] bytes, World world) { var ms = new MemoryStream(bytes, 4, bytes.Length - 4); diff --git a/OpenRA.Game/StreamExts.cs b/OpenRA.Game/StreamExts.cs index 5ed2d1342656..0f5fe2ab285b 100644 --- a/OpenRA.Game/StreamExts.cs +++ b/OpenRA.Game/StreamExts.cs @@ -81,7 +81,7 @@ public static int ReadInt32(this Stream s) public static void Write(this Stream s, int value) { - s.Write(BitConverter.GetBytes(value)); + s.WriteArray(BitConverter.GetBytes(value)); } public static float ReadFloat(this Stream s) @@ -131,7 +131,9 @@ public static byte[] ReadAllBytes(this Stream s) } } - public static void Write(this Stream s, byte[] data) + // Note: renamed from Write() to avoid being aliased by + // System.IO.Stream.Write(System.ReadOnlySpan) (which is not implemented in Mono) + public static void WriteArray(this Stream s, byte[] data) { s.Write(data, 0, data.Length); } @@ -166,7 +168,7 @@ public static int WriteString(this Stream s, Encoding encoding, string text) bytes = new byte[0]; s.Write(bytes.Length); - s.Write(bytes); + s.WriteArray(bytes); return 4 + bytes.Length; } diff --git a/OpenRA.Mods.Cnc/FileSystem/BagFile.cs b/OpenRA.Mods.Cnc/FileSystem/BagFile.cs index c31875041dcb..ee8966bcd87d 100644 --- a/OpenRA.Mods.Cnc/FileSystem/BagFile.cs +++ b/OpenRA.Mods.Cnc/FileSystem/BagFile.cs @@ -55,19 +55,19 @@ public Stream GetStream(string filename) if ((entry.Flags & 2) > 0) { // PCM - waveHeaderMemoryStream.Write(Encoding.ASCII.GetBytes("RIFF")); - waveHeaderMemoryStream.Write(BitConverter.GetBytes(entry.Length + 36)); - waveHeaderMemoryStream.Write(Encoding.ASCII.GetBytes("WAVE")); - waveHeaderMemoryStream.Write(Encoding.ASCII.GetBytes("fmt ")); - waveHeaderMemoryStream.Write(BitConverter.GetBytes(16)); - waveHeaderMemoryStream.Write(BitConverter.GetBytes((short)1)); - waveHeaderMemoryStream.Write(BitConverter.GetBytes((short)channels)); - waveHeaderMemoryStream.Write(BitConverter.GetBytes(entry.SampleRate)); - waveHeaderMemoryStream.Write(BitConverter.GetBytes(2 * channels * entry.SampleRate)); - waveHeaderMemoryStream.Write(BitConverter.GetBytes((short)(2 * channels))); - waveHeaderMemoryStream.Write(BitConverter.GetBytes((short)16)); - waveHeaderMemoryStream.Write(Encoding.ASCII.GetBytes("data")); - waveHeaderMemoryStream.Write(BitConverter.GetBytes(entry.Length)); + waveHeaderMemoryStream.WriteArray(Encoding.ASCII.GetBytes("RIFF")); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes(entry.Length + 36)); + waveHeaderMemoryStream.WriteArray(Encoding.ASCII.GetBytes("WAVE")); + waveHeaderMemoryStream.WriteArray(Encoding.ASCII.GetBytes("fmt ")); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes(16)); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes((short)1)); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes((short)channels)); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes(entry.SampleRate)); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes(2 * channels * entry.SampleRate)); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes((short)(2 * channels))); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes((short)16)); + waveHeaderMemoryStream.WriteArray(Encoding.ASCII.GetBytes("data")); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes(entry.Length)); } if ((entry.Flags & 8) > 0) @@ -77,24 +77,24 @@ public Stream GetStream(string filename) var bytesPerSec = (int)Math.Floor(((double)(2 * entry.ChunkSize) / samplesPerChunk) * ((double)entry.SampleRate / 2)); var chunkSize = entry.ChunkSize > entry.Length ? entry.Length : entry.ChunkSize; - waveHeaderMemoryStream.Write(Encoding.ASCII.GetBytes("RIFF")); - waveHeaderMemoryStream.Write(BitConverter.GetBytes(entry.Length + 52)); - waveHeaderMemoryStream.Write(Encoding.ASCII.GetBytes("WAVE")); - waveHeaderMemoryStream.Write(Encoding.ASCII.GetBytes("fmt ")); - waveHeaderMemoryStream.Write(BitConverter.GetBytes(20)); - waveHeaderMemoryStream.Write(BitConverter.GetBytes((short)17)); - waveHeaderMemoryStream.Write(BitConverter.GetBytes((short)channels)); - waveHeaderMemoryStream.Write(BitConverter.GetBytes(entry.SampleRate)); - waveHeaderMemoryStream.Write(BitConverter.GetBytes(bytesPerSec)); - waveHeaderMemoryStream.Write(BitConverter.GetBytes((short)chunkSize)); - waveHeaderMemoryStream.Write(BitConverter.GetBytes((short)4)); - waveHeaderMemoryStream.Write(BitConverter.GetBytes((short)2)); - waveHeaderMemoryStream.Write(BitConverter.GetBytes((short)samplesPerChunk)); - waveHeaderMemoryStream.Write(Encoding.ASCII.GetBytes("fact")); - waveHeaderMemoryStream.Write(BitConverter.GetBytes(4)); - waveHeaderMemoryStream.Write(BitConverter.GetBytes(4 * entry.Length)); - waveHeaderMemoryStream.Write(Encoding.ASCII.GetBytes("data")); - waveHeaderMemoryStream.Write(BitConverter.GetBytes(entry.Length)); + waveHeaderMemoryStream.WriteArray(Encoding.ASCII.GetBytes("RIFF")); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes(entry.Length + 52)); + waveHeaderMemoryStream.WriteArray(Encoding.ASCII.GetBytes("WAVE")); + waveHeaderMemoryStream.WriteArray(Encoding.ASCII.GetBytes("fmt ")); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes(20)); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes((short)17)); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes((short)channels)); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes(entry.SampleRate)); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes(bytesPerSec)); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes((short)chunkSize)); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes((short)4)); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes((short)2)); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes((short)samplesPerChunk)); + waveHeaderMemoryStream.WriteArray(Encoding.ASCII.GetBytes("fact")); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes(4)); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes(4 * entry.Length)); + waveHeaderMemoryStream.WriteArray(Encoding.ASCII.GetBytes("data")); + waveHeaderMemoryStream.WriteArray(BitConverter.GetBytes(entry.Length)); } waveHeaderMemoryStream.Seek(0, SeekOrigin.Begin); diff --git a/OpenRA.Mods.Common/FileFormats/VqaReader.cs b/OpenRA.Mods.Common/FileFormats/VqaReader.cs index 388d842532a6..09b8841baab9 100644 --- a/OpenRA.Mods.Common/FileFormats/VqaReader.cs +++ b/OpenRA.Mods.Common/FileFormats/VqaReader.cs @@ -194,14 +194,14 @@ void CollectAudioData() else if (audioChannels == 1) { var rawAudio = stream.ReadBytes((int)length); - audio1.Write(rawAudio); + audio1.WriteArray(rawAudio); } else { var rawAudio = stream.ReadBytes((int)length / 2); - audio1.Write(rawAudio); + audio1.WriteArray(rawAudio); rawAudio = stream.ReadBytes((int)length / 2); - audio2.Write(rawAudio); + audio2.WriteArray(rawAudio); if (length % 2 != 0) stream.ReadBytes(2); } From 711bad91a3c87a9437969c9afa1e355aa3f46389 Mon Sep 17 00:00:00 2001 From: Voidwalker Date: Thu, 28 Dec 2017 12:18:29 +0000 Subject: [PATCH 16/48] Generalize WormManager into ActorSpawnManager. Added support of multiple actors, conditions and types. --- OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 2 + OpenRA.Mods.Common/Traits/ActorSpawner.cs | 34 +++++ .../Traits/World/ActorSpawnManager.cs | 116 ++++++++++++++++++ .../UtilityCommands/UpgradeRules.cs | 15 +++ OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj | 2 - OpenRA.Mods.D2k/Traits/Sandworm.cs | 6 +- OpenRA.Mods.D2k/Traits/World/WormManager.cs | 113 ----------------- OpenRA.Mods.D2k/Traits/WormSpawner.cs | 19 --- mods/d2k/maps/mount-idaho/rules.yaml | 2 +- mods/d2k/maps/oasis-conquest/rules.yaml | 2 +- mods/d2k/maps/pasty-mesa/rules.yaml | 2 +- mods/d2k/maps/shellmap/rules.yaml | 2 +- mods/d2k/rules/campaign-rules.yaml | 2 +- mods/d2k/rules/misc.yaml | 2 +- mods/d2k/rules/world.yaml | 3 +- 15 files changed, 178 insertions(+), 144 deletions(-) create mode 100644 OpenRA.Mods.Common/Traits/ActorSpawner.cs create mode 100644 OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs delete mode 100644 OpenRA.Mods.D2k/Traits/World/WormManager.cs delete mode 100644 OpenRA.Mods.D2k/Traits/WormSpawner.cs diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 8767e17ce713..a78dc915c31d 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -552,6 +552,8 @@ + + diff --git a/OpenRA.Mods.Common/Traits/ActorSpawner.cs b/OpenRA.Mods.Common/Traits/ActorSpawner.cs new file mode 100644 index 000000000000..ea1c8b57238a --- /dev/null +++ b/OpenRA.Mods.Common/Traits/ActorSpawner.cs @@ -0,0 +1,34 @@ +#region Copyright & License Information +/* + * Copyright 2007-2018 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("An actor with this trait indicates a valid spawn point for actors of ActorSpawnManager.")] + public class ActorSpawnerInfo : ConditionalTraitInfo + { + [Desc("Type of ActorSpawner with which it connects.")] + public readonly HashSet Types = new HashSet() { }; + + public override object Create(ActorInitializer init) { return new ActorSpawner(this); } + } + + public class ActorSpawner : ConditionalTrait + { + public ActorSpawner(ActorSpawnerInfo info) + : base(info) { } + + public HashSet Types { get { return Info.Types; } } + } +} diff --git a/OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs b/OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs new file mode 100644 index 000000000000..e10249e89608 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs @@ -0,0 +1,116 @@ +#region Copyright & License Information +/* + * Copyright 2007-2018 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.Primitives; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Controls the spawning of specified actor types. Attach this to the world actor.")] + public class ActorSpawnManagerInfo : ConditionalTraitInfo, Requires + { + [Desc("Minimum number of actors.")] + public readonly int Minimum = 0; + + [Desc("Maximum number of actors.")] + public readonly int Maximum = 4; + + [Desc("Time (in ticks) between actor spawn.")] + public readonly int SpawnInterval = 6000; + + [FieldLoader.Require] + [ActorReference] + [Desc("Name of the actor that will be randomly picked to spawn.")] + public readonly string[] Actors = { }; + + public readonly string Owner = "Creeps"; + + [Desc("Type of ActorSpawner with which it connects.")] + public readonly HashSet Types = new HashSet() { }; + + public override object Create(ActorInitializer init) { return new ActorSpawnManager(init.Self, this); } + } + + public class ActorSpawnManager : ConditionalTrait, ITick, INotifyCreated + { + readonly ActorSpawnManagerInfo info; + TraitPair[] spawnPointActors; + + bool enabled; + int spawnCountdown; + int actorsPresent; + + public ActorSpawnManager(Actor self, ActorSpawnManagerInfo info) : base(info) + { + this.info = info; + } + + void INotifyCreated.Created(Actor self) + { + self.World.AddFrameEndTask(w => + { + spawnPointActors = w.ActorsWithTrait() + .Where(x => info.Types.Overlaps(x.Trait.Types) || !x.Trait.Types.Any()) + .ToArray(); + + enabled = self.Trait().Enabled && spawnPointActors.Any(); + }); + } + + void ITick.Tick(Actor self) + { + if (IsTraitDisabled || !enabled) + return; + + if (info.Maximum < 1 || actorsPresent >= info.Maximum) + return; + + if (--spawnCountdown > 0 && actorsPresent >= info.Minimum) + return; + + spawnCountdown = info.SpawnInterval; + + do + { + // Always spawn at least one actor, plus + // however many needed to reach the minimum. + SpawnActor(self); + } while (actorsPresent < info.Minimum); + } + + WPos SpawnActor(Actor self) + { + var spawnPoint = GetRandomSpawnPoint(self.World.SharedRandom); + self.World.AddFrameEndTask(w => w.CreateActor(info.Actors.Random(self.World.SharedRandom), new TypeDictionary + { + new OwnerInit(w.Players.First(x => x.PlayerName == info.Owner)), + new LocationInit(spawnPoint.Location) + })); + + actorsPresent++; + + return spawnPoint.CenterPosition; + } + + Actor GetRandomSpawnPoint(Support.MersenneTwister random) + { + return spawnPointActors.Random(random).Actor; + } + + public void DecreaseActorCount() + { + actorsPresent--; + } + } +} diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 3d40f700884b..8d9c2156bb7a 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -1635,6 +1635,21 @@ internal static void UpgradeActorRules(ModData modData, int engineVersion, ref L } } + if (engineVersion < 20180225) + { + if (node.Key == "WormSpawner") + RenameNodeKey(node, "ActorSpawner"); + + if (node.Key == "WormManager") + { + RenameNodeKey(node, "ActorSpawnManager"); + + var wormSignature = node.Value.Nodes.FirstOrDefault(n => n.Key == "WormSignature"); + if (wormSignature != null) + wormSignature.Key = "Actors"; + } + } + UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); } diff --git a/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj b/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj index bfed6c8eec56..3f2340997feb 100644 --- a/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj +++ b/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj @@ -80,9 +80,7 @@ - - diff --git a/OpenRA.Mods.D2k/Traits/Sandworm.cs b/OpenRA.Mods.D2k/Traits/Sandworm.cs index 5a21607921d6..30f310293b17 100644 --- a/OpenRA.Mods.D2k/Traits/Sandworm.cs +++ b/OpenRA.Mods.D2k/Traits/Sandworm.cs @@ -37,7 +37,7 @@ class Sandworm : Wanders, ITick, INotifyActorDisposing { public readonly SandwormInfo WormInfo; - readonly WormManager manager; + readonly ActorSpawnManager manager; readonly Mobile mobile; readonly AttackBase attackTrait; @@ -54,7 +54,7 @@ public Sandworm(Actor self, SandwormInfo info) WormInfo = info; mobile = self.Trait(); attackTrait = self.Trait(); - manager = self.World.WorldActor.Trait(); + manager = self.World.WorldActor.Trait(); } public override void DoAction(Actor self, CPos targetCell) @@ -140,7 +140,7 @@ void INotifyActorDisposing.Disposing(Actor self) if (disposed) return; - manager.DecreaseWormCount(); + manager.DecreaseActorCount(); disposed = true; } } diff --git a/OpenRA.Mods.D2k/Traits/World/WormManager.cs b/OpenRA.Mods.D2k/Traits/World/WormManager.cs deleted file mode 100644 index 8044624ef959..000000000000 --- a/OpenRA.Mods.D2k/Traits/World/WormManager.cs +++ /dev/null @@ -1,113 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2018 The OpenRA Developers (see AUTHORS) - * This file is part of OpenRA, which is free software. It is made - * available to you under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. For more - * information, see COPYING. - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; -using OpenRA.Mods.Common.Traits; -using OpenRA.Primitives; -using OpenRA.Traits; - -namespace OpenRA.Mods.D2k.Traits -{ - [Desc("Controls the spawning of sandworms. Attach this to the world actor.")] - class WormManagerInfo : ITraitInfo, Requires - { - [Desc("Minimum number of worms")] - public readonly int Minimum = 0; - - [Desc("Maximum number of worms")] - public readonly int Maximum = 4; - - [Desc("Time (in ticks) between worm spawn.")] - public readonly int SpawnInterval = 6000; - - [Desc("Name of the actor that will be spawned.")] - public readonly string WormSignature = "sandworm"; - - public readonly string WormSignNotification = "WormSign"; - public readonly string WormOwnerPlayer = "Creeps"; - - public object Create(ActorInitializer init) { return new WormManager(init.Self, this); } - } - - class WormManager : ITick, INotifyCreated - { - readonly WormManagerInfo info; - readonly Lazy spawnPointActors; - - bool enabled; - int spawnCountdown; - int wormsPresent; - - public WormManager(Actor self, WormManagerInfo info) - { - this.info = info; - spawnPointActors = Exts.Lazy(() => self.World.ActorsHavingTrait().ToArray()); - } - - void INotifyCreated.Created(Actor self) - { - enabled = self.Trait().Enabled; - } - - void ITick.Tick(Actor self) - { - if (!enabled) - return; - - if (!spawnPointActors.Value.Any()) - return; - - // Apparently someone doesn't want worms or the maximum number of worms has been reached - if (info.Maximum < 1 || wormsPresent >= info.Maximum) - return; - - if (--spawnCountdown > 0 && wormsPresent >= info.Minimum) - return; - - spawnCountdown = info.SpawnInterval; - - var wormLocations = new List(); - - do - { - // Always spawn at least one worm, plus however many - // more we need to reach the defined minimum count. - wormLocations.Add(SpawnWorm(self)); - } while (wormsPresent < info.Minimum); - } - - WPos SpawnWorm(Actor self) - { - var spawnPoint = GetRandomSpawnPoint(self); - self.World.AddFrameEndTask(w => w.CreateActor(info.WormSignature, new TypeDictionary - { - new OwnerInit(w.Players.First(x => x.PlayerName == info.WormOwnerPlayer)), - new LocationInit(spawnPoint.Location) - })); - - wormsPresent++; - - return spawnPoint.CenterPosition; - } - - Actor GetRandomSpawnPoint(Actor self) - { - return spawnPointActors.Value.Random(self.World.SharedRandom); - } - - public void DecreaseWormCount() - { - wormsPresent--; - } - } -} diff --git a/OpenRA.Mods.D2k/Traits/WormSpawner.cs b/OpenRA.Mods.D2k/Traits/WormSpawner.cs deleted file mode 100644 index 499e1a0f3d03..000000000000 --- a/OpenRA.Mods.D2k/Traits/WormSpawner.cs +++ /dev/null @@ -1,19 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2018 The OpenRA Developers (see AUTHORS) - * This file is part of OpenRA, which is free software. It is made - * available to you under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. For more - * information, see COPYING. - */ -#endregion - -using OpenRA.Traits; - -namespace OpenRA.Mods.D2k.Traits -{ - [Desc("An actor with this trait indicates a valid spawn point for sandworms.")] - class WormSpawnerInfo : TraitInfo { } - class WormSpawner { } -} \ No newline at end of file diff --git a/mods/d2k/maps/mount-idaho/rules.yaml b/mods/d2k/maps/mount-idaho/rules.yaml index 114a8bf4e95a..eb556d164ff8 100644 --- a/mods/d2k/maps/mount-idaho/rules.yaml +++ b/mods/d2k/maps/mount-idaho/rules.yaml @@ -1,4 +1,4 @@ World: - WormManager: + ActorSpawnManager: Minimum: 1 Maximum: 2 diff --git a/mods/d2k/maps/oasis-conquest/rules.yaml b/mods/d2k/maps/oasis-conquest/rules.yaml index 363d4add29eb..93bcc8dd133a 100644 --- a/mods/d2k/maps/oasis-conquest/rules.yaml +++ b/mods/d2k/maps/oasis-conquest/rules.yaml @@ -1,4 +1,4 @@ World: - WormManager: + ActorSpawnManager: Minimum: 3 Maximum: 6 diff --git a/mods/d2k/maps/pasty-mesa/rules.yaml b/mods/d2k/maps/pasty-mesa/rules.yaml index 114a8bf4e95a..eb556d164ff8 100644 --- a/mods/d2k/maps/pasty-mesa/rules.yaml +++ b/mods/d2k/maps/pasty-mesa/rules.yaml @@ -1,4 +1,4 @@ World: - WormManager: + ActorSpawnManager: Minimum: 1 Maximum: 2 diff --git a/mods/d2k/maps/shellmap/rules.yaml b/mods/d2k/maps/shellmap/rules.yaml index 402728dfb1e0..9579520ef87c 100644 --- a/mods/d2k/maps/shellmap/rules.yaml +++ b/mods/d2k/maps/shellmap/rules.yaml @@ -8,7 +8,7 @@ World: -MPStartLocations: ResourceType@Spice: ValuePerUnit: 0 - WormManager: + ActorSpawnManager: Minimum: 1 Maximum: 3 MusicPlaylist: diff --git a/mods/d2k/rules/campaign-rules.yaml b/mods/d2k/rules/campaign-rules.yaml index 38ee58733136..7807fed2d28d 100644 --- a/mods/d2k/rules/campaign-rules.yaml +++ b/mods/d2k/rules/campaign-rules.yaml @@ -16,7 +16,7 @@ World: -MPStartLocations: ObjectivesPanel: PanelName: MISSION_OBJECTIVES - WormManager: + ActorSpawnManager: Minimum: 1 Maximum: 1 MapCreeps: diff --git a/mods/d2k/rules/misc.yaml b/mods/d2k/rules/misc.yaml index 2bc5abd3daad..e2eefdc98b15 100644 --- a/mods/d2k/rules/misc.yaml +++ b/mods/d2k/rules/misc.yaml @@ -204,7 +204,7 @@ wormspawner: WithSpriteBody: BodyOrientation: QuantizedFacings: 1 - WormSpawner: + ActorSpawner: EditorTilesetFilter: Categories: System diff --git a/mods/d2k/rules/world.yaml b/mods/d2k/rules/world.yaml index 427a864fbf57..03067b25c754 100644 --- a/mods/d2k/rules/world.yaml +++ b/mods/d2k/rules/world.yaml @@ -72,7 +72,8 @@ World: BuildingInfluence: ProductionQueueFromSelection: ProductionPaletteWidget: PRODUCTION_PALETTE - WormManager: + ActorSpawnManager: + Actors: sandworm CrateSpawner: Minimum: 0 Maximum: 2 From 55b11d1745380b53015a75e4c16e87631d5840c3 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sun, 25 Feb 2018 16:56:47 +0100 Subject: [PATCH 17/48] Don't cache ActorSpawners at creation Re-evaluate before every spawn attempt instead. Also accounts for ActorSpawner being conditional now. While a bit more costly in terms of performance, this allows to create spawns mid-game. --- .../Traits/World/ActorSpawnManager.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs b/OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs index e10249e89608..bea170999236 100644 --- a/OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs +++ b/OpenRA.Mods.Common/Traits/World/ActorSpawnManager.cs @@ -45,7 +45,6 @@ public class ActorSpawnManagerInfo : ConditionalTraitInfo, Requires, ITick, INotifyCreated { readonly ActorSpawnManagerInfo info; - TraitPair[] spawnPointActors; bool enabled; int spawnCountdown; @@ -58,14 +57,7 @@ public ActorSpawnManager(Actor self, ActorSpawnManagerInfo info) : base(info) void INotifyCreated.Created(Actor self) { - self.World.AddFrameEndTask(w => - { - spawnPointActors = w.ActorsWithTrait() - .Where(x => info.Types.Overlaps(x.Trait.Types) || !x.Trait.Types.Any()) - .ToArray(); - - enabled = self.Trait().Enabled && spawnPointActors.Any(); - }); + enabled = self.Trait().Enabled; } void ITick.Tick(Actor self) @@ -79,19 +71,23 @@ void ITick.Tick(Actor self) if (--spawnCountdown > 0 && actorsPresent >= info.Minimum) return; + var spawnPoint = GetRandomSpawnPoint(self.World, self.World.SharedRandom); + + if (spawnPoint == null) + return; + spawnCountdown = info.SpawnInterval; do { // Always spawn at least one actor, plus // however many needed to reach the minimum. - SpawnActor(self); + SpawnActor(self, spawnPoint); } while (actorsPresent < info.Minimum); } - WPos SpawnActor(Actor self) + WPos SpawnActor(Actor self, Actor spawnPoint) { - var spawnPoint = GetRandomSpawnPoint(self.World.SharedRandom); self.World.AddFrameEndTask(w => w.CreateActor(info.Actors.Random(self.World.SharedRandom), new TypeDictionary { new OwnerInit(w.Players.First(x => x.PlayerName == info.Owner)), @@ -103,9 +99,13 @@ WPos SpawnActor(Actor self) return spawnPoint.CenterPosition; } - Actor GetRandomSpawnPoint(Support.MersenneTwister random) + Actor GetRandomSpawnPoint(World world, Support.MersenneTwister random) { - return spawnPointActors.Random(random).Actor; + var spawnPointActors = world.ActorsWithTrait() + .Where(x => !x.Trait.IsTraitDisabled && (info.Types.Overlaps(x.Trait.Types) || !x.Trait.Types.Any())) + .ToArray(); + + return spawnPointActors.Any() ? spawnPointActors.Random(random).Actor : null; } public void DecreaseActorCount() From d90ff99e745a9546d94b8fac6a5ea044c709a95f Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sat, 16 Sep 2017 19:45:41 +0200 Subject: [PATCH 18/48] Replace WithReloadingSpriteTurret with conditions WithReloadingSpriteTurret was bound to run into conflicts with any WithTurret*Animation traits due to overriding the turret sequence constantly via ITick. Using (stacked) conditions instead avoids that. --- OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj | 1 - .../Render/WithReloadingSpriteTurret.cs | 76 ------------------- .../UtilityCommands/UpgradeRules.cs | 24 ++++++ mods/cnc/rules/vehicles.yaml | 11 ++- mods/cnc/sequences/vehicles.yaml | 4 - 5 files changed, 33 insertions(+), 83 deletions(-) delete mode 100644 OpenRA.Mods.Cnc/Traits/Render/WithReloadingSpriteTurret.cs diff --git a/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj b/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj index 7cd81fde781b..53000d511f6d 100644 --- a/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj +++ b/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj @@ -71,7 +71,6 @@ - diff --git a/OpenRA.Mods.Cnc/Traits/Render/WithReloadingSpriteTurret.cs b/OpenRA.Mods.Cnc/Traits/Render/WithReloadingSpriteTurret.cs deleted file mode 100644 index f7f48a3e283e..000000000000 --- a/OpenRA.Mods.Cnc/Traits/Render/WithReloadingSpriteTurret.cs +++ /dev/null @@ -1,76 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2018 The OpenRA Developers (see AUTHORS) - * This file is part of OpenRA, which is free software. It is made - * available to you under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. For more - * information, see COPYING. - */ -#endregion - -using System; -using System.Linq; -using OpenRA.Mods.Common.Traits; -using OpenRA.Mods.Common.Traits.Render; -using OpenRA.Traits; - -namespace OpenRA.Mods.Cnc.Traits.Render -{ - [Desc("Renders ammo-dependent turret graphics for units with the Turreted trait.")] - public class WithReloadingSpriteTurretInfo : WithSpriteTurretInfo, Requires, Requires - { - [Desc("AmmoPool to use for ammo-dependent sequences.")] - public readonly string AmmoPoolName = null; - - [Desc("How many reload stages does this turret have. Defaults to AmmoPool's Ammo.", - "Adds current reload stage to Sequence as suffix when a matching AmmoPool is present.")] - public readonly int ReloadStages = -1; - - public override object Create(ActorInitializer init) { return new WithReloadingSpriteTurret(init.Self, this); } - } - - public class WithReloadingSpriteTurret : WithSpriteTurret - { - readonly int reloadStages; - readonly AmmoPool ammoPool; - string sequence; - string ammoSuffix; - - public WithReloadingSpriteTurret(Actor self, WithReloadingSpriteTurretInfo info) - : base(self, info) - { - ammoPool = self.TraitsImplementing().FirstOrDefault(a => a.Info.Name == info.AmmoPoolName); - if (ammoPool == null) - throw new InvalidOperationException("Actor type '" + self.Info.Name + "' does not define a valid ammo pool for its reloading turret."); - - sequence = Info.Sequence; - reloadStages = info.ReloadStages; - - var initialAmmo = ammoPool.Info.InitialAmmo; - var ammo = ammoPool.Info.Ammo; - var initialAmmoStage = initialAmmo >= 0 && initialAmmo != ammo ? initialAmmo : ammo; - - if (ammoPool != null && reloadStages < 0) - ammoSuffix = initialAmmoStage.ToString(); - if (ammoPool != null && reloadStages >= 0) - ammoSuffix = (initialAmmoStage * reloadStages / ammo).ToString(); - } - - protected override void Tick(Actor self) - { - if (Info.AimSequence != null) - sequence = Attack.IsAiming ? Info.AimSequence : Info.Sequence; - - var currentAmmo = ammoPool.GetAmmoCount(); - if (reloadStages < 0) - ammoSuffix = currentAmmo.ToString(); - if (reloadStages >= 0) - ammoSuffix = (currentAmmo * reloadStages / ammoPool.Info.Ammo).ToString(); - - var newSequence = NormalizeSequence(self, sequence + ammoSuffix); - if (DefaultAnimation.CurrentSequence.Name != newSequence) - DefaultAnimation.ReplaceAnim(newSequence); - } - } -} diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 8d9c2156bb7a..985dcfed650f 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -1650,6 +1650,30 @@ internal static void UpgradeActorRules(ModData modData, int engineVersion, ref L } } + // Removed WithReloadingSpriteTurret + if (engineVersion < 20180223) + { + var reloadingTurret = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("WithReloadingSpriteTurret", StringComparison.Ordinal)); + if (reloadingTurret != null) + { + var ammoPool = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("AmmoPool", StringComparison.Ordinal)); + if (ammoPool != null) + ammoPool.Value.Nodes.Add(new MiniYamlNode("AmmoCondition", "ammo")); + + RenameNodeKey(reloadingTurret, "WithSpriteTurret"); + var noAmmoTurret = new MiniYamlNode("WithSpriteTurret@NoAmmo", ""); + var reqAmmoCondition = new MiniYamlNode("RequiresCondition", "ammo"); + var reqNoAmmoCondition = new MiniYamlNode("RequiresCondition", "!ammo"); + + reloadingTurret.Value.Nodes.Add(reqAmmoCondition); + noAmmoTurret.Value.Nodes.Add(reqNoAmmoCondition); + node.Value.Nodes.Add(noAmmoTurret); + + Console.WriteLine("WithReloadingSpriteTurret has been removed in favor of using stacked AmmoPool.AmmoConditions."); + Console.WriteLine("Check if your affected actors need further changes."); + } + } + UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); } diff --git a/mods/cnc/rules/vehicles.yaml b/mods/cnc/rules/vehicles.yaml index 6d21a70662fa..577a7827e30a 100644 --- a/mods/cnc/rules/vehicles.yaml +++ b/mods/cnc/rules/vehicles.yaml @@ -539,9 +539,16 @@ MLRS: AmmoPool: Ammo: 2 PipCount: 0 + AmmoCondition: ammo AttackTurreted: - WithReloadingSpriteTurret: - AmmoPoolName: primary + WithSpriteTurret: + RequiresCondition: ammo > 1 + WithSpriteTurret@OneMissile: + RequiresCondition: ammo == 1 + Sequence: turret1 + WithSpriteTurret@NoMissiles: + RequiresCondition: !ammo + Sequence: turret0 AutoTarget: InitialStanceAI: Defend RenderRangeCircle: diff --git a/mods/cnc/sequences/vehicles.yaml b/mods/cnc/sequences/vehicles.yaml index c354858fac14..c8602cc9a8be 100644 --- a/mods/cnc/sequences/vehicles.yaml +++ b/mods/cnc/sequences/vehicles.yaml @@ -256,10 +256,6 @@ mlrs: Start: 32 Facings: 32 UseClassicFacingFudge: True - turret2: - Start: 32 - Facings: 32 - UseClassicFacingFudge: True turret1: Start: 64 Facings: 32 From 92584c3c850590869ff45dc2f6313f6388371bd5 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Thu, 8 Mar 2018 15:50:21 +0100 Subject: [PATCH 19/48] Fix upgrade rule dates to be after release-20180307 --- OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 985dcfed650f..b86d39291a92 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -1635,7 +1635,7 @@ internal static void UpgradeActorRules(ModData modData, int engineVersion, ref L } } - if (engineVersion < 20180225) + if (engineVersion < 20180308) { if (node.Key == "WormSpawner") RenameNodeKey(node, "ActorSpawner"); @@ -1651,7 +1651,7 @@ internal static void UpgradeActorRules(ModData modData, int engineVersion, ref L } // Removed WithReloadingSpriteTurret - if (engineVersion < 20180223) + if (engineVersion < 20180308) { var reloadingTurret = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("WithReloadingSpriteTurret", StringComparison.Ordinal)); if (reloadingTurret != null) From 12054506e1399bccadd94727299266fc3133821f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Mail=C3=A4nder?= Date: Sat, 26 Aug 2017 18:47:58 +0200 Subject: [PATCH 20/48] Add support for JASC and GIMP color palettes --- OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 1 + .../Traits/World/PaletteFromFile.cs | 1 + .../Traits/World/PaletteFromGimpOrJascFile.cs | 127 ++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 OpenRA.Mods.Common/Traits/World/PaletteFromGimpOrJascFile.cs diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index a78dc915c31d..f61fd348f69f 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -530,6 +530,7 @@ + diff --git a/OpenRA.Mods.Common/Traits/World/PaletteFromFile.cs b/OpenRA.Mods.Common/Traits/World/PaletteFromFile.cs index 23661b47afe6..20e3f061b554 100644 --- a/OpenRA.Mods.Common/Traits/World/PaletteFromFile.cs +++ b/OpenRA.Mods.Common/Traits/World/PaletteFromFile.cs @@ -15,6 +15,7 @@ namespace OpenRA.Mods.Common.Traits { + [Desc("Load VGA palette (.pal) registers.")] class PaletteFromFileInfo : ITraitInfo { [FieldLoader.Require, PaletteDefinition] diff --git a/OpenRA.Mods.Common/Traits/World/PaletteFromGimpOrJascFile.cs b/OpenRA.Mods.Common/Traits/World/PaletteFromGimpOrJascFile.cs new file mode 100644 index 000000000000..01736884d4e1 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/World/PaletteFromGimpOrJascFile.cs @@ -0,0 +1,127 @@ +#region Copyright & License Information +/* + * Copyright 2007-2018 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Load a GIMP .gpl or JASC .pal palette file. Supports per-color alpha. Index 0 is hardcoded to be fully transparent/invisible.")] + class PaletteFromGimpOrJascFileInfo : ITraitInfo + { + [FieldLoader.Require, PaletteDefinition] + [Desc("Palette name used internally.")] + public readonly string Name = null; + + [Desc("Defines for which tileset IDs this palette should be loaded.", + "If none specified, it applies to all tileset IDs not explicitly excluded.")] + public readonly HashSet Tilesets = new HashSet(); + + [Desc("Don't load palette for these tileset IDs.")] + public readonly HashSet ExcludeTilesets = new HashSet(); + + [FieldLoader.Require] + [Desc("Name of the file to load.")] + public readonly string Filename = null; + + [Desc("Premultiply colors with their alpha values.")] + public readonly bool Premultiply = true; + + public readonly bool AllowModifiers = true; + + public object Create(ActorInitializer init) { return new PaletteFromGimpOrJascFile(init.World, this); } + } + + class PaletteFromGimpOrJascFile : ILoadsPalettes, IProvidesAssetBrowserPalettes + { + readonly World world; + readonly PaletteFromGimpOrJascFileInfo info; + + public PaletteFromGimpOrJascFile(World world, PaletteFromGimpOrJascFileInfo info) + { + this.world = world; + this.info = info; + } + + public void LoadPalettes(WorldRenderer wr) + { + var colors = new uint[Palette.Size]; + using (var s = world.Map.Open(info.Filename)) + { + using (var lines = s.ReadAllLines().GetEnumerator()) + { + if (lines == null) + return; + + if (!lines.MoveNext() || (lines.Current != "GIMP Palette" && lines.Current != "JASC-PAL")) + throw new InvalidDataException("File `{0}` is not a valid GIMP or JASC palette.".F(info.Filename)); + + byte r, g, b, a; + a = 255; + var i = 0; + + while (lines.MoveNext() && i < Palette.Size) + { + // Skip until first color. Ignore # comments, Name/Columns and blank lines as well as JASC header values. + if (string.IsNullOrEmpty(lines.Current) || !char.IsDigit(lines.Current.Trim()[0]) || lines.Current == "0100" || lines.Current == "256") + continue; + + var rgba = lines.Current.Split((char[])null, StringSplitOptions.RemoveEmptyEntries); + if (rgba.Length < 3) + throw new InvalidDataException("Invalid RGB(A) triplet/quartet: ({0})".F(string.Join(" ", rgba))); + + if (!byte.TryParse(rgba[0], out r)) + throw new InvalidDataException("Invalid R value: {0}".F(rgba[0])); + + if (!byte.TryParse(rgba[1], out g)) + throw new InvalidDataException("Invalid G value: {0}".F(rgba[1])); + + if (!byte.TryParse(rgba[2], out b)) + throw new InvalidDataException("Invalid B value: {0}".F(rgba[2])); + + // Check if color has a (valid) alpha value. + // Note: We can't throw on "rgba.Length > 3 but parse failed", because in GIMP palettes the 'invalid' value is probably a color name string. + var noAlpha = rgba.Length > 3 ? !byte.TryParse(rgba[3], out a) : true; + + // Index 0 should always be completely transparent/background color + if (i == 0) + colors[i] = 0; + else if (noAlpha) + colors[i] = (uint)Color.FromArgb(r, g, b).ToArgb(); + else if (info.Premultiply) + colors[i] = (uint)Color.FromArgb(a, r * a / 255, g * a / 255, b * a / 255).ToArgb(); + else + colors[i] = (uint)Color.FromArgb(a, r, g, b).ToArgb(); + + i++; + } + } + } + + wr.AddPalette(info.Name, new ImmutablePalette(colors), info.AllowModifiers); + } + + public IEnumerable PaletteNames + { + get + { + // Only expose the palette if it is available for the shellmap's tileset (which is a requirement for its use). + if ((info.Tilesets.Count == 0 || info.Tilesets.Contains(world.Map.Rules.TileSet.Id)) + && !info.ExcludeTilesets.Contains(world.Map.Rules.TileSet.Id)) + yield return info.Name; + } + } + } +} From c976bb1d7b64bdc50dd039af31fb50e550f49ff3 Mon Sep 17 00:00:00 2001 From: Mustafa Alperen Seki Date: Tue, 16 Jan 2018 12:38:20 +0300 Subject: [PATCH 21/48] Make BaseProvider PausableConditional --- .../Traits/Buildings/BaseProvider.cs | 17 +++++++++++------ OpenRA.Mods.Common/Traits/Buildings/Building.cs | 4 ++-- .../Traits/Player/PlaceBuilding.cs | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Buildings/BaseProvider.cs b/OpenRA.Mods.Common/Traits/Buildings/BaseProvider.cs index 97f3d694d2ec..1881d7aec519 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/BaseProvider.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/BaseProvider.cs @@ -18,18 +18,17 @@ namespace OpenRA.Mods.Common.Traits { [Desc("Limits the zone where buildings can be constructed to a radius around this actor.")] - public class BaseProviderInfo : ITraitInfo + public class BaseProviderInfo : PausableConditionalTraitInfo { public readonly WDist Range = WDist.FromCells(10); public readonly int Cooldown = 0; public readonly int InitialDelay = 0; - public object Create(ActorInitializer init) { return new BaseProvider(init.Self, this); } + public override object Create(ActorInitializer init) { return new BaseProvider(init.Self, this); } } - public class BaseProvider : ITick, INotifyCreated, IRenderAboveShroudWhenSelected, ISelectionBar + public class BaseProvider : PausableConditionalTrait, ITick, INotifyCreated, IRenderAboveShroudWhenSelected, ISelectionBar { - public readonly BaseProviderInfo Info; readonly DeveloperMode devMode; readonly Actor self; readonly bool allyBuildEnabled; @@ -41,8 +40,8 @@ public class BaseProvider : ITick, INotifyCreated, IRenderAboveShroudWhenSelecte int progress; public BaseProvider(Actor self, BaseProviderInfo info) + : base(info) { - Info = info; this.self = self; devMode = self.Owner.PlayerActor.Trait(); progress = total = info.InitialDelay; @@ -69,7 +68,7 @@ public void BeginCooldown() public bool Ready() { - if (building != null && building.Locked) + if (IsTraitDisabled || IsTraitPaused || (building != null && building.Locked)) return false; return devMode.FastBuild || progress == 0; @@ -82,6 +81,9 @@ bool ValidRenderPlayer() public IEnumerable RangeCircleRenderables(WorldRenderer wr) { + if (IsTraitDisabled) + yield break; + // Visible to player and allies if (!ValidRenderPlayer()) yield break; @@ -103,6 +105,9 @@ IEnumerable IRenderAboveShroudWhenSelected.RenderAboveShroud(Actor float ISelectionBar.GetValue() { + if (IsTraitDisabled) + return 0f; + // Visible to player and allies if (!ValidRenderPlayer()) return 0f; diff --git a/OpenRA.Mods.Common/Traits/Buildings/Building.cs b/OpenRA.Mods.Common/Traits/Buildings/Building.cs index 8e75703d4c2e..c552c9fd3a13 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Building.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Building.cs @@ -143,7 +143,7 @@ public WVec CenterOffset(World w) return (off - new WVec(0, 0, off.Z)) + LocalCenterOffset; } - public Actor FindBaseProvider(World world, Player p, CPos topLeft) + public BaseProvider FindBaseProvider(World world, Player p, CPos topLeft) { var center = world.Map.CenterOfCell(topLeft) + CenterOffset(world); var mapBuildRadius = world.WorldActor.Trait(); @@ -161,7 +161,7 @@ public Actor FindBaseProvider(World world, Player p, CPos topLeft) // Range is counted from the center of the actor, not from each cell. var target = Target.FromPos(bp.Actor.CenterPosition); if (target.IsInRange(center, bp.Trait.Info.Range)) - return bp.Actor; + return bp.Trait; } return null; diff --git a/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs b/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs index d4242b6f43e4..437c0083a316 100644 --- a/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs +++ b/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs @@ -159,7 +159,7 @@ void IResolveOrder.ResolveOrder(Actor self, Order order) // BuildingInfo.IsCloseEnoughToBase has already verified that this is a valid build location var provider = buildingInfo.FindBaseProvider(w, self.Owner, order.TargetLocation); if (provider != null) - provider.Trait().BeginCooldown(); + provider.BeginCooldown(); } if (GetNumBuildables(self.Owner) > prevItems) From b620e8107fb422ca2f327bf621b008a3ca747930 Mon Sep 17 00:00:00 2001 From: Pavel Penev Date: Thu, 19 Oct 2017 14:04:36 +0300 Subject: [PATCH 22/48] Added GrantRandomCondition trait. --- OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 1 + .../Traits/Conditions/GrantRandomCondition.cs | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 OpenRA.Mods.Common/Traits/Conditions/GrantRandomCondition.cs diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index f61fd348f69f..58d2580718a6 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -301,6 +301,7 @@ + diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantRandomCondition.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantRandomCondition.cs new file mode 100644 index 000000000000..f035376f5066 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantRandomCondition.cs @@ -0,0 +1,47 @@ +#region Copyright & License Information +/* + * Copyright 2007-2018 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.Linq; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits.Conditions +{ + [Desc("Grants a random condition from a predefined list to the actor when created.")] + public class GrantRandomConditionInfo : ITraitInfo + { + [FieldLoader.Require] + [GrantedConditionReference] + [Desc("List of conditions to grant from.")] + public readonly string[] Conditions = null; + + public object Create(ActorInitializer init) { return new GrantRandomCondition(init.Self, this); } + } + + public class GrantRandomCondition : INotifyCreated + { + readonly GrantRandomConditionInfo info; + + public GrantRandomCondition(Actor self, GrantRandomConditionInfo info) + { + this.info = info; + } + + void INotifyCreated.Created(Actor self) + { + if (!info.Conditions.Any()) + return; + + var condition = info.Conditions.Random(self.World.SharedRandom); + var conditionManager = self.Trait(); + conditionManager.GrantCondition(self, condition); + } + } +} From 5e7e3bb011e9d714af3ddccba0d2616ca5062054 Mon Sep 17 00:00:00 2001 From: Mustafa Alperen Seki Date: Sat, 3 Feb 2018 11:07:02 +0300 Subject: [PATCH 23/48] Add DamageTypes to Kill() and make some traits use it. --- OpenRA.Game/Actor.cs | 4 ++-- OpenRA.Game/Traits/TraitsInterfaces.cs | 2 +- OpenRA.Mods.Cnc/Activities/Leap.cs | 7 +++++-- OpenRA.Mods.Cnc/Traits/Attack/AttackLeap.cs | 5 ++++- OpenRA.Mods.Cnc/Traits/Chronoshiftable.cs | 7 ++++++- OpenRA.Mods.Cnc/Traits/MadTank.cs | 5 ++++- OpenRA.Mods.Cnc/Traits/Mine.cs | 2 +- OpenRA.Mods.Common/Traits/Attack/AttackSuicides.cs | 7 +++++-- OpenRA.Mods.Common/Traits/Buildings/Bridge.cs | 5 ++++- OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs | 5 ++++- OpenRA.Mods.Common/Traits/Crushable.cs | 3 ++- OpenRA.Mods.Common/Traits/Health.cs | 8 ++++++-- OpenRA.Mods.Common/Traits/KillsSelf.cs | 6 +++++- OpenRA.Mods.Common/Traits/Mobile.cs | 3 +++ OpenRA.Mods.Common/Traits/Parachutable.cs | 5 ++++- OpenRA.Mods.Common/Traits/Player/DeveloperMode.cs | 4 +--- OpenRA.Mods.Common/Warheads/DamageWarhead.cs | 2 +- 17 files changed, 58 insertions(+), 22 deletions(-) diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index 7d25eac80f3b..b0a8d644145c 100644 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -321,12 +321,12 @@ public void InflictDamage(Actor attacker, Damage damage) health.InflictDamage(this, attacker, damage, false); } - public void Kill(Actor attacker) + public void Kill(Actor attacker, HashSet damageTypes = null) { if (Disposed || health == null) return; - health.Kill(this, attacker); + health.Kill(this, attacker, damageTypes); } public bool CanBeViewedByPlayer(Player player) diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 9b2eee1f797a..f46fb5b001f1 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -42,7 +42,7 @@ public interface IHealth bool IsDead { get; } void InflictDamage(Actor self, Actor attacker, Damage damage, bool ignoreModifiers); - void Kill(Actor self, Actor attacker); + void Kill(Actor self, Actor attacker, HashSet damageTypes); } // depends on the order of pips in WorldRenderer.cs! diff --git a/OpenRA.Mods.Cnc/Activities/Leap.cs b/OpenRA.Mods.Cnc/Activities/Leap.cs index 1d7f11e46d44..999cabeff78c 100644 --- a/OpenRA.Mods.Cnc/Activities/Leap.cs +++ b/OpenRA.Mods.Cnc/Activities/Leap.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Generic; using System.Linq; using OpenRA.Activities; using OpenRA.GameRules; @@ -29,8 +30,9 @@ class Leap : Activity WPos to; int ticks; WAngle angle; + HashSet damageTypes; - public Leap(Actor self, Actor target, Armament a, WDist speed, WAngle angle) + public Leap(Actor self, Actor target, Armament a, WDist speed, WAngle angle, HashSet damageTypes) { var targetMobile = target.TraitOrDefault(); if (targetMobile == null) @@ -38,6 +40,7 @@ public Leap(Actor self, Actor target, Armament a, WDist speed, WAngle angle) this.weapon = a.Weapon; this.angle = angle; + this.damageTypes = damageTypes; mobile = self.Trait(); mobile.SetLocation(mobile.FromCell, mobile.FromSubCell, targetMobile.FromCell, targetMobile.FromSubCell); mobile.IsMoving = true; @@ -67,7 +70,7 @@ public override Activity Tick(Actor self) self.World.ActorMap.GetActorsAt(mobile.ToCell, mobile.ToSubCell) .Except(new[] { self }).Where(t => weapon.IsValidAgainst(t, self)) - .Do(t => t.Kill(self)); + .Do(t => t.Kill(self, damageTypes)); return NextActivity; } diff --git a/OpenRA.Mods.Cnc/Traits/Attack/AttackLeap.cs b/OpenRA.Mods.Cnc/Traits/Attack/AttackLeap.cs index a3b1baf0c214..0d86bef57d8d 100644 --- a/OpenRA.Mods.Cnc/Traits/Attack/AttackLeap.cs +++ b/OpenRA.Mods.Cnc/Traits/Attack/AttackLeap.cs @@ -25,6 +25,9 @@ class AttackLeapInfo : AttackFrontalInfo public readonly WAngle Angle = WAngle.FromDegrees(20); + [Desc("Types of damage that this trait causes. Leave empty for no damage types.")] + public readonly HashSet DamageTypes = new HashSet(); + public override object Create(ActorInitializer init) { return new AttackLeap(init.Self, this); } } @@ -51,7 +54,7 @@ public override void DoAttack(Actor self, Target target, IEnumerable a return; self.CancelActivity(); - self.QueueActivity(new Leap(self, target.Actor, a, info.Speed, info.Angle)); + self.QueueActivity(new Leap(self, target.Actor, a, info.Speed, info.Angle, info.DamageTypes)); } } } diff --git a/OpenRA.Mods.Cnc/Traits/Chronoshiftable.cs b/OpenRA.Mods.Cnc/Traits/Chronoshiftable.cs index ba5415c66fa8..d61341682727 100644 --- a/OpenRA.Mods.Cnc/Traits/Chronoshiftable.cs +++ b/OpenRA.Mods.Cnc/Traits/Chronoshiftable.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Generic; using System.Drawing; using OpenRA.Mods.Cnc.Activities; using OpenRA.Mods.Common.Traits; @@ -22,6 +23,10 @@ public class ChronoshiftableInfo : ITraitInfo { [Desc("Should the actor die instead of being teleported?")] public readonly bool ExplodeInstead = false; + + [Desc("Types of damage that this trait causes to self when 'ExplodeInstead' is true. Leave empty for no damage types.")] + public readonly HashSet DamageTypes = new HashSet(); + public readonly string ChronoshiftSound = "chrono2.aud"; [Desc("Should the actor return to its previous location after the chronoshift wore out?")] @@ -95,7 +100,7 @@ public virtual bool Teleport(Actor self, CPos targetLocation, int duration, bool { // Damage is inflicted by the chronosphere if (!self.Disposed) - self.InflictDamage(chronosphere, new Damage(int.MaxValue)); + self.Kill(chronosphere, info.DamageTypes); }); return true; } diff --git a/OpenRA.Mods.Cnc/Traits/MadTank.cs b/OpenRA.Mods.Cnc/Traits/MadTank.cs index ebcaa38e6613..8b534deb2e59 100644 --- a/OpenRA.Mods.Cnc/Traits/MadTank.cs +++ b/OpenRA.Mods.Cnc/Traits/MadTank.cs @@ -53,6 +53,9 @@ class MadTankInfo : ITraitInfo, IRulesetLoaded, Requires, Requires public WeaponInfo ThumpDamageWeaponInfo { get; private set; } public WeaponInfo DetonationWeaponInfo { get; private set; } + [Desc("Types of damage that this trait causes to self while self-destructing. Leave empty for no damage types.")] + public readonly HashSet DamageTypes = new HashSet(); + public object Create(ActorInitializer init) { return new MadTank(init.Self, this); } public void RulesetLoaded(Ruleset rules, ActorInfo ai) { @@ -146,7 +149,7 @@ void Detonate() info.DetonationWeaponInfo.Impact(Target.FromPos(self.CenterPosition), self, Enumerable.Empty()); } - self.Kill(self); + self.Kill(self, info.DamageTypes); }); } diff --git a/OpenRA.Mods.Cnc/Traits/Mine.cs b/OpenRA.Mods.Cnc/Traits/Mine.cs index 8d95b7d0b3dd..07dd13761e2d 100644 --- a/OpenRA.Mods.Cnc/Traits/Mine.cs +++ b/OpenRA.Mods.Cnc/Traits/Mine.cs @@ -48,7 +48,7 @@ void INotifyCrushed.OnCrush(Actor self, Actor crusher, HashSet crushClas if (mobile != null && !info.DetonateClasses.Overlaps(mobile.Info.Crushes)) return; - self.Kill(crusher); + self.Kill(crusher, mobile != null ? mobile.Info.CrushDamageTypes : new HashSet()); } bool ICrushable.CrushableBy(Actor self, Actor crusher, HashSet crushClasses) diff --git a/OpenRA.Mods.Common/Traits/Attack/AttackSuicides.cs b/OpenRA.Mods.Common/Traits/Attack/AttackSuicides.cs index 89c03836bfa7..e5a09c6fef8d 100644 --- a/OpenRA.Mods.Common/Traits/Attack/AttackSuicides.cs +++ b/OpenRA.Mods.Common/Traits/Attack/AttackSuicides.cs @@ -20,6 +20,9 @@ namespace OpenRA.Mods.Common.Traits [Desc("Does a suicide attack where it moves next to the target when used in combination with `Explodes`.")] class AttackSuicidesInfo : ConditionalTraitInfo, Requires { + [Desc("Types of damage that this trait causes to self while suiciding. Leave empty for no damage types.")] + public readonly HashSet DamageTypes = new HashSet(); + [VoiceReference] public readonly string Voice = "Action"; public override object Create(ActorInitializer init) { return new AttackSuicides(init.Self, this); } @@ -82,10 +85,10 @@ public void ResolveOrder(Actor self, Order order) self.QueueActivity(move.MoveToTarget(self, target)); - self.QueueActivity(new CallFunc(() => self.Kill(self))); + self.QueueActivity(new CallFunc(() => self.Kill(self, Info.DamageTypes))); } else if (order.OrderString == "Detonate") - self.Kill(self); + self.Kill(self, Info.DamageTypes); } } } diff --git a/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs b/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs index f4b29ad4deeb..111b236dfdec 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs @@ -46,6 +46,9 @@ class BridgeInfo : ITraitInfo, IRulesetLoaded, Requires, Requires DamageTypes = new HashSet(); + public object Create(ActorInitializer init) { return new Bridge(init.Self, this); } public void RulesetLoaded(Ruleset rules, ActorInfo ai) @@ -241,7 +244,7 @@ void KillUnitsOnBridge() foreach (var c in footprint.Keys) foreach (var a in self.World.ActorMap.GetActorsAt(c)) if (a.Info.HasTraitInfo() && !a.Trait().CanEnterCell(c)) - a.Kill(self); + a.Kill(self, info.DamageTypes); } bool NeighbourIsDeadShore(Bridge neighbour) diff --git a/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs b/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs index 32ec2daaf597..75c4e7332e00 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs @@ -30,6 +30,9 @@ class GroundLevelBridgeInfo : ITraitInfo, IRulesetLoaded, Requires public WeaponInfo DemolishWeaponInfo { get; private set; } + [Desc("Types of damage that this bridge causes to units over/in path of it while being destroyed/repaired. Leave empty for no damage types.")] + public readonly HashSet DamageTypes = new HashSet(); + public void RulesetLoaded(Ruleset rules, ActorInfo ai) { WeaponInfo weapon; @@ -95,7 +98,7 @@ void KillInvalidActorsInFootprint(Actor self) foreach (var c in cells) foreach (var a in self.World.ActorMap.GetActorsAt(c)) if (a.Info.HasTraitInfo() && !a.Trait().CanEnterCell(c)) - a.Kill(self); + a.Kill(self, Info.DamageTypes); } void IBridgeSegment.Repair(Actor repairer) diff --git a/OpenRA.Mods.Common/Traits/Crushable.cs b/OpenRA.Mods.Common/Traits/Crushable.cs index 29bd75fb74e9..49cf688d2d1f 100644 --- a/OpenRA.Mods.Common/Traits/Crushable.cs +++ b/OpenRA.Mods.Common/Traits/Crushable.cs @@ -57,7 +57,8 @@ void INotifyCrushed.OnCrush(Actor self, Actor crusher, HashSet crushClas Game.Sound.Play(SoundType.World, Info.CrushSound, crusher.CenterPosition); - self.Kill(crusher); + var crusherMobile = crusher.TraitOrDefault(); + self.Kill(crusher, crusherMobile != null ? crusherMobile.Info.CrushDamageTypes : new HashSet()); } bool ICrushable.CrushableBy(Actor self, Actor crusher, HashSet crushClasses) diff --git a/OpenRA.Mods.Common/Traits/Health.cs b/OpenRA.Mods.Common/Traits/Health.cs index 0d82b3edf547..dfce76a6412d 100644 --- a/OpenRA.Mods.Common/Traits/Health.cs +++ b/OpenRA.Mods.Common/Traits/Health.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Generic; using System.Linq; using OpenRA.Traits; @@ -163,9 +164,12 @@ public void InflictDamage(Actor self, Actor attacker, Damage damage, bool ignore } } - public void Kill(Actor self, Actor attacker) + public void Kill(Actor self, Actor attacker, HashSet damageTypes = null) { - InflictDamage(self, attacker, new Damage(MaxHP), true); + if (damageTypes == null) + damageTypes = new HashSet(); + + InflictDamage(self, attacker, new Damage(MaxHP, damageTypes), true); } void ITick.Tick(Actor self) diff --git a/OpenRA.Mods.Common/Traits/KillsSelf.cs b/OpenRA.Mods.Common/Traits/KillsSelf.cs index a997486d1cb1..687ad589b35b 100644 --- a/OpenRA.Mods.Common/Traits/KillsSelf.cs +++ b/OpenRA.Mods.Common/Traits/KillsSelf.cs @@ -9,6 +9,7 @@ */ #endregion +using System.Collections.Generic; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -21,6 +22,9 @@ class KillsSelfInfo : ConditionalTraitInfo [Desc("The amount of time (in ticks) before the actor dies. Two values indicate a range between which a random value is chosen.")] public readonly int[] Delay = { 0 }; + [Desc("Types of damage that this trait causes. Leave empty for no damage types.")] + public readonly HashSet DamageTypes = new HashSet(); + [GrantedConditionReference] [Desc("The condition to grant moments before suiciding.")] public readonly string GrantsCondition = null; @@ -81,7 +85,7 @@ void Kill(Actor self) if (Info.RemoveInstead || !self.Info.HasTraitInfo()) self.Dispose(); else - self.Kill(self); + self.Kill(self, Info.DamageTypes); } } } diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index f3a82e93b4da..8401a26639bd 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -59,6 +59,9 @@ public class MobileInfo : ConditionalTraitInfo, IMoveInfo, IPositionableInfo, IF [Desc("e.g. crate, wall, infantry")] public readonly HashSet Crushes = new HashSet(); + [Desc("Types of damage that are caused while crushing. Leave empty for no damage types.")] + public readonly HashSet CrushDamageTypes = new HashSet(); + public readonly int WaitAverage = 5; public readonly int WaitSpread = 2; diff --git a/OpenRA.Mods.Common/Traits/Parachutable.cs b/OpenRA.Mods.Common/Traits/Parachutable.cs index fdcdf9f4260d..c86bab703886 100644 --- a/OpenRA.Mods.Common/Traits/Parachutable.cs +++ b/OpenRA.Mods.Common/Traits/Parachutable.cs @@ -22,6 +22,9 @@ public class ParachutableInfo : ITraitInfo, Requires [Desc("If we land on invalid terrain for my actor type should we be killed?")] public readonly bool KilledOnImpassableTerrain = true; + [Desc("Types of damage that this trait causes to self when 'KilledOnImpassableTerrain' is true. Leave empty for no damage types.")] + public readonly HashSet DamageTypes = new HashSet(); + [Desc("Image where Ground/WaterCorpseSequence is looked up.")] public readonly string Image = "explosion"; @@ -101,7 +104,7 @@ void INotifyParachute.OnLanded(Actor self, Actor ignore) if (sequence != null && palette != null) self.World.AddFrameEndTask(w => w.Add(new SpriteEffect(self.OccupiesSpace.CenterPosition, w, info.Image, sequence, palette))); - self.Kill(self); + self.Kill(self, info.DamageTypes); } } } diff --git a/OpenRA.Mods.Common/Traits/Player/DeveloperMode.cs b/OpenRA.Mods.Common/Traits/Player/DeveloperMode.cs index 48309818556b..176c1f9e90bf 100644 --- a/OpenRA.Mods.Common/Traits/Player/DeveloperMode.cs +++ b/OpenRA.Mods.Common/Traits/Player/DeveloperMode.cs @@ -219,15 +219,13 @@ public void ResolveOrder(Actor self, Order order) break; var actor = order.Target.Actor; - var health = actor.TraitOrDefault(); var args = order.TargetString.Split(' '); var damageTypes = new HashSet(); foreach (var damageType in args) damageTypes.Add(damageType); - if (health != null) - health.InflictDamage(actor, actor, new Damage(health.HP, damageTypes), true); + actor.Kill(actor, damageTypes); break; } diff --git a/OpenRA.Mods.Common/Warheads/DamageWarhead.cs b/OpenRA.Mods.Common/Warheads/DamageWarhead.cs index 4077e89e917c..9ac14f15a220 100644 --- a/OpenRA.Mods.Common/Warheads/DamageWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/DamageWarhead.cs @@ -21,7 +21,7 @@ public abstract class DamageWarhead : Warhead [Desc("How much (raw) damage to deal.")] public readonly int Damage = 0; - [Desc("Types of damage that this warhead causes. Leave empty for no damage.")] + [Desc("Types of damage that this warhead causes. Leave empty for no damage types.")] public readonly HashSet DamageTypes = new HashSet(); [Desc("Damage percentage versus each armortype.")] From 58b5e7b2436bc826d2b4857507efe94ced493c46 Mon Sep 17 00:00:00 2001 From: netnazgul Date: Thu, 8 Mar 2018 18:00:03 +0300 Subject: [PATCH 24/48] Fix Pitfight and Ascent maps in RA mod map pool --- mods/ra/maps/ascent.oramap | Bin 0 -> 16479 bytes mods/ra/maps/pitfight.oramap | Bin 13158 -> 13139 bytes mods/ra/maps/volcano.oramap | Bin 18765 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 mods/ra/maps/ascent.oramap delete mode 100644 mods/ra/maps/volcano.oramap diff --git a/mods/ra/maps/ascent.oramap b/mods/ra/maps/ascent.oramap new file mode 100644 index 0000000000000000000000000000000000000000..cbfa2cddd9ef802e18f5b05fc9001175cdfbcdcb GIT binary patch literal 16479 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 literal 0 HcmV?d00001 diff --git a/mods/ra/maps/pitfight.oramap b/mods/ra/maps/pitfight.oramap index c4fa851b90e2ff848e23009054602b763febbfcb..28b108a8f80e4aad4da5f2f4d979c005baa90463 100644 GIT binary patch delta 3039 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 delta 3061 zcmV@6aWAK2mp1XL9q=~27h&guk(?bG2z@I(Si(Bav;aS{u!z4($S{QA>9V^>uG z(H}@iWV7>4bzQpdlUM8K?+?eP_4)peXtjE?`R%ejZceWb@9yu8>(%P#?RmGkzx!!> zet*CH@bSD_eS0~7JRI-ueqH~5`KJGVw>fPZ`t9o)vjEZ-DBj>Q}4(9xnTL zr}plNrs~>%tJVLur|rXbw>^Kkzx(0v@bG1|dbvJtJ{*qQO?&d?VgK7@b83`++O5BA zj;H(8i{(#mHt#pb&Hi!opVynq`MBP--+#P#y?$!cu7A;f{_*qv^vmJ6Ysdfjc-izP z4*Qpfr-$`{C}kKrja;2St7UN{pHK)4=<;G_wv=ZkLSazkbn6P ze1CU-bhHS4bF^Cc;qbU#UKaC2*XPMM0J*{QWpcOY!%V>cJs`!2{0*@jDGU+y28c6& z2Y@iq?NHuBh$Dne9np6=$L;&VU33$G$7M5&+hcc48T?*(91W_T+?Aah7mw(fuv1qP(APJ#p zd{hkp!heOBRWYy*uZb7c1OT$BnjMlcd-cuUAt|ue+|IiQWJV336cPgPYU18iAiX1# zGy6ID8X+3nQp!_>!fKpyl7@tXicUA>ED$L9YBgR_Drg&{Xs0Bh5NS+!D35F)f60OQy;5OFV$BPwjWJe5 zH1@^#8M3h+LqsnO37L%{bH(z|B9}N~7B&?TSA>d!7Cu#6Slgvl9tufN(q4^yCB`ZV zgTTt0sfcwYtbb(Go}j$F6_2X+1eJEpQq+VKRCcxKp2ZtwD1W?DF+ML7Sb8Zh8B$6z zBF+#iB^etoSfi1R>I>Rx)Voy}>>cV=DUc>}Fq$kCr6ML)A@)-??z=)(nX2KXLcAoe z0WUbL?;Vx>;0SESXQjemo7o%;YY-Z*7aWv7H(nG%4uNFS=oJX6z>71yTn9-sW?!j9 z2vRL1fe_WpwSR3kZk-!mpn5m65wZ{97()-{861_!eF+{a-P^5{02X{k$8|D@@(}yo z^$9Nl8#fen7a?Gi$84!Ys6B2nq!6I7MmA~*NEP3;!h%318z~q)GE%7}HmBl%@gX5E z%vsy0LhXftB9K;pcgAw6h_tx+$Wm-;ZU+M62X)2saDR>b*hz?VBQzlhmX8D=vI#>W zVtou7AYz4@>*iS4)XzlvfOQaiY9#j%>marSOfOY}JQpQPrGR}(y(HIUa@?m>NJd6I zz37sFvP=!f4GL9b2Ad`Zfea4G6r5q691cf~=2~T_IZZ|(88ToSua@THczj_H3?Q7Q z!(nMx=6_+KDS}%1m3i2PGl+uqjdKtLbZ=YwB(j}bmcx#ei6C?84D{Fa3eFwNA%e9! z33JDCc$;jlIge(6X@n|#ZdndH+SaIQ1rTJMB0H#S9xP&(E)A`U1rPuL>3!un_a8Q# zO)A6Kys;%rNp^7xvf9;GK!lIw#=7Ck1!qAdAb;3aXK7?4Jo7{%tYxEshNtqfw^R|8 z_hl{NW4TaySikrr2#+A@M54mwLX8ng5sYyzh!`B^*JBa{GG-u}LKZ|tf@ahCEojEM zQ#l-dOi4)0vr`%;OTh`E0A|%ZVkojw~hw2l=!*=&D%y*NlZS~kchP0H#%dzI* zKYzM|ExYm}-6i9wS=liPG8Zj%y5w$$$2BAu4?*QoDhWiS+@994tc+YcW1UGPY*v z`;GiC#x!eTRb|6`8YUgA4$RjXF|koiTNtdjlq?JuNvl&t z0oH#^u$vau2-*h&f;c7^87^{ zjhE(S(!$s$aX#r#gXb~ksFr9op42N$(4te)4Rxjp&ela(^IjZ{2U$S?1hj-3K=|$% z5|k%+()yOMO}dukN715(hKoK}D#*61XG?^UB~YieJ!9Vyw%ac)2lmJ(IISkq=GS#u zZ~PUgbSc;)_Sk%xA>x3(Mt{nN5yJMhicS!C9z>6L4`yV>JrNzJu2mCi?&Zq+$NB5s zzwi7hq<7`U5Zj5xGu_^f6$9&2^^b?s^Ozk8gni)s7>22PkJB&SULIaF^Cf$N`ord`&WjgAKVsHc zIW6NLOafV9%Um6SnWir%*dqEGk(ghCO(NTrc=@6nad+8Lo0mi&V^3r!?gf=3nGN4C zued^o>^sh)fMqWQausGRiRZ84AW61#SqI)3_GU@f9Qe9ajeo$rF6r zM}nC4byWcO$U)aoc=-$t@hr_#if*vQ5+@&?am~<(nncjIy>cj@@p<)sP)h>@6aWAK z2mp1XK}__gh_km5@*o3sqd}9bGBOQyqd`pLsBe}$3jhGrlj|}mKXs!)O!TLSVbUM~ z0AZy700#g700000000000002F5dZ*fVQ?;DX>L$U1qJ{B000930|1}^006Ku00000 D>0F!w diff --git a/mods/ra/maps/volcano.oramap b/mods/ra/maps/volcano.oramap deleted file mode 100644 index 1bd8a8f6649816e978d7b16440d0ae0bf6d293ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18765 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 VwpRbzt3Bd5u&ct|`p#hzddC0& From d643d2ebda5bf4396788695d8fcc0922f6ca5957 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sun, 10 Sep 2017 12:45:49 +0200 Subject: [PATCH 25/48] Remove legacy .aud sound defaults from Common traits While C&C-specific sound defaults might be acceptable for C&C-specific traits like MadTank and Chronoshiftable, for common, generic traits like Building they no longer are. --- .../Traits/Buildings/Building.cs | 4 +-- OpenRA.Mods.Common/Traits/EjectOnDeath.cs | 2 +- OpenRA.Mods.Common/Traits/ParaDrop.cs | 2 +- .../Traits/ProductionParadrop.cs | 2 +- .../UtilityCommands/UpgradeRules.cs | 35 +++++++++++++++++++ 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Buildings/Building.cs b/OpenRA.Mods.Common/Traits/Buildings/Building.cs index c552c9fd3a13..37d46bc8c3ad 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Building.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Building.cs @@ -55,9 +55,9 @@ public class BuildingInfo : ITraitInfo, IOccupySpaceInfo, IPlaceBuildingDecorati [Desc("Clear smudges from underneath the building footprint on transform.")] public readonly bool RemoveSmudgesOnTransform = true; - public readonly string[] BuildSounds = { "placbldg.aud", "build5.aud" }; + public readonly string[] BuildSounds = { }; - public readonly string[] UndeploySounds = { "cashturn.aud" }; + public readonly string[] UndeploySounds = { }; public virtual object Create(ActorInitializer init) { return new Building(init, this); } diff --git a/OpenRA.Mods.Common/Traits/EjectOnDeath.cs b/OpenRA.Mods.Common/Traits/EjectOnDeath.cs index c04def1660fd..5216de1768ad 100644 --- a/OpenRA.Mods.Common/Traits/EjectOnDeath.cs +++ b/OpenRA.Mods.Common/Traits/EjectOnDeath.cs @@ -26,7 +26,7 @@ public class EjectOnDeathInfo : ITraitInfo public readonly int SuccessRate = 50; [Desc("Sound to play when ejecting the pilot from the aircraft.")] - public readonly string ChuteSound = "chute1.aud"; + public readonly string ChuteSound = null; [Desc("Can a destroyed aircraft eject its pilot while it has not yet fallen to ground level?")] public readonly bool EjectInAir = false; diff --git a/OpenRA.Mods.Common/Traits/ParaDrop.cs b/OpenRA.Mods.Common/Traits/ParaDrop.cs index 7a43b5097990..2d22c56e9de6 100644 --- a/OpenRA.Mods.Common/Traits/ParaDrop.cs +++ b/OpenRA.Mods.Common/Traits/ParaDrop.cs @@ -23,7 +23,7 @@ public class ParaDropInfo : ITraitInfo, Requires public readonly WDist DropRange = WDist.FromCells(4); [Desc("Sound to play when dropping.")] - public readonly string ChuteSound = "chute1.aud"; + public readonly string ChuteSound = null; public object Create(ActorInitializer init) { return new ParaDrop(init.Self, this); } } diff --git a/OpenRA.Mods.Common/Traits/ProductionParadrop.cs b/OpenRA.Mods.Common/Traits/ProductionParadrop.cs index c016098d7413..cd3a760858da 100644 --- a/OpenRA.Mods.Common/Traits/ProductionParadrop.cs +++ b/OpenRA.Mods.Common/Traits/ProductionParadrop.cs @@ -25,7 +25,7 @@ public class ProductionParadropInfo : ProductionInfo, Requires [ActorReference(typeof(AircraftInfo))] public readonly string ActorType = "badr"; [Desc("Sound to play when dropping the unit.")] - public readonly string ChuteSound = "chute1.aud"; + public readonly string ChuteSound = null; [Desc("Notification to play when dropping the unit.")] public readonly string ReadyAudio = null; diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index b86d39291a92..12b629b9221c 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -1674,6 +1674,41 @@ internal static void UpgradeActorRules(ModData modData, int engineVersion, ref L } } + if (engineVersion < 20180309) + { + if (node.Key == "ParaDrop") + { + var soundNodePD = node.Value.Nodes.FirstOrDefault(n => n.Key == "ChuteSound"); + if (soundNodePD == null) + node.Value.Nodes.Add(new MiniYamlNode("ChuteSound", "chute1.aud")); + } + + if (depth == 1 && node.Key == "EjectOnDeath") + { + var soundNodeEOD = node.Value.Nodes.FirstOrDefault(n => n.Key == "ChuteSound"); + if (soundNodeEOD == null) + node.Value.Nodes.Add(new MiniYamlNode("ChuteSound", "chute1.aud")); + } + + if (node.Key.StartsWith("ProductionParadrop", StringComparison.Ordinal)) + { + var soundNodePP = node.Value.Nodes.FirstOrDefault(n => n.Key == "ChuteSound"); + if (soundNodePP == null) + node.Value.Nodes.Add(new MiniYamlNode("ChuteSound", "chute1.aud")); + } + + if (node.Key == "Building") + { + var soundNodeB1 = node.Value.Nodes.FirstOrDefault(n => n.Key == "BuildSounds"); + if (soundNodeB1 == null) + node.Value.Nodes.Add(new MiniYamlNode("BuildSounds", "placbldg.aud, build5.aud")); + + var soundNodeB2 = node.Value.Nodes.FirstOrDefault(n => n.Key == "UndeploySounds"); + if (soundNodeB2 == null) + node.Value.Nodes.Add(new MiniYamlNode("UndeploySounds", "cashturn.aud")); + } + } + UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); } From b40970ca5de2ec1ee99896c47e178bd1f736b91c Mon Sep 17 00:00:00 2001 From: reaperrr Date: Fri, 9 Mar 2018 01:52:47 +0100 Subject: [PATCH 26/48] Adapt RA rules --- mods/ra/maps/bomber-john/rules.yaml | 2 ++ mods/ra/rules/aircraft.yaml | 1 + mods/ra/rules/defaults.yaml | 5 +++++ 3 files changed, 8 insertions(+) diff --git a/mods/ra/maps/bomber-john/rules.yaml b/mods/ra/maps/bomber-john/rules.yaml index 57038e35a015..9f5ff780d062 100644 --- a/mods/ra/maps/bomber-john/rules.yaml +++ b/mods/ra/maps/bomber-john/rules.yaml @@ -145,6 +145,8 @@ MINVV: HiddenUnderFog: Building: TerrainTypes: Clear,Road + BuildSounds: placbldg.aud, build5.aud + UndeploySounds: cashturn.aud Buildable: Queue: Building BuildPaletteOrder: 10 diff --git a/mods/ra/rules/aircraft.yaml b/mods/ra/rules/aircraft.yaml index 698f00088c26..c21691b16109 100644 --- a/mods/ra/rules/aircraft.yaml +++ b/mods/ra/rules/aircraft.yaml @@ -2,6 +2,7 @@ BADR: Inherits: ^NeutralPlane ParaDrop: DropRange: 4c0 + ChuteSound: chute1.aud Health: HP: 30000 Armor: diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index fe91aceb526a..7a4b85592b45 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -531,6 +531,7 @@ EjectOnGround: false EjectInAir: true AllowUnsuitableCell: true + ChuteSound: chute1.aud GpsDot: String: Plane Tooltip: @@ -591,6 +592,8 @@ Footprint: x TerrainTypes: Clear,Road RequiresBaseProvider: True + BuildSounds: placbldg.aud, build5.aud + UndeploySounds: cashturn.aud RequiresBuildableArea: AreaTypes: building SoundOnDamageTransition: @@ -671,6 +674,7 @@ Footprint: x BuildSounds: placbldg.aud TerrainTypes: Clear,Road + UndeploySounds: cashturn.aud RequiresBuildableArea: AreaTypes: building Adjacent: 7 @@ -717,6 +721,7 @@ Building: BuildSounds: place2.aud TerrainTypes: Clear, Road + UndeploySounds: cashturn.aud RequiresBuildableArea: AreaTypes: building Adjacent: 4 From e17e8c8cbd0e0cfce3298ab526826a1d52c4e841 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Fri, 9 Mar 2018 02:02:56 +0100 Subject: [PATCH 27/48] Adapt TD --- mods/cnc/maps/nod07c/rules.yaml | 2 ++ mods/cnc/rules/defaults.yaml | 1 + 2 files changed, 3 insertions(+) diff --git a/mods/cnc/maps/nod07c/rules.yaml b/mods/cnc/maps/nod07c/rules.yaml index 290a38c9bea6..768af461cc62 100644 --- a/mods/cnc/maps/nod07c/rules.yaml +++ b/mods/cnc/maps/nod07c/rules.yaml @@ -141,6 +141,8 @@ HPAD.IN: CaptureThreshold: 100 Building: Footprint: x_ xx + BuildSounds: placbldg.aud, build5.aud + UndeploySounds: cashturn.aud -Sellable: -Power: diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index 0500ca6b8c10..05ce44f87db0 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -700,6 +700,7 @@ Building: RequiresBaseProvider: true BuildSounds: constru2.aud, hvydoor1.aud + UndeploySounds: cashturn.aud TerrainTypes: Clear,Road RequiresBuildableArea: AreaTypes: building From bd097730cf0f47f227be58ab91b8d275ae823553 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Fri, 9 Mar 2018 02:14:00 +0100 Subject: [PATCH 28/48] Adapt TS --- mods/ts/rules/defaults.yaml | 3 +++ mods/ts/rules/gdi-support.yaml | 1 - mods/ts/rules/nod-support.yaml | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index b3bec4629d45..c0e6d51bd14b 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -306,6 +306,7 @@ Footprint: x BuildSounds: place2.aud TerrainTypes: Clear, Rough, Road, DirtRoad, Green, Sand, Pavement + UndeploySounds: cashturn.aud RequiresBuildableArea: AreaTypes: building Adjacent: 4 @@ -442,6 +443,7 @@ Footprint: x BuildSounds: place2.aud TerrainTypes: Clear, Rough, Road, DirtRoad, Green, Sand, Pavement + UndeploySounds: cashturn.aud RequiresBuildableArea: AreaTypes: building Adjacent: 7 @@ -485,6 +487,7 @@ AlwaysVisible: Building: BuildSounds: place2.aud + UndeploySounds: cashturn.aud KillsSelf: RemoveInstead: true RenderSprites: diff --git a/mods/ts/rules/gdi-support.yaml b/mods/ts/rules/gdi-support.yaml index 74f9d69e6320..1cdc42fe423e 100644 --- a/mods/ts/rules/gdi-support.yaml +++ b/mods/ts/rules/gdi-support.yaml @@ -51,7 +51,6 @@ GACTWR: BuildPaletteOrder: 70 Prerequisites: gapile, ~structures.gdi, ~techlevel.low Description: Modular tower for base defenses. - Building: Selectable: Bounds: 48, 36, 0, -6 DecorationBounds: 48, 48, 0, -12 diff --git a/mods/ts/rules/nod-support.yaml b/mods/ts/rules/nod-support.yaml index 1ceab0fa38ed..ea2c90f51ba7 100644 --- a/mods/ts/rules/nod-support.yaml +++ b/mods/ts/rules/nod-support.yaml @@ -142,7 +142,6 @@ NALASR: Prerequisites: nahand, ~structures.nod, ~techlevel.low BuildPaletteOrder: 90 Description: Basic base defense.\nRequires power to operate.\n Strong vs Ground units\n Weak vs Aircraft - Building: Selectable: Bounds: 40, 30, -8, -6 DecorationBounds: 40, 36, -8, -8 From a1ad76ba74841d102d5774e072dcbe32ce927a51 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sat, 16 Sep 2017 20:30:22 +0200 Subject: [PATCH 29/48] Split *AimAnimation from WithTurretAttackAnimation These two didn't interact and actually even conflicted when used at the same time, so splitting them is the sensible thing to do. --- OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 1 + .../Traits/Render/WithTurretAimAnimation.cs | 68 +++++++++++++++++++ .../Render/WithTurretAttackAnimation.cs | 39 +++-------- .../UtilityCommands/UpgradeRules.cs | 47 +++++++++++++ 4 files changed, 126 insertions(+), 29 deletions(-) create mode 100644 OpenRA.Mods.Common/Traits/Render/WithTurretAimAnimation.cs diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 58d2580718a6..54abd31fa387 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -423,6 +423,7 @@ + diff --git a/OpenRA.Mods.Common/Traits/Render/WithTurretAimAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithTurretAimAnimation.cs new file mode 100644 index 000000000000..5abe65824803 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Render/WithTurretAimAnimation.cs @@ -0,0 +1,68 @@ +#region Copyright & License Information +/* + * Copyright 2007-2018 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.Linq; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits.Render +{ + public class WithTurretAimAnimationInfo : ITraitInfo, Requires, Requires, Requires + { + [Desc("Armament name")] + public readonly string Armament = "primary"; + + [Desc("Turret name")] + public readonly string Turret = "primary"; + + [Desc("Displayed while targeting.")] + [SequenceReference] public readonly string Sequence = null; + + [Desc("Shown while reloading.")] + [SequenceReference(null, true)] public readonly string ReloadPrefix = null; + + public object Create(ActorInitializer init) { return new WithTurretAimAnimation(init, this); } + } + + public class WithTurretAimAnimation : ITick + { + readonly WithTurretAimAnimationInfo info; + readonly AttackBase attack; + readonly Armament armament; + readonly WithSpriteTurret wst; + + public WithTurretAimAnimation(ActorInitializer init, WithTurretAimAnimationInfo info) + { + this.info = info; + attack = init.Self.Trait(); + armament = init.Self.TraitsImplementing() + .Single(a => a.Info.Name == info.Armament); + wst = init.Self.TraitsImplementing() + .Single(st => st.Info.Turret == info.Turret); + } + + void ITick.Tick(Actor self) + { + if (string.IsNullOrEmpty(info.Sequence) && string.IsNullOrEmpty(info.ReloadPrefix)) + return; + + var sequence = wst.Info.Sequence; + if (!string.IsNullOrEmpty(info.Sequence) && attack.IsAiming) + sequence = info.Sequence; + + var prefix = (armament.IsReloading && !string.IsNullOrEmpty(info.ReloadPrefix)) ? info.ReloadPrefix : ""; + + if (!string.IsNullOrEmpty(prefix) && sequence != (prefix + sequence)) + sequence = prefix + sequence; + + wst.DefaultAnimation.ReplaceAnim(sequence); + } + } +} diff --git a/OpenRA.Mods.Common/Traits/Render/WithTurretAttackAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithTurretAttackAnimation.cs index 16ca34ba87f6..1a09ac7aeb5c 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithTurretAttackAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithTurretAttackAnimation.cs @@ -14,7 +14,7 @@ namespace OpenRA.Mods.Common.Traits.Render { - public class WithTurretAttackAnimationInfo : ITraitInfo, Requires, Requires, Requires + public class WithTurretAttackAnimationInfo : ITraitInfo, Requires, Requires { [Desc("Armament name")] public readonly string Armament = "primary"; @@ -23,13 +23,7 @@ public class WithTurretAttackAnimationInfo : ITraitInfo, Requires(); - armament = init.Self.TraitsImplementing() - .Single(a => a.Info.Name == info.Armament); wst = init.Self.TraitsImplementing() .Single(st => st.Info.Turret == info.Turret); } void PlayAttackAnimation(Actor self) { - if (!string.IsNullOrEmpty(info.AttackSequence)) - wst.PlayCustomAnimation(self, info.AttackSequence); + if (!string.IsNullOrEmpty(info.Sequence)) + wst.PlayCustomAnimation(self, info.Sequence); } void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel) { + if (a.Info.Name != info.Armament) + return; + if (info.DelayRelativeTo == AttackDelayType.Attack) { if (info.Delay > 0) @@ -78,6 +70,9 @@ void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barre void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel) { + if (a.Info.Name != info.Armament) + return; + if (info.DelayRelativeTo == AttackDelayType.Preparation) { if (info.Delay > 0) @@ -91,20 +86,6 @@ void ITick.Tick(Actor self) { if (info.Delay > 0 && --tick == 0) PlayAttackAnimation(self); - - if (string.IsNullOrEmpty(info.AimSequence) && string.IsNullOrEmpty(info.ReloadPrefix)) - return; - - var sequence = wst.Info.Sequence; - if (!string.IsNullOrEmpty(info.AimSequence) && attack.IsAiming) - sequence = info.AimSequence; - - var prefix = (armament.IsReloading && !string.IsNullOrEmpty(info.ReloadPrefix)) ? info.ReloadPrefix : ""; - - if (!string.IsNullOrEmpty(prefix) && sequence != (prefix + sequence)) - sequence = prefix + sequence; - - wst.DefaultAnimation.ReplaceAnim(sequence); } } } diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 12b629b9221c..2b5decb0b4c5 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -1709,6 +1709,53 @@ internal static void UpgradeActorRules(ModData modData, int engineVersion, ref L } } + // Split aim animation logic from WithTurretAttackAnimation to separate WithTurretAimAnimation + if (engineVersion < 20180223) + { + var turAttackAnim = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("WithTurretAttackAnimation", StringComparison.Ordinal)); + if (turAttackAnim != null) + { + var atkSequence = turAttackAnim.Value.Nodes.FirstOrDefault(n => n.Key == "AttackSequence"); + var aimSequence = turAttackAnim.Value.Nodes.FirstOrDefault(n => n.Key == "AimSequence"); + + // If only AimSequence is null, just rename AttackSequence to Sequence (ReloadPrefix is very unlikely to be defined in that case). + // If only AttackSequence is null, just rename the trait and property (the delay properties will likely be undefined). + // If both aren't null, split/copy everything relevant to the new WithTurretAimAnimation. + // If both are null (extremely unlikely), do nothing. + if (atkSequence == null && aimSequence != null) + { + RenameNodeKey(turAttackAnim, "WithTurretAimAnimation"); + RenameNodeKey(aimSequence, "Sequence"); + } + else if (atkSequence != null && aimSequence == null) + RenameNodeKey(atkSequence, "Sequence"); + else if (atkSequence != null && aimSequence != null) + { + var aimAnim = new MiniYamlNode("WithTurretAimAnimation", ""); + RenameNodeKey(aimSequence, "Sequence"); + aimAnim.Value.Nodes.Add(aimSequence); + turAttackAnim.Value.Nodes.Remove(aimSequence); + + var relPrefix = turAttackAnim.Value.Nodes.FirstOrDefault(n => n.Key == "ReloadPrefix"); + var turr = turAttackAnim.Value.Nodes.FirstOrDefault(n => n.Key == "Turret"); + var arm = turAttackAnim.Value.Nodes.FirstOrDefault(n => n.Key == "Armament"); + if (relPrefix != null) + { + aimAnim.Value.Nodes.Add(relPrefix); + turAttackAnim.Value.Nodes.Remove(relPrefix); + } + + if (turr != null) + aimAnim.Value.Nodes.Add(turr); + if (arm != null) + aimAnim.Value.Nodes.Add(arm); + + RenameNodeKey(atkSequence, "Sequence"); + node.Value.Nodes.Add(aimAnim); + } + } + } + UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); } From 79c78090d109898b2eff9e5d3c728168646ace35 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sat, 16 Sep 2017 22:31:06 +0200 Subject: [PATCH 30/48] Remove WithSpriteTurret.AimSequence We can now use WithTurretAimAnimation instead. --- .../Traits/Render/WithSpriteTurret.cs | 22 +------------------ .../UtilityCommands/UpgradeRules.cs | 18 +++++++++++++++ mods/cnc/rules/vehicles.yaml | 4 +++- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Render/WithSpriteTurret.cs b/OpenRA.Mods.Common/Traits/Render/WithSpriteTurret.cs index 0acd2cc27858..924a7692c8db 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithSpriteTurret.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithSpriteTurret.cs @@ -25,9 +25,6 @@ public class WithSpriteTurretInfo : ConditionalTraitInfo, IRenderActorPreviewSpr [Desc("Sequence name to use")] [SequenceReference] public readonly string Sequence = "turret"; - [Desc("Sequence name to use when prepared to fire")] - [SequenceReference] public readonly string AimSequence = null; - [Desc("Turreted 'Turret' key to display")] public readonly string Turret = "primary"; @@ -62,10 +59,9 @@ public IEnumerable RenderPreviewSprites(ActorPreviewInitializer i } } - public class WithSpriteTurret : ConditionalTrait, INotifyBuildComplete, INotifySold, INotifyTransform, ITick, INotifyDamageStateChanged + public class WithSpriteTurret : ConditionalTrait, INotifyBuildComplete, INotifySold, INotifyTransform, INotifyDamageStateChanged { public readonly Animation DefaultAnimation; - protected readonly AttackBase Attack; readonly RenderSprites rs; readonly BodyOrientation body; readonly Turreted t; @@ -79,7 +75,6 @@ public WithSpriteTurret(Actor self, WithSpriteTurretInfo info) { rs = self.Trait(); body = self.Trait(); - Attack = self.TraitOrDefault(); t = self.TraitsImplementing() .First(tt => tt.Name == info.Turret); arms = self.TraitsImplementing() @@ -119,26 +114,11 @@ protected virtual void DamageStateChanged(Actor self) DefaultAnimation.ReplaceAnim(NormalizeSequence(self, DefaultAnimation.CurrentSequence.Name)); } - protected virtual void Tick(Actor self) - { - if (Info.AimSequence == null) - return; - - var sequence = Attack.IsAiming ? Info.AimSequence : Info.Sequence; - DefaultAnimation.ReplaceAnim(sequence); - } - void INotifyDamageStateChanged.DamageStateChanged(Actor self, AttackInfo e) { DamageStateChanged(self); } - void ITick.Tick(Actor self) - { - // Split into a protected method to allow subclassing - Tick(self); - } - public void PlayCustomAnimation(Actor self, string name, Action after = null) { DefaultAnimation.PlayThen(NormalizeSequence(self, name), () => diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 2b5decb0b4c5..541304be4ad5 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -1756,6 +1756,24 @@ internal static void UpgradeActorRules(ModData modData, int engineVersion, ref L } } + // Removed AimSequence from WithSpriteTurret, use WithTurretAimAnimation instead + if (engineVersion < 20180224) + { + var spriteTurret = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("WithSpriteTurret", StringComparison.Ordinal)); + if (spriteTurret != null) + { + var aimSequence = spriteTurret.Value.Nodes.FirstOrDefault(n => n.Key == "AimSequence"); + if (aimSequence != null) + { + var aimAnim = new MiniYamlNode("WithTurretAimAnimation", ""); + RenameNodeKey(aimSequence, "Sequence"); + aimAnim.Value.Nodes.Add(aimSequence); + spriteTurret.Value.Nodes.Remove(aimSequence); + node.Value.Nodes.Add(aimAnim); + } + } + } + UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); } diff --git a/mods/cnc/rules/vehicles.yaml b/mods/cnc/rules/vehicles.yaml index 577a7827e30a..92d4e9faea9b 100644 --- a/mods/cnc/rules/vehicles.yaml +++ b/mods/cnc/rules/vehicles.yaml @@ -496,15 +496,17 @@ MSAM: Weapon: 227mm LocalOffset: 213,128,0, 213,-128,0 Armament@SECONDARY: + Name: secondary Weapon: 227mm LocalOffset: 213,-128,0, 213,128,0 AttackFrontal: WithSpriteTurret: - AimSequence: aim SpawnActorOnDeath: Actor: MSAM.Husk OwnerType: InternalName EffectiveOwnerFromOwner: true + WithTurretAimAnimation: + Sequence: aim MLRS: Inherits: ^Tank From 02b153030097d46350fc539d14cc3f902d680f5a Mon Sep 17 00:00:00 2001 From: reaperrr Date: Thu, 8 Mar 2018 16:09:43 +0100 Subject: [PATCH 31/48] Fix upgrade rule dates --- OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 541304be4ad5..4c55e4f037fb 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -1710,7 +1710,7 @@ internal static void UpgradeActorRules(ModData modData, int engineVersion, ref L } // Split aim animation logic from WithTurretAttackAnimation to separate WithTurretAimAnimation - if (engineVersion < 20180223) + if (engineVersion < 20180309) { var turAttackAnim = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("WithTurretAttackAnimation", StringComparison.Ordinal)); if (turAttackAnim != null) @@ -1757,7 +1757,7 @@ internal static void UpgradeActorRules(ModData modData, int engineVersion, ref L } // Removed AimSequence from WithSpriteTurret, use WithTurretAimAnimation instead - if (engineVersion < 20180224) + if (engineVersion < 20180309) { var spriteTurret = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("WithSpriteTurret", StringComparison.Ordinal)); if (spriteTurret != null) From 76da40bbda325fbd2631ea5e05eed8e4937d19c9 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sat, 16 Sep 2017 20:50:18 +0200 Subject: [PATCH 32/48] Make WithTurretAttackAnimation conditional --- .../Render/WithTurretAttackAnimation.cs | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Render/WithTurretAttackAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithTurretAttackAnimation.cs index 1a09ac7aeb5c..e1fd4c0b796d 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithTurretAttackAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithTurretAttackAnimation.cs @@ -14,7 +14,7 @@ namespace OpenRA.Mods.Common.Traits.Render { - public class WithTurretAttackAnimationInfo : ITraitInfo, Requires, Requires + public class WithTurretAttackAnimationInfo : ConditionalTraitInfo, Requires, Requires { [Desc("Armament name")] public readonly string Armament = "primary"; @@ -31,38 +31,39 @@ public class WithTurretAttackAnimationInfo : ITraitInfo, Requires, ITick, INotifyAttack { - readonly WithTurretAttackAnimationInfo info; readonly WithSpriteTurret wst; - + readonly Armament armament; int tick; public WithTurretAttackAnimation(ActorInitializer init, WithTurretAttackAnimationInfo info) + : base(info) { - this.info = info; + armament = init.Self.TraitsImplementing() + .Single(a => a.Info.Name == info.Armament); wst = init.Self.TraitsImplementing() .Single(st => st.Info.Turret == info.Turret); } void PlayAttackAnimation(Actor self) { - if (!string.IsNullOrEmpty(info.Sequence)) - wst.PlayCustomAnimation(self, info.Sequence); + if (!string.IsNullOrEmpty(Info.Sequence)) + wst.PlayCustomAnimation(self, Info.Sequence); } void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel) { - if (a.Info.Name != info.Armament) + if (IsTraitDisabled || a != armament) return; - if (info.DelayRelativeTo == AttackDelayType.Attack) + if (Info.DelayRelativeTo == AttackDelayType.Attack) { - if (info.Delay > 0) - tick = info.Delay; + if (Info.Delay > 0) + tick = Info.Delay; else PlayAttackAnimation(self); } @@ -70,13 +71,13 @@ void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barre void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel) { - if (a.Info.Name != info.Armament) + if (IsTraitDisabled || a != armament) return; - if (info.DelayRelativeTo == AttackDelayType.Preparation) + if (Info.DelayRelativeTo == AttackDelayType.Preparation) { - if (info.Delay > 0) - tick = info.Delay; + if (Info.Delay > 0) + tick = Info.Delay; else PlayAttackAnimation(self); } @@ -84,7 +85,7 @@ void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel void ITick.Tick(Actor self) { - if (info.Delay > 0 && --tick == 0) + if (!IsTraitDisabled && Info.Delay > 0 && --tick == 0) PlayAttackAnimation(self); } } From 769a49ef0b1be2a337b21efc6d606ff9df04fe17 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sat, 16 Sep 2017 20:52:32 +0200 Subject: [PATCH 33/48] Make WithTurretAimAnimation conditional --- .../Traits/Render/WithTurretAimAnimation.cs | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Render/WithTurretAimAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithTurretAimAnimation.cs index 5abe65824803..9fa8ddbaf82f 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithTurretAimAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithTurretAimAnimation.cs @@ -14,7 +14,7 @@ namespace OpenRA.Mods.Common.Traits.Render { - public class WithTurretAimAnimationInfo : ITraitInfo, Requires, Requires, Requires + public class WithTurretAimAnimationInfo : ConditionalTraitInfo, Requires, Requires, Requires { [Desc("Armament name")] public readonly string Armament = "primary"; @@ -28,19 +28,27 @@ public class WithTurretAimAnimationInfo : ITraitInfo, Requires().Any(t => t.Turret == Turret); + if (turretAttackAnim) + throw new YamlException("WithTurretAimAnimation is currently not compatible with WithTurretAttackAnimation."); + + base.RulesetLoaded(rules, ai); + } } - public class WithTurretAimAnimation : ITick + public class WithTurretAimAnimation : ConditionalTrait, ITick { - readonly WithTurretAimAnimationInfo info; readonly AttackBase attack; readonly Armament armament; readonly WithSpriteTurret wst; public WithTurretAimAnimation(ActorInitializer init, WithTurretAimAnimationInfo info) + : base(info) { - this.info = info; attack = init.Self.Trait(); armament = init.Self.TraitsImplementing() .Single(a => a.Info.Name == info.Armament); @@ -50,14 +58,14 @@ public WithTurretAimAnimation(ActorInitializer init, WithTurretAimAnimationInfo void ITick.Tick(Actor self) { - if (string.IsNullOrEmpty(info.Sequence) && string.IsNullOrEmpty(info.ReloadPrefix)) + if (IsTraitDisabled) return; var sequence = wst.Info.Sequence; - if (!string.IsNullOrEmpty(info.Sequence) && attack.IsAiming) - sequence = info.Sequence; + if (!string.IsNullOrEmpty(Info.Sequence) && attack.IsAiming) + sequence = Info.Sequence; - var prefix = (armament.IsReloading && !string.IsNullOrEmpty(info.ReloadPrefix)) ? info.ReloadPrefix : ""; + var prefix = (armament.IsReloading && !string.IsNullOrEmpty(Info.ReloadPrefix)) ? Info.ReloadPrefix : ""; if (!string.IsNullOrEmpty(prefix) && sequence != (prefix + sequence)) sequence = prefix + sequence; From 5a889c0efd84bb426f22d0afda0953c61200c35b Mon Sep 17 00:00:00 2001 From: reaperrr Date: Wed, 15 Nov 2017 19:14:41 +0100 Subject: [PATCH 34/48] De-duplicate some WithTurretAttackAnimation code --- .../Render/WithTurretAttackAnimation.cs | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Render/WithTurretAttackAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithTurretAttackAnimation.cs index e1fd4c0b796d..d512f2f8e927 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithTurretAttackAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithTurretAttackAnimation.cs @@ -55,32 +55,24 @@ void PlayAttackAnimation(Actor self) wst.PlayCustomAnimation(self, Info.Sequence); } - void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel) + void NotifyAttack(Actor self) { - if (IsTraitDisabled || a != armament) - return; + if (Info.Delay > 0) + tick = Info.Delay; + else + PlayAttackAnimation(self); + } - if (Info.DelayRelativeTo == AttackDelayType.Attack) - { - if (Info.Delay > 0) - tick = Info.Delay; - else - PlayAttackAnimation(self); - } + void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel) + { + if (!IsTraitDisabled && a == armament && Info.DelayRelativeTo == AttackDelayType.Attack) + NotifyAttack(self); } void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel) { - if (IsTraitDisabled || a != armament) - return; - - if (Info.DelayRelativeTo == AttackDelayType.Preparation) - { - if (Info.Delay > 0) - tick = Info.Delay; - else - PlayAttackAnimation(self); - } + if (!IsTraitDisabled && a == armament && Info.DelayRelativeTo == AttackDelayType.Preparation) + NotifyAttack(self); } void ITick.Tick(Actor self) From 56e7b1edc21083b810ef05d3b7e64ce1bdc63f91 Mon Sep 17 00:00:00 2001 From: abcdefg30 Date: Thu, 22 Feb 2018 02:39:04 +0100 Subject: [PATCH 35/48] Fix int2 arrays not being parsed --- OpenRA.Game/FieldLoader.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/OpenRA.Game/FieldLoader.cs b/OpenRA.Game/FieldLoader.cs index 22274d27df19..d26722144acf 100644 --- a/OpenRA.Game/FieldLoader.cs +++ b/OpenRA.Game/FieldLoader.cs @@ -475,6 +475,23 @@ public static object GetValue(string fieldName, Type fieldType, MiniYaml yaml, M } else if (fieldType == typeof(bool)) return ParseYesNo(value, fieldType, fieldName); + else if (fieldType == typeof(int2[])) + { + if (value != null) + { + var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length % 2 != 0) + return InvalidValueAction(value, fieldType, fieldName); + + var ints = new int2[parts.Length / 2]; + for (var i = 0; i < ints.Length; i++) + ints[i] = new int2(Exts.ParseIntegerInvariant(parts[2 * i]), Exts.ParseIntegerInvariant(parts[2 * i + 1])); + + return ints; + } + + return InvalidValueAction(value, fieldType, fieldName); + } else if (fieldType.IsArray && fieldType.GetArrayRank() == 1) { if (value == null) From 7283f9804e729105e03921981e82dbe4781dfd98 Mon Sep 17 00:00:00 2001 From: atlimit8 Date: Fri, 9 Jun 2017 05:02:00 -0500 Subject: [PATCH 36/48] Added Polygon IHitShape --- OpenRA.Mods.Common/HitShapes/Polygon.cs | 128 ++++++++++++++++ OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 1 + OpenRA.Test/OpenRA.Mods.Common/ShapeTest.cs | 150 +++++++++++++++++++ 3 files changed, 279 insertions(+) create mode 100644 OpenRA.Mods.Common/HitShapes/Polygon.cs diff --git a/OpenRA.Mods.Common/HitShapes/Polygon.cs b/OpenRA.Mods.Common/HitShapes/Polygon.cs new file mode 100644 index 000000000000..cdf096bd7900 --- /dev/null +++ b/OpenRA.Mods.Common/HitShapes/Polygon.cs @@ -0,0 +1,128 @@ +#region Copyright & License Information +/* + * Copyright 2007-2018 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.Drawing; +using System.Linq; +using OpenRA.Graphics; +using OpenRA.Mods.Common.Graphics; + +namespace OpenRA.Mods.Common.HitShapes +{ + public class PolygonShape : IHitShape + { + public WDist OuterRadius { get; private set; } + + [FieldLoader.Require] + public readonly int2[] Points; + + [Desc("Defines the top offset relative to the actor's target point.")] + public readonly int VerticalTopOffset = 0; + + [Desc("Defines the bottom offset relative to the actor's target point.")] + public readonly int VerticalBottomOffset = 0; + + [Desc("Rotates shape by an angle relative to actor facing. Mostly required for buildings on isometric terrain.", + "Mobile actors do NOT need this!")] + public readonly WAngle LocalYaw = WAngle.Zero; + + WVec[] combatOverlayVertsTop; + WVec[] combatOverlayVertsBottom; + int[] squares; + + public PolygonShape() { } + + public PolygonShape(int2[] points) { Points = points; } + + public void Initialize() + { + if (VerticalTopOffset < VerticalBottomOffset) + throw new YamlException("VerticalTopOffset must be equal to or higher than VerticalBottomOffset."); + + OuterRadius = new WDist(Points.Max(x => x.Length)); + combatOverlayVertsTop = Points.Select(p => new WVec(p.X, p.Y, VerticalTopOffset)).ToArray(); + combatOverlayVertsBottom = Points.Select(p => new WVec(p.X, p.Y, VerticalBottomOffset)).ToArray(); + squares = new int[Points.Length]; + squares[0] = (Points[0] - Points[Points.Length - 1]).LengthSquared; + for (var i = 1; i < Points.Length; i++) + squares[i] = (Points[i] - Points[i - 1]).LengthSquared; + } + + static int DistanceSquaredFromLineSegment(int2 c, int2 a, int2 b, int ab2) + { + var ac = c - a; + var ac2 = ac.LengthSquared; + var bc2 = (c - b).LengthSquared; + + // c is closest to point a + if (ac2 + ab2 <= bc2) + return ac2; + + // c is closest to point b + if (bc2 + ab2 <= ac2) + return bc2; + + // c is closest to its unknown orthogonal projection (p) onto the line spanned by b with a as the origin + // Cast to a long for the calculations to avoid overflows + var ab = b - a; + var ap2 = ac.X * ab.X + ac.Y * ab.Y; + var ap = new int2((int)((long)ab.X * ap2 / ab2), (int)((long)ab.Y * ap2 / ab2)); + + // Length of vector pc squared. + return (ac - ap).LengthSquared; + } + + public WDist DistanceFromEdge(WVec v) + { + var p = new int2(v.X, v.Y); + var z = Math.Abs(v.Z); + if (Points.PolygonContains(p)) + return new WDist(z); + + var min2 = DistanceSquaredFromLineSegment(p, Points[Points.Length - 1], Points[0], squares[0]); + for (var i = 1; i < Points.Length; i++) + { + var d2 = DistanceSquaredFromLineSegment(p, Points[i - 1], Points[i], squares[i]); + if (d2 < min2) + min2 = d2; + } + + return new WDist(Exts.ISqrt(min2 + z * z)); + } + + public WDist DistanceFromEdge(WPos pos, Actor actor) + { + var actorPos = actor.CenterPosition; + var orientation = actor.Orientation + WRot.FromYaw(LocalYaw); + + if (pos.Z > actorPos.Z + VerticalTopOffset) + return DistanceFromEdge((pos - (actorPos + new WVec(0, 0, VerticalTopOffset))).Rotate(-orientation)); + + if (pos.Z < actorPos.Z + VerticalBottomOffset) + return DistanceFromEdge((pos - (actorPos + new WVec(0, 0, VerticalBottomOffset))).Rotate(-orientation)); + + return DistanceFromEdge((pos - new WPos(actorPos.X, actorPos.Y, pos.Z)).Rotate(-orientation)); + } + + public void DrawCombatOverlay(WorldRenderer wr, RgbaColorRenderer wcr, Actor actor) + { + var actorPos = actor.CenterPosition; + var orientation = actor.Orientation + WRot.FromYaw(LocalYaw); + + var vertsTop = combatOverlayVertsTop.Select(v => wr.Screen3DPosition(actorPos + v.Rotate(orientation))); + var vertsBottom = combatOverlayVertsBottom.Select(v => wr.Screen3DPosition(actorPos + v.Rotate(orientation))); + wcr.DrawPolygon(vertsTop.ToArray(), 1, Color.Yellow); + wcr.DrawPolygon(vertsBottom.ToArray(), 1, Color.Yellow); + + RangeCircleRenderable.DrawRangeCircle(wr, actorPos, OuterRadius, 1, Color.LimeGreen, 0, Color.LimeGreen); + } + } +} diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 54abd31fa387..a664d82d37e0 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -173,6 +173,7 @@ + diff --git a/OpenRA.Test/OpenRA.Mods.Common/ShapeTest.cs b/OpenRA.Test/OpenRA.Mods.Common/ShapeTest.cs index dc1168ed6643..37a8d58b41fd 100644 --- a/OpenRA.Test/OpenRA.Mods.Common/ShapeTest.cs +++ b/OpenRA.Test/OpenRA.Mods.Common/ShapeTest.cs @@ -92,5 +92,155 @@ public void Rectangle() Assert.That(shape.DistanceFromEdge(new WVec(-1000, -400, 0)).Length, Is.EqualTo(877)); } + + [TestCase(TestName = "PolygonShape report accurate distance")] + public void Polygon() + { + // Rectangle like above, + // Note: The calculations don't match for all, but do have a tolerance of 1. + shape = new PolygonShape(new int2[] { new int2(-123, -456), new int2(100, -456), new int2(100, 100), new int2(-123, 100) }); + shape.Initialize(); + + Assert.That(shape.DistanceFromEdge(new WVec(10, 10, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(-100, 50, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(0, 200, 0)).Length, + Is.EqualTo(100)); + + Assert.That(shape.DistanceFromEdge(new WVec(123, 0, 0)).Length, + Is.EqualTo(23)); + + Assert.That(shape.DistanceFromEdge(new WVec(-100, -400, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(-1000, -400, 0)).Length, + Is.EqualTo(877)); + + // Rectangle like above but reverse order + // Note: The calculations don't match for all, but do have a tolerance of 1. + shape = new PolygonShape(new int2[] { new int2(-123, 100), new int2(100, 100), new int2(100, -456), new int2(-123, -456) }); + shape.Initialize(); + + Assert.That(shape.DistanceFromEdge(new WVec(10, 10, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(-100, 50, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(0, 200, 0)).Length, + Is.EqualTo(100)); + + Assert.That(shape.DistanceFromEdge(new WVec(123, 0, 0)).Length, + Is.EqualTo(23)); + + Assert.That(shape.DistanceFromEdge(new WVec(-100, -400, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(-1000, -400, 0)).Length, + Is.EqualTo(877)); + + // Right triangle taken from above by removing a point + shape = new PolygonShape(new int2[] { new int2(-123, -456), new int2(100, -456), new int2(100, 100) }); + shape.Initialize(); + + Assert.That(shape.DistanceFromEdge(new WVec(10, 10, 0)).Length, + Is.EqualTo(50)); + + Assert.That(shape.DistanceFromEdge(new WVec(99, 10, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(100, 10, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(-100, 50, 0)).Length, + Is.EqualTo(167)); + + Assert.That(shape.DistanceFromEdge(new WVec(0, 200, 0)).Length, + Is.EqualTo(141)); + + Assert.That(shape.DistanceFromEdge(new WVec(123, 0, 0)).Length, + Is.EqualTo(23)); + + Assert.That(shape.DistanceFromEdge(new WVec(-100, -400, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(-1000, -400, 0)).Length, + Is.EqualTo(878)); + + // Right triangle taken from above but reverse order + shape = new PolygonShape(new int2[] { new int2(100, 100), new int2(100, -456), new int2(-123, -456) }); + shape.Initialize(); + + Assert.That(shape.DistanceFromEdge(new WVec(10, 10, 0)).Length, + Is.EqualTo(49)); // Differs from above by integer rounding. + + Assert.That(shape.DistanceFromEdge(new WVec(99, 10, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(100, 10, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(-100, 50, 0)).Length, + Is.EqualTo(167)); + + Assert.That(shape.DistanceFromEdge(new WVec(0, 200, 0)).Length, + Is.EqualTo(141)); + + Assert.That(shape.DistanceFromEdge(new WVec(123, 0, 0)).Length, + Is.EqualTo(23)); + + Assert.That(shape.DistanceFromEdge(new WVec(-100, -400, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(-1000, -400, 0)).Length, + Is.EqualTo(878)); + + // Plus shaped dodecagon + shape = new PolygonShape(new int2[] { + new int2(-511, -1535), new int2(511, -1535), new int2(511, -511), new int2(1535, -511), + new int2(1535, 511), new int2(511, 511), new int2(511, 1535), new int2(-511, 1535), + new int2(-511, 511), new int2(-1535, 511), new int2(-1535, -511), new int2(-511, -511) + }); + shape.Initialize(); + + Assert.That(shape.DistanceFromEdge(new WVec(10, 10, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(-511, -1535, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(-512, -1536, 0)).Length, + Is.EqualTo(1)); + + Assert.That(shape.DistanceFromEdge(new WVec(0, -1535, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(0, 1535, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(-1535, 0, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(1535, 0, 0)).Length, + Is.EqualTo(0)); + + Assert.That(shape.DistanceFromEdge(new WVec(-1535, -1535, 0)).Length, + Is.EqualTo(1024)); + + Assert.That(shape.DistanceFromEdge(new WVec(1535, -1535, 0)).Length, + Is.EqualTo(1024)); + + Assert.That(shape.DistanceFromEdge(new WVec(-1535, 1535, 0)).Length, + Is.EqualTo(1024)); + + Assert.That(shape.DistanceFromEdge(new WVec(1535, 1535, 0)).Length, + Is.EqualTo(1024)); + + Assert.That(shape.DistanceFromEdge(new WVec(-500, -1635, 0)).Length, + Is.EqualTo(100)); + } } } From ed6a6b61deff702309bc6537b57833fc46bf28e5 Mon Sep 17 00:00:00 2001 From: abcdefg30 Date: Thu, 22 Feb 2018 02:39:18 +0100 Subject: [PATCH 37/48] Throw a proper error when parsing an invalid int2 --- OpenRA.Game/FieldLoader.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/OpenRA.Game/FieldLoader.cs b/OpenRA.Game/FieldLoader.cs index d26722144acf..f09fd1f25def 100644 --- a/OpenRA.Game/FieldLoader.cs +++ b/OpenRA.Game/FieldLoader.cs @@ -548,6 +548,9 @@ public static object GetValue(string fieldName, Type fieldType, MiniYaml yaml, M if (value != null) { var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length != 2) + return InvalidValueAction(value, fieldType, fieldName); + return new int2(Exts.ParseIntegerInvariant(parts[0]), Exts.ParseIntegerInvariant(parts[1])); } From b012fa62fe75999b91f913aad8c6374308e1fa54 Mon Sep 17 00:00:00 2001 From: abcdefg30 Date: Thu, 22 Feb 2018 02:41:12 +0100 Subject: [PATCH 38/48] Replace 'target point' by 'center' in all descriptions --- OpenRA.Mods.Common/HitShapes/Capsule.cs | 4 ++-- OpenRA.Mods.Common/HitShapes/Circle.cs | 4 ++-- OpenRA.Mods.Common/HitShapes/Polygon.cs | 4 ++-- OpenRA.Mods.Common/HitShapes/Rectangle.cs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/OpenRA.Mods.Common/HitShapes/Capsule.cs b/OpenRA.Mods.Common/HitShapes/Capsule.cs index 831b5d947ddd..052db69c3aec 100644 --- a/OpenRA.Mods.Common/HitShapes/Capsule.cs +++ b/OpenRA.Mods.Common/HitShapes/Capsule.cs @@ -30,10 +30,10 @@ public class CapsuleShape : IHitShape public readonly WDist Radius = new WDist(426); - [Desc("Defines the top offset relative to the actor's target point.")] + [Desc("Defines the top offset relative to the actor's center.")] public readonly int VerticalTopOffset = 0; - [Desc("Defines the bottom offset relative to the actor's target point.")] + [Desc("Defines the bottom offset relative to the actor's center.")] public readonly int VerticalBottomOffset = 0; int2 ab; diff --git a/OpenRA.Mods.Common/HitShapes/Circle.cs b/OpenRA.Mods.Common/HitShapes/Circle.cs index 83dfb6d62e8e..cfdc9065eedb 100644 --- a/OpenRA.Mods.Common/HitShapes/Circle.cs +++ b/OpenRA.Mods.Common/HitShapes/Circle.cs @@ -25,10 +25,10 @@ public class CircleShape : IHitShape [FieldLoader.Require] public readonly WDist Radius = new WDist(426); - [Desc("Defines the top offset relative to the actor's target point.")] + [Desc("Defines the top offset relative to the actor's center.")] public readonly int VerticalTopOffset = 0; - [Desc("Defines the bottom offset relative to the actor's target point.")] + [Desc("Defines the bottom offset relative to the actor's center.")] public readonly int VerticalBottomOffset = 0; public CircleShape() { } diff --git a/OpenRA.Mods.Common/HitShapes/Polygon.cs b/OpenRA.Mods.Common/HitShapes/Polygon.cs index cdf096bd7900..184425ed2118 100644 --- a/OpenRA.Mods.Common/HitShapes/Polygon.cs +++ b/OpenRA.Mods.Common/HitShapes/Polygon.cs @@ -24,10 +24,10 @@ public class PolygonShape : IHitShape [FieldLoader.Require] public readonly int2[] Points; - [Desc("Defines the top offset relative to the actor's target point.")] + [Desc("Defines the top offset relative to the actor's center.")] public readonly int VerticalTopOffset = 0; - [Desc("Defines the bottom offset relative to the actor's target point.")] + [Desc("Defines the bottom offset relative to the actor's center.")] public readonly int VerticalBottomOffset = 0; [Desc("Rotates shape by an angle relative to actor facing. Mostly required for buildings on isometric terrain.", diff --git a/OpenRA.Mods.Common/HitShapes/Rectangle.cs b/OpenRA.Mods.Common/HitShapes/Rectangle.cs index 0d005acf42f4..666b94b7c2b9 100644 --- a/OpenRA.Mods.Common/HitShapes/Rectangle.cs +++ b/OpenRA.Mods.Common/HitShapes/Rectangle.cs @@ -28,10 +28,10 @@ public class RectangleShape : IHitShape [FieldLoader.Require] public readonly int2 BottomRight; - [Desc("Defines the top offset relative to the actor's target point.")] + [Desc("Defines the top offset relative to the actor's center.")] public readonly int VerticalTopOffset = 0; - [Desc("Defines the bottom offset relative to the actor's target point.")] + [Desc("Defines the bottom offset relative to the actor's center.")] public readonly int VerticalBottomOffset = 0; [Desc("Rotates shape by an angle relative to actor facing. Mostly required for buildings on isometric terrain.", From 1d8b1906ef84ae7bd24349e1741a233680712aa1 Mon Sep 17 00:00:00 2001 From: Mustafa Alperen Seki Date: Tue, 23 Jan 2018 13:31:32 +0300 Subject: [PATCH 39/48] Add IsPlayerPalette support to WithDecoration --- OpenRA.Mods.Common/Traits/Render/WithDecoration.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Render/WithDecoration.cs b/OpenRA.Mods.Common/Traits/Render/WithDecoration.cs index 8d05777ddc7f..7fb520e35894 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithDecoration.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithDecoration.cs @@ -37,7 +37,10 @@ public class WithDecorationInfo : ConditionalTraitInfo, Requires RenderInner(Actor self, WorldRenderer wr) } var pxPos = wr.Viewport.WorldToViewPx(boundsOffset) + sizeOffset; - return new IRenderable[] { new UISpriteRenderable(Anim.Image, self.CenterPosition, pxPos, Info.ZOffset, wr.Palette(Info.Palette), 1f) }; + return new IRenderable[] + { + new UISpriteRenderable(Anim.Image, self.CenterPosition, pxPos, Info.ZOffset, wr.Palette(Info.Palette + (Info.IsPlayerPalette ? self.Owner.InternalName : "")), 1f) + }; } void ITick.Tick(Actor self) { Anim.Tick(); } From fd83cbf60fd30a0461e7c36751897c32510925dd Mon Sep 17 00:00:00 2001 From: reaperrr Date: Fri, 9 Mar 2018 21:28:53 +0100 Subject: [PATCH 40/48] Fix WithTurretAimAnimation disabled handling The old sequence was not recovering when this trait lost its required condition while the aim anim was running. Now it doesn't unconditionally return, but instead checks what the current sequence is and resets to base turret sequence if AimAnim is disabled. --- .../Traits/Render/WithTurretAimAnimation.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Render/WithTurretAimAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithTurretAimAnimation.cs index 9fa8ddbaf82f..a7c6e1b36604 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithTurretAimAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithTurretAimAnimation.cs @@ -34,7 +34,7 @@ public override void RulesetLoaded(Ruleset rules, ActorInfo ai) { var turretAttackAnim = ai.TraitInfos().Any(t => t.Turret == Turret); if (turretAttackAnim) - throw new YamlException("WithTurretAimAnimation is currently not compatible with WithTurretAttackAnimation."); + throw new YamlException("WithTurretAimAnimation is currently not compatible with WithTurretAttackAnimation. Don't use them on the same turret."); base.RulesetLoaded(rules, ai); } @@ -45,6 +45,7 @@ public class WithTurretAimAnimation : ConditionalTrait a.Info.Name == info.Armament); wst = init.Self.TraitsImplementing() .Single(st => st.Info.Turret == info.Turret); + + sequence = wst.Info.Sequence; } void ITick.Tick(Actor self) { - if (IsTraitDisabled) + if (IsTraitDisabled && sequence == wst.Info.Sequence) return; - var sequence = wst.Info.Sequence; - if (!string.IsNullOrEmpty(Info.Sequence) && attack.IsAiming) + sequence = wst.Info.Sequence; + if (!IsTraitDisabled && !string.IsNullOrEmpty(Info.Sequence) && attack.IsAiming) sequence = Info.Sequence; var prefix = (armament.IsReloading && !string.IsNullOrEmpty(Info.ReloadPrefix)) ? Info.ReloadPrefix : ""; - if (!string.IsNullOrEmpty(prefix) && sequence != (prefix + sequence)) + if (!IsTraitDisabled && !string.IsNullOrEmpty(prefix) && sequence != (prefix + sequence)) sequence = prefix + sequence; wst.DefaultAnimation.ReplaceAnim(sequence); From ce396e840de8c169db6c734fbdec50a603a43883 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sat, 10 Mar 2018 01:49:49 +0100 Subject: [PATCH 41/48] Improve RA service depot hitshape + target offsets --- mods/ra/rules/fakes.yaml | 11 +++-------- mods/ra/rules/structures.yaml | 11 +++-------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/mods/ra/rules/fakes.yaml b/mods/ra/rules/fakes.yaml index 021042882bc1..f93ab4815500 100644 --- a/mods/ra/rules/fakes.yaml +++ b/mods/ra/rules/fakes.yaml @@ -206,14 +206,9 @@ FIXF: Valued: Cost: 120 HitShape: - Type: Rectangle - TopLeft: -1536, -683 - BottomRight: 1536, 853 - HitShape@TOPANDBOTTOM: - TargetableOffsets: 840,0,0, -1060,0,0 - Type: Rectangle - TopLeft: -640, -768 - BottomRight: 640, 1024 + TargetableOffsets: 840,0,0, 598,-640,0, 598,640,0, -1060,0,0, -768,-640,0, -768,640,0 + Type: Polygon + Points: -1536,-300, -640,-811, 640,-811, 1536,-300, 1536,555, 640,1110, -640,1110, -1536,555 FAPW: Inherits: ^FakeBuilding diff --git a/mods/ra/rules/structures.yaml b/mods/ra/rules/structures.yaml index 44cb99e84e29..3166a1870b91 100644 --- a/mods/ra/rules/structures.yaml +++ b/mods/ra/rules/structures.yaml @@ -1821,14 +1821,9 @@ FIX: Amount: -30 ProvidesPrerequisite@buildingname: HitShape: - Type: Rectangle - TopLeft: -1536, -683 - BottomRight: 1536, 853 - HitShape@TOPANDBOTTOM: - TargetableOffsets: 840,0,0, -1060,0,0 - Type: Rectangle - TopLeft: -640, -768 - BottomRight: 640, 1024 + TargetableOffsets: 840,0,0, 598,-640,0, 598,640,0, -1060,0,0, -768,-640,0, -768,640,0 + Type: Polygon + Points: -1536,-300, -640,-811, 640,-811, 1536,-300, 1536,555, 640,1110, -640,1110, -1536,555 SBAG: Inherits: ^Wall From 1f4573886b98592c7b86f38f09bd5b825e18adc1 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sat, 10 Mar 2018 02:02:04 +0100 Subject: [PATCH 42/48] Improve TD service depot hitshape and target offsets --- mods/cnc/rules/structures.yaml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/mods/cnc/rules/structures.yaml b/mods/cnc/rules/structures.yaml index 1a2328ba627e..997858d4365c 100644 --- a/mods/cnc/rules/structures.yaml +++ b/mods/cnc/rules/structures.yaml @@ -563,14 +563,9 @@ HQ: FIX: Inherits: ^BaseBuilding HitShape: - Type: Rectangle - TopLeft: -1536, -683 - BottomRight: 1536, 768 - HitShape@TOPANDBOTTOM: - TargetableOffsets: 840,0,0, -1060,0,0 - Type: Rectangle - TopLeft: -640, -980 - BottomRight: 640, 1110 + TargetableOffsets: 840,0,0, 598,-640,0, 598,640,0, -1060,0,0, -768,-640,0, -768,640,0 + Type: Polygon + Points: -1536,-256, -341,-940, 341,-940, 1536,-256, 1536,341, 341,1110, -341,1110, -1536,341 Valued: Cost: 500 Tooltip: From 3ce2417a068f94923dc75c075ac6c34fd6805806 Mon Sep 17 00:00:00 2001 From: Peter Antal <172160+PeterAntal@users.noreply.github.com> Date: Sun, 4 Mar 2018 22:04:39 -0800 Subject: [PATCH 43/48] Create SupportDirPrefix and IsPathRelativeToSupportDirectory() as members on Platform class. --- OpenRA.Game/Game.cs | 4 ++-- OpenRA.Game/Map/MapCache.cs | 4 ++-- OpenRA.Game/Network/ReplayRecorder.cs | 2 +- OpenRA.Game/Platform.cs | 10 ++++++++-- OpenRA.Game/Server/Server.cs | 2 +- OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs | 2 +- OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs | 2 +- 7 files changed, 16 insertions(+), 10 deletions(-) diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index c3e728df0f86..4aac25096ba7 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -243,7 +243,7 @@ public static bool IsHost public static void InitializeSettings(Arguments args) { - Settings = new Settings(Platform.ResolvePath(Path.Combine("^", "settings.yaml")), args); + Settings = new Settings(Platform.ResolvePath(Path.Combine(Platform.SupportDirPrefix, "settings.yaml")), args); } public static RunStatus InitializeAndRun(string[] args) @@ -528,7 +528,7 @@ static void TakeScreenshotInner() ThreadPool.QueueUserWorkItem(_ => { var mod = ModData.Manifest.Metadata; - var directory = Platform.ResolvePath("^", "Screenshots", ModData.Manifest.Id, mod.Version); + var directory = Platform.ResolvePath(Platform.SupportDirPrefix, "Screenshots", ModData.Manifest.Id, mod.Version); Directory.CreateDirectory(directory); var filename = TimestampedFilename(true); diff --git a/OpenRA.Game/Map/MapCache.cs b/OpenRA.Game/Map/MapCache.cs index dcd7476c2e93..195d7602bab8 100644 --- a/OpenRA.Game/Map/MapCache.cs +++ b/OpenRA.Game/Map/MapCache.cs @@ -70,7 +70,7 @@ public void LoadMaps() try { // HACK: If the path is inside the the support directory then we may need to create it - if (name.StartsWith("^", StringComparison.Ordinal)) + if (Platform.IsPathRelativeToSupportDirectory(name)) { // Assume that the path is a directory if there is not an existing file with the same name var resolved = Platform.ResolvePath(name); @@ -144,7 +144,7 @@ public IEnumerable EnumerateMapsWithoutCaching(MapClassification classifica name = name.Substring(1); // Don't try to open the map directory in the support directory if it doesn't exist - if (name.StartsWith("^", StringComparison.Ordinal)) + if (Platform.IsPathRelativeToSupportDirectory(name)) { var resolved = Platform.ResolvePath(name); if (!Directory.Exists(resolved) || !File.Exists(resolved)) diff --git a/OpenRA.Game/Network/ReplayRecorder.cs b/OpenRA.Game/Network/ReplayRecorder.cs index 50741388c5e9..50162716b51e 100644 --- a/OpenRA.Game/Network/ReplayRecorder.cs +++ b/OpenRA.Game/Network/ReplayRecorder.cs @@ -45,7 +45,7 @@ void StartSavingReplay(byte[] initialContent) { var filename = chooseFilename(); var mod = Game.ModData.Manifest; - var dir = Platform.ResolvePath("^", "Replays", mod.Id, mod.Metadata.Version); + var dir = Platform.ResolvePath(Platform.SupportDirPrefix, "Replays", mod.Id, mod.Metadata.Version); if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); diff --git a/OpenRA.Game/Platform.cs b/OpenRA.Game/Platform.cs index 16bc44ea284a..b0747fb3141c 100644 --- a/OpenRA.Game/Platform.cs +++ b/OpenRA.Game/Platform.cs @@ -21,6 +21,7 @@ public enum PlatformType { Unknown, Windows, OSX, Linux } public static class Platform { + public const string SupportDirPrefix = "^"; public static PlatformType CurrentPlatform { get { return currentPlatform.Value; } } public static readonly Guid SessionGUID = Guid.NewGuid(); @@ -143,7 +144,7 @@ public static string ResolvePath(string path) path = path.TrimEnd(' ', '\t'); // Paths starting with ^ are relative to the support dir - if (path.StartsWith("^", StringComparison.Ordinal)) + if (Platform.IsPathRelativeToSupportDirectory(path)) path = SupportDir + path.Substring(1); // Paths starting with . are relative to the game dir @@ -166,12 +167,17 @@ public static string ResolvePath(params string[] path) public static string UnresolvePath(string path) { if (path.StartsWith(SupportDir, StringComparison.Ordinal)) - path = "^" + path.Substring(SupportDir.Length); + path = Platform.SupportDirPrefix + path.Substring(SupportDir.Length); if (path.StartsWith(GameDir, StringComparison.Ordinal)) path = "./" + path.Substring(GameDir.Length); return path; } + + public static bool IsPathRelativeToSupportDirectory(string path) + { + return path.StartsWith(Platform.SupportDirPrefix, StringComparison.Ordinal); + } } } diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index cc0db7906f29..d75470969cab 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -384,7 +384,7 @@ void ValidateClient(Connection newConn, string data) if (Dedicated) { - var motdFile = Platform.ResolvePath("^", "motd.txt"); + var motdFile = Platform.ResolvePath(Platform.SupportDirPrefix, "motd.txt"); if (!File.Exists(motdFile)) File.WriteAllText(motdFile, "Welcome, have fun and good luck!"); diff --git a/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs index 8ea8205c324d..586f631b38da 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs @@ -289,7 +289,7 @@ void LoadAndDisplayNews(string newsURL, Widget newsBG) { if (newsBG != null) { - var cacheFile = Platform.ResolvePath("^", "news.yaml"); + var cacheFile = Platform.ResolvePath(Platform.SupportDirPrefix, "news.yaml"); var currentNews = ParseNews(cacheFile); if (currentNews != null) DisplayNews(currentNews); diff --git a/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs index 080a68aa6cf6..157c06db215a 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs @@ -63,7 +63,7 @@ public ReplayBrowserLogic(Widget widget, ModData modData, Action onExit, Action var template = panel.Get("REPLAY_TEMPLATE"); var mod = modData.Manifest; - var dir = Platform.ResolvePath("^", "Replays", mod.Id, mod.Metadata.Version); + var dir = Platform.ResolvePath(Platform.SupportDirPrefix, "Replays", mod.Id, mod.Metadata.Version); if (Directory.Exists(dir)) ThreadPool.QueueUserWorkItem(_ => LoadReplays(dir, template)); From 227cf35d5f4f8425cafa7e655280baba693a9a2e Mon Sep 17 00:00:00 2001 From: xecollons Date: Sat, 10 Mar 2018 17:19:26 +0100 Subject: [PATCH 44/48] Added TooltipDescription --- OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 1 + .../Traits/TooltipDescription.cs | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 OpenRA.Mods.Common/Traits/TooltipDescription.cs diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index a664d82d37e0..98a4ed45c981 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -304,6 +304,7 @@ + diff --git a/OpenRA.Mods.Common/Traits/TooltipDescription.cs b/OpenRA.Mods.Common/Traits/TooltipDescription.cs new file mode 100644 index 000000000000..1fdcb7091329 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/TooltipDescription.cs @@ -0,0 +1,41 @@ +#region Copyright & License Information +/* + * Copyright 2007-2018 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Additional info shown in the battlefield tooltip.")] + public class TooltipDescriptionInfo : ConditionalTraitInfo + { + [Desc("Text shown in tooltip.")] + [Translate] + public readonly string Description = ""; + + public override object Create(ActorInitializer init) { return new TooltipDescription(this); } + } + + public class TooltipDescription : ConditionalTrait, IProvideTooltipInfo + { + public TooltipDescription(TooltipDescriptionInfo info) + : base(info) { } + + public bool IsTooltipVisible(Player forPlayer) { return !IsTraitDisabled; } + + public string TooltipText + { + get + { + return Info.Description; + } + } + } +} From 311cd521e333f0e5a1dedc2bd372027906d188d8 Mon Sep 17 00:00:00 2001 From: "DESKTOP-7PLKRFC\\jur_1" Date: Sat, 3 Mar 2018 22:36:35 +0100 Subject: [PATCH 45/48] Refinery spawned harvesters should prioritize lower ore #14827 --- OpenRA.Mods.Common/Activities/FindResources.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/OpenRA.Mods.Common/Activities/FindResources.cs b/OpenRA.Mods.Common/Activities/FindResources.cs index 956488d8feb9..5d755433da1d 100644 --- a/OpenRA.Mods.Common/Activities/FindResources.cs +++ b/OpenRA.Mods.Common/Activities/FindResources.cs @@ -121,7 +121,7 @@ public override Activity Tick(Actor self) return self.Location; // Determine where to search from and how far to search: - var searchFromLoc = harv.LastOrderLocation ?? (harv.LastLinkedProc ?? harv.LinkedProc ?? self).Location; + var searchFromLoc = GetSearchFromLocation(self); var searchRadius = harv.LastOrderLocation.HasValue ? harvInfo.SearchFromOrderRadius : harvInfo.SearchFromProcRadius; var searchRadiusSquared = searchRadius * searchRadius; @@ -152,5 +152,16 @@ public override IEnumerable GetTargets(Actor self) { yield return Target.FromCell(self.World, self.Location); } + + CPos GetSearchFromLocation(Actor self) + { + if (harv.LastOrderLocation.HasValue) + return harv.LastOrderLocation.Value; + else if (harv.LastLinkedProc != null) + return harv.LastLinkedProc.Location + harv.LastLinkedProc.Trait().DeliveryOffset; + else if (harv.LinkedProc != null) + return harv.LinkedProc.Location + harv.LinkedProc.Trait().DeliveryOffset; + return self.Location; + } } } From ac7b6e42f05b770a96164bcc90880cf9e7cc9132 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 1 Mar 2018 19:06:25 +0000 Subject: [PATCH 46/48] Improve behaviour of Platform.UnresolvePath on Windows. --- OpenRA.Game/Platform.cs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/OpenRA.Game/Platform.cs b/OpenRA.Game/Platform.cs index b0747fb3141c..e71ae16829d9 100644 --- a/OpenRA.Game/Platform.cs +++ b/OpenRA.Game/Platform.cs @@ -144,7 +144,7 @@ public static string ResolvePath(string path) path = path.TrimEnd(' ', '\t'); // Paths starting with ^ are relative to the support dir - if (Platform.IsPathRelativeToSupportDirectory(path)) + if (IsPathRelativeToSupportDirectory(path)) path = SupportDir + path.Substring(1); // Paths starting with . are relative to the game dir @@ -163,21 +163,30 @@ public static string ResolvePath(params string[] path) return ResolvePath(path.Aggregate(Path.Combine)); } - /// Replace the full path prefix with the special notation characters ^ or . + /// + /// Replace the full path prefix with the special notation characters ^ or . + /// and transforms \ path separators to / on Windows + /// public static string UnresolvePath(string path) { - if (path.StartsWith(SupportDir, StringComparison.Ordinal)) - path = Platform.SupportDirPrefix + path.Substring(SupportDir.Length); + // Use a case insensitive comparison on windows to avoid problems + // with inconsistent drive letter case + var compare = CurrentPlatform == PlatformType.Windows ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + if (path.StartsWith(SupportDir, compare)) + path = SupportDirPrefix + path.Substring(SupportDir.Length); - if (path.StartsWith(GameDir, StringComparison.Ordinal)) + if (path.StartsWith(GameDir, compare)) path = "./" + path.Substring(GameDir.Length); + if (CurrentPlatform == PlatformType.Windows) + path = path.Replace('\\', '/'); + return path; } public static bool IsPathRelativeToSupportDirectory(string path) { - return path.StartsWith(Platform.SupportDirPrefix, StringComparison.Ordinal); + return path.StartsWith(SupportDirPrefix, StringComparison.Ordinal); } } } From be96bafc69679d63ab349602649ecf8e1f2fff5b Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 10 Mar 2018 11:11:44 +0000 Subject: [PATCH 47/48] Match missions using the unresolved path. --- .../Widgets/Logic/MissionBrowserLogic.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/OpenRA.Mods.Common/Widgets/Logic/MissionBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MissionBrowserLogic.cs index f671e3597e67..2d5074a13e93 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MissionBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MissionBrowserLogic.cs @@ -105,11 +105,18 @@ public MissionBrowserLogic(Widget widget, ModData modData, World world, Action o foreach (var kv in yaml) { - var missionMapPaths = kv.Value.Nodes.Select(n => Path.GetFullPath(n.Key)).ToList(); + var missionMapPaths = kv.Value.Nodes.Select(n => n.Key).ToList(); var previews = modData.MapCache - .Where(p => p.Status == MapStatus.Available && missionMapPaths.Contains(p.Package.Name)) - .OrderBy(p => missionMapPaths.IndexOf(p.Package.Name)); + .Where(p => p.Status == MapStatus.Available) + .Select(p => new + { + Preview = p, + Index = missionMapPaths.IndexOf(Platform.UnresolvePath(p.Package.Name)) + }) + .Where(x => x.Index != -1) + .OrderBy(x => x.Index) + .Select(x => x.Preview); CreateMissionGroup(kv.Key, previews); allPreviews.AddRange(previews); From 65c42a68ce4ec67bff780bd5b71d6bd063c3531f Mon Sep 17 00:00:00 2001 From: fruehstueck Date: Sun, 4 Mar 2018 21:01:52 +0100 Subject: [PATCH 48/48] Add shortcuts for make.ps1 commands --- make.ps1 | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/make.ps1 b/make.ps1 index ae7b1ea69fc6..8271ca412649 100644 --- a/make.ps1 +++ b/make.ps1 @@ -232,16 +232,16 @@ if ($args.Length -eq 0) { echo "Command list:" echo "" - echo " all Builds the game and its development tools." - echo " dependencies Copies the game's dependencies into the main game folder." - echo " version Sets the version strings for the default mods to the latest" - echo " version for the current Git branch." - echo " clean Removes all built and copied files. Use the 'all' and" - echo " 'dependencies' commands to restore removed files." - echo " test Tests the default mods for errors." - echo " check Checks .cs files for StyleCop violations." - echo " check-scripts Checks .lua files for syntax errors." - echo " docs Generates the trait and Lua API documentation." + echo " all, a Builds the game and its development tools." + echo " dependencies, d Copies the game's dependencies into the main game folder." + echo " version, v Sets the version strings for the default mods to the" + echo " latest version for the current Git branch." + echo " clean, c Removes all built and copied files. Use the 'all' and" + echo " 'dependencies' commands to restore removed files." + echo " test, t Tests the default mods for errors." + echo " check, ck Checks .cs files for StyleCop violations." + echo " check-scripts, cs Checks .lua files for syntax errors." + echo " docs Generates the trait and Lua API documentation." echo "" $command = (Read-Host "Enter command").Split(' ', 2) } @@ -258,14 +258,14 @@ if ($command.Length -gt 1) switch ($execute) { - "all" { All-Command } - "dependencies" { Dependencies-Command } - "version" { Version-Command } - "clean" { Clean-Command } - "test" { Test-Command } - "check" { Check-Command } - "check-scripts" { Check-Scripts-Command } - "docs" { Docs-Command } + {"all", "a" -contains $_} { All-Command } + {"dependencies", "d" -contains $_} { Dependencies-Command } + {"version", "v" -contains $_} { Version-Command } + {"clean", "c" -contains $_} { Clean-Command } + {"test", "t" -contains $_} { Test-Command } + {"check", "ck" -contains $_} { Check-Command } + {"check-scripts", "cs" -contains $_} { Check-Scripts-Command } + "docs" { Docs-Command } Default { echo ("Invalid command '{0}'" -f $command) } }