From f3c24e568b0d7915beac995e504ac1ccdd497873 Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Thu, 25 Dec 2025 13:42:39 +0000 Subject: [PATCH 01/28] Frozen Rift map fix. --- mods/ca/maps/frozen_rift_ca.oramap | Bin 24742 -> 24776 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/mods/ca/maps/frozen_rift_ca.oramap b/mods/ca/maps/frozen_rift_ca.oramap index 6f2ba1c5d21a90e6e4a8f02cd82d7f48d36eadb3..e8af655d15e1c24be6016aa47f45aa9560ee4550 100644 GIT binary patch delta 445 zcmZ2>knzMp#`*wnW)=|!1_llWm)sfAQPp>jodWU%7=ai_C*_o;mZv6`WTY19RVL=< z`1T#lVm9P)Ie#+z1@oPwkwQ0qP0N`U!xcMg=}yrQcg02PWOGa>)YU$5*IotG zJ*t>@quw&ZyzBZ~w}%~(H`uK{>1R59w7;=@LQ$c?mr%C0(y5Jm*BWhjz1rl%p^0iw zHt=rO-k1F2;`T46I~Vt?JTYVP@}RcUuDh>&$}hRryliuE?4pRf#ihF~cW343ZvXo) zrOT*#_aA16$0t9Elh`c5*by5i&wwi!RDb~i0t#RfGf=poGK>ro3;};dq;8koVT934 ze*eWm{K>cCv>1O(W{tOHYGj%`pGkajbi5Yhx5;hs7K{fb?~gZUWSxXa h#h*qD47rH~dP$ji0p6@^AbUB0P=bkpVOAW72LNn?tXBX4 delta 385 zcmX?cka5{T#`*wnW)=|!1`ZB}wR2UY{L0i4&M+`Ah%quS2mtA%oYK_t)WnjE)FQpg z#M~U;{sY@$bl5ReO>%{rC)+f$lLr?0Txv9} zwo*79^G^5Q0-Mt5bvq}mbBp}EwD*nv{Nh!gBZZd>mz7-Fwq`?ZhSK~yIy+tWC(gMk zQdqZoX3d+uvj3PtUf=j Date: Thu, 25 Dec 2025 13:42:53 +0000 Subject: [PATCH 02/28] Captured Nerve Centers in Banishment count towards objective. --- mods/ca/missions/main-campaign/ca48-banishment/banishment.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ca/missions/main-campaign/ca48-banishment/banishment.lua b/mods/ca/missions/main-campaign/ca48-banishment/banishment.lua index 95a3a67b44..67b4051c2e 100644 --- a/mods/ca/missions/main-campaign/ca48-banishment/banishment.lua +++ b/mods/ca/missions/main-campaign/ca48-banishment/banishment.lua @@ -129,7 +129,7 @@ WorldLoaded = function() end) local nerveCenters = MaleficScrin.GetActorsByType("nerv") - Trigger.OnAllKilled(nerveCenters, function() + Trigger.OnAllKilledOrCaptured(nerveCenters, function() Greece.MarkCompletedObjective(ObjectiveDestroyScrinBases) end) From a17b8d58ffa12eab697c9274d9525b78b7565f52 Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Wed, 24 Dec 2025 15:47:01 +0000 Subject: [PATCH 03/28] - Encyclopedia update. - Deliverance Co-op low power announcement fix. --- mods/ca/fluent/encyclopedia.ftl | 2 ++ .../ca03-deliverance-coop/deliverance-coop.lua | 7 +++++++ .../main-campaign/ca03-deliverance/deliverance.lua | 13 ++++++------- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/mods/ca/fluent/encyclopedia.ftl b/mods/ca/fluent/encyclopedia.ftl index e8857f1895..8e1db5d173 100644 --- a/mods/ca/fluent/encyclopedia.ftl +++ b/mods/ca/fluent/encyclopedia.ftl @@ -37,3 +37,5 @@ encyclopedia-tips-general-description = • Most infantry prone when they are fi • Service Depot repairs are completely free. Vehicles can be sold while being repaired. • All production times are proportional to cost. The cost to time ratio is lower for Refineries, MCVs and higher for upgrades. + + • In Single Queue the maximum production speed is 2x, which requires 5 infantry/aircraft production structures, or 4 vehicle/building production structures. diff --git a/mods/ca/missions/coop-campaign/ca03-deliverance-coop/deliverance-coop.lua b/mods/ca/missions/coop-campaign/ca03-deliverance-coop/deliverance-coop.lua index 4e6585aa70..e827806531 100644 --- a/mods/ca/missions/coop-campaign/ca03-deliverance-coop/deliverance-coop.lua +++ b/mods/ca/missions/coop-campaign/ca03-deliverance-coop/deliverance-coop.lua @@ -26,6 +26,13 @@ AfterTick = function() end TransferGDIUnits = function() + Utils.Do(MissionPlayers, function(p) + p.PlayLowPowerNotification = false + Trigger.AfterDelay(DateTime.Seconds(10), function() + p.PlayLowPowerNotification = true + end) + end) + local gdiForces = GDI.GetActors() Utils.Do(gdiForces, function(a) if a.Type ~= "player" then diff --git a/mods/ca/missions/main-campaign/ca03-deliverance/deliverance.lua b/mods/ca/missions/main-campaign/ca03-deliverance/deliverance.lua index 43cca14a89..15ab11b4b9 100644 --- a/mods/ca/missions/main-campaign/ca03-deliverance/deliverance.lua +++ b/mods/ca/missions/main-campaign/ca03-deliverance/deliverance.lua @@ -349,15 +349,9 @@ GDIBaseFound = function() IsGDIBaseFound = true MediaCA.PlaySound(MissionDir .. "/r_gdibasediscovered.aud", 2) - Greece.PlayLowPowerNotification = false - TransferGDIUnits() - - Trigger.AfterDelay(DateTime.Seconds(5), function() - Greece.PlayLowPowerNotification = true - end) - InitUSSRAttacks() + TimerTicks = HoldOutTime[Difficulty] Trigger.AfterDelay(DateTime.Seconds(1), function() @@ -396,6 +390,11 @@ end -- overridden in co-op version TransferGDIUnits = function() + Greece.PlayLowPowerNotification = false + Trigger.AfterDelay(DateTime.Seconds(10), function() + Greece.PlayLowPowerNotification = true + end) + local gdiForces = GDI.GetActors() Utils.Do(gdiForces, function(a) if a.Type ~= "player" then From b290df22fb2ce15bd1397ae5c9313fc83ab99c3d Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Fri, 26 Dec 2025 11:03:04 +0000 Subject: [PATCH 04/28] Speculative AI Nuke Cannon behaviour fix. --- mods/ca/rules/vehicles.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mods/ca/rules/vehicles.yaml b/mods/ca/rules/vehicles.yaml index 6dc8c50aec..6504229823 100644 --- a/mods/ca/rules/vehicles.yaml +++ b/mods/ca/rules/vehicles.yaml @@ -216,7 +216,7 @@ NUKC: Voice: Move ImmovableCondition: deployed RequireForceMoveCondition: !undeployed - PauseOnCondition: being-captured || empdisable || being-warped || driver-dead || notmobile || deploying || undeploying + PauseOnCondition: being-captured || empdisable || being-warped || driver-dead || notmobile || deploying || undeploying || deployed RevealsShroud: MinRange: 4c0 Range: 5c0 @@ -280,11 +280,11 @@ NUKC: Sequence: undeploy Body: body WithFacingSpriteBody: - RequiresCondition: undeployed + RequiresCondition: undeployed || deploying WithFacingSpriteBody@DeployedChassis: Name: deployed Sequence: deployed - RequiresCondition: !undeployed + RequiresCondition: !undeployed && !deploying WithFacingSpriteBody@Overlay: Name: overlay Sequence: overlay From 30b2f29c8b665e57faad4fb847efcade8845cc01 Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Sat, 27 Dec 2025 10:12:40 +0000 Subject: [PATCH 05/28] Encyclopedia effects fix. --- .../Traits/Modifiers/WithColoredOverlayCA.cs | 18 +++---- .../Traits/Modifiers/WithPalettedOverlay.cs | 2 +- .../Traits/Render/WithPreviewDecoration.cs | 1 - .../Widgets/ActorPreviewCAWidget.cs | 47 +++++++++++++++---- 4 files changed, 49 insertions(+), 19 deletions(-) diff --git a/OpenRA.Mods.CA/Traits/Modifiers/WithColoredOverlayCA.cs b/OpenRA.Mods.CA/Traits/Modifiers/WithColoredOverlayCA.cs index f4c3046dab..7c8b6479d9 100644 --- a/OpenRA.Mods.CA/Traits/Modifiers/WithColoredOverlayCA.cs +++ b/OpenRA.Mods.CA/Traits/Modifiers/WithColoredOverlayCA.cs @@ -9,6 +9,7 @@ #endregion using System.Collections.Generic; +using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; @@ -94,19 +95,18 @@ public WithColoredOverlayPreviewModifier(WithColoredOverlayCAInfo info) public IEnumerable ModifyPreviewRender(WorldRenderer wr, IEnumerable renderables, Rectangle bounds) { - var result = new List(); - - foreach (var r in renderables) + foreach (var r in renderables.ToList()) { - result.Add(r); + yield return r; - // For preview rendering, we apply tint to all modifyable renderables - // (unlike in-game where we skip decorations) if (r is IModifyableRenderable mr) - result.Add(mr.WithTint(tint, mr.TintModifiers | TintModifiers.ReplaceColor).WithAlpha(alpha)); + { + var currentTintModifiers = mr.TintModifiers; + yield return mr.WithTint(tint, currentTintModifiers | TintModifiers.ReplaceColor) + .WithAlpha(alpha) + .AsDecoration(); + } } - - return result; } public void Tick() { } diff --git a/OpenRA.Mods.CA/Traits/Modifiers/WithPalettedOverlay.cs b/OpenRA.Mods.CA/Traits/Modifiers/WithPalettedOverlay.cs index eafed33c46..430970d4c1 100644 --- a/OpenRA.Mods.CA/Traits/Modifiers/WithPalettedOverlay.cs +++ b/OpenRA.Mods.CA/Traits/Modifiers/WithPalettedOverlay.cs @@ -117,7 +117,7 @@ public IEnumerable ModifyPreviewRender(WorldRenderer wr, IEnumerabl yield return r; // For preview rendering, apply palette swap to all paletted renderables - // (unlike in-game where we skip decorations) + // (unlike in-game where we skip decorations, because the colored overlay used for customising the actor color is a decoration) if (palette != null && r is IPalettedRenderable pr) yield return pr.WithPalette(palette) .WithZOffset(r.ZOffset + 1) diff --git a/OpenRA.Mods.CA/Traits/Render/WithPreviewDecoration.cs b/OpenRA.Mods.CA/Traits/Render/WithPreviewDecoration.cs index 6fad1be597..f915acd4b4 100644 --- a/OpenRA.Mods.CA/Traits/Render/WithPreviewDecoration.cs +++ b/OpenRA.Mods.CA/Traits/Render/WithPreviewDecoration.cs @@ -13,7 +13,6 @@ using OpenRA.Graphics; using OpenRA.Mods.Common.Traits.Render; using OpenRA.Primitives; -using OpenRA.Traits; namespace OpenRA.Mods.CA.Traits.Render { diff --git a/OpenRA.Mods.CA/Widgets/ActorPreviewCAWidget.cs b/OpenRA.Mods.CA/Widgets/ActorPreviewCAWidget.cs index b4def9ff4f..79a4b6c0aa 100644 --- a/OpenRA.Mods.CA/Widgets/ActorPreviewCAWidget.cs +++ b/OpenRA.Mods.CA/Widgets/ActorPreviewCAWidget.cs @@ -26,6 +26,7 @@ namespace OpenRA.Mods.CA.Widgets /// - Arbitrary player colors (not tied to map players) /// - Preview rendering of overlay traits like WithColoredOverlay, WithPalettedOverlay /// - Automatic palette swapping for player-colored sprites to use encyclopedia palette + /// Uses Render() instead of RenderUI() to get SpriteRenderables which already support IModifyableRenderable /// public class ActorPreviewCAWidget : Widget { @@ -110,14 +111,40 @@ public void SetPreview(ActorInfo actor, TypeDictionary td, Color? color = null) public override void PrepareRenderables() { - var scale = GetScale() * viewportSizes.DefaultScale; var origin = RenderOrigin + PreviewOffset + new int2(RenderBounds.Size.Width / 2, RenderBounds.Size.Height / 2); - IEnumerable baseRenderables = preview - .SelectMany(p => p.RenderUI(worldRenderer, origin, scale)); + // Calculate a world position that will project to our desired screen location + // We need to reverse the viewport projection + var centerPos = worldRenderer.Viewport.ViewToWorldPx(origin); + var worldPos = worldRenderer.ProjectedPosition(centerPos); + + // Use Render() instead of RenderUI() to get SpriteRenderables which already implement IModifyableRenderable + // This allows WithColoredOverlayCA and alpha modifications to work automatically + var baseRenderables = preview + .SelectMany(p => p.Render(worldRenderer, worldPos)) + .Select(r => + { + // Apply encyclopedia scale to SpriteRenderables + if (r is SpriteRenderable sr && GetScale() != 1f) + { + var scaleFactor = GetScale(); + return new SpriteRenderable( + sr.GetType().GetField("sprite", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(sr) as Sprite, + sr.Pos, + sr.Offset, + sr.ZOffset, + sr.Palette, + (float)sr.GetType().GetField("scale", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(sr) * scaleFactor, + sr.Alpha, + sr.Tint, + sr.TintModifiers, + sr.IsDecoration); + } + return r; + }) + .ToList(); // Calculate scaled bounds for modifiers - // The bounds are centered around origin, scaled appropriately var scaledBounds = new Rectangle( origin.X - (int)(IdealPreviewSize.X * GetScale() / 2), origin.Y - (int)(IdealPreviewSize.Y * GetScale() / 2), @@ -126,12 +153,11 @@ public override void PrepareRenderables() // Apply preview modifiers foreach (var modifier in previewModifiers) - baseRenderables = modifier.ModifyPreviewRender(worldRenderer, baseRenderables, scaledBounds); + baseRenderables = modifier.ModifyPreviewRender(worldRenderer, baseRenderables, scaledBounds).ToList(); // Swap player palettes to encyclopedia palettes for proper coloring - baseRenderables = baseRenderables.Select(r => SwapPlayerPalette(r)); - renderables = baseRenderables + .Select(r => SwapPlayerPalette(r)) .OrderBy(WorldRenderer.RenderableZPositionComparisonKey) .Select(r => r.PrepareRender(worldRenderer)) .ToArray(); @@ -165,7 +191,12 @@ IRenderable SwapPlayerPalette(IRenderable r) paletteCache[newPaletteName] = newPalette; } - return pr.WithPalette(newPalette); + var ret = (IRenderable)pr.WithPalette(newPalette); + + if (r is IModifyableRenderable mr && ret is IModifyableRenderable retMr) + ret = retMr.WithAlpha(mr.Alpha).WithTint(mr.Tint, mr.TintModifiers); + + return ret; } } From 36d1578284dc21259bc86cecf486c86cf1a83287 Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Sat, 27 Dec 2025 14:40:38 +0000 Subject: [PATCH 06/28] Treachery co-op fix. --- .../ca14-treachery-coop/treachery-coop.lua | 13 +++++++++++++ .../ca27-emancipation-coop/emancipation-coop.lua | 2 ++ .../main-campaign/ca07-conspiracy/conspiracy.lua | 8 ++++---- .../main-campaign/ca14-treachery/treachery.lua | 8 ++++---- .../ca27-emancipation/emancipation.lua | 8 ++++---- 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/mods/ca/missions/coop-campaign/ca14-treachery-coop/treachery-coop.lua b/mods/ca/missions/coop-campaign/ca14-treachery-coop/treachery-coop.lua index 8dcf9fab27..b466f58796 100644 --- a/mods/ca/missions/coop-campaign/ca14-treachery-coop/treachery-coop.lua +++ b/mods/ca/missions/coop-campaign/ca14-treachery-coop/treachery-coop.lua @@ -42,3 +42,16 @@ end AfterTick = function() end + +TransferAbandonedBase = function() + local baseBuildings = Map.ActorsInBox(AbandonedBaseTopLeft.CenterPosition, AbandonedBaseBottomRight.CenterPosition, function(a) + return a.Owner == USSRAbandoned + end) + + Utils.Do(baseBuildings, function(a) + a.Owner = GetFirstActivePlayer() + end) + + CACoopQueueSyncer() + StopSpread = false +end diff --git a/mods/ca/missions/coop-campaign/ca27-emancipation-coop/emancipation-coop.lua b/mods/ca/missions/coop-campaign/ca27-emancipation-coop/emancipation-coop.lua index d1f1f67210..2333b9042e 100644 --- a/mods/ca/missions/coop-campaign/ca27-emancipation-coop/emancipation-coop.lua +++ b/mods/ca/missions/coop-campaign/ca27-emancipation-coop/emancipation-coop.lua @@ -63,4 +63,6 @@ FreeSlaves = function(slaves) end) end end) + + CACoopQueueSyncer() end diff --git a/mods/ca/missions/main-campaign/ca07-conspiracy/conspiracy.lua b/mods/ca/missions/main-campaign/ca07-conspiracy/conspiracy.lua index e0872b28dd..496523c467 100644 --- a/mods/ca/missions/main-campaign/ca07-conspiracy/conspiracy.lua +++ b/mods/ca/missions/main-campaign/ca07-conspiracy/conspiracy.lua @@ -355,10 +355,6 @@ AwakenSleeperCell = function() GDIHarvester.Owner = Nod end - Trigger.AfterDelay(1, function() - Actor.Create("QueueUpdaterDummy", true, { Owner = Nod }) - end) - if ObjectiveRescueResearchers == nil then ObjectiveRescueResearchers = Nod.AddObjective("Locate and rescue Nod researchers.") end @@ -389,6 +385,10 @@ TransferLegionForces = function() self.Owner = Nod end end) + + Trigger.AfterDelay(1, function() + Actor.Create("QueueUpdaterDummy", true, { Owner = Nod }) + end) end InitEvacSite = function() diff --git a/mods/ca/missions/main-campaign/ca14-treachery/treachery.lua b/mods/ca/missions/main-campaign/ca14-treachery/treachery.lua index 97e0f87182..5a05840dd6 100644 --- a/mods/ca/missions/main-campaign/ca14-treachery/treachery.lua +++ b/mods/ca/missions/main-campaign/ca14-treachery/treachery.lua @@ -327,10 +327,6 @@ AbandonedBaseDiscovered = function() InitAlliedAttacks() - Trigger.AfterDelay(1, function() - Actor.Create("QueueUpdaterDummy", true, { Owner = USSR }) - end) - Trigger.AfterDelay(ReinforcementsDelay[Difficulty], function() PlaySpeechNotificationToMissionPlayers("ReinforcementsArrived") Beacon.New(USSR, ReinforcementsDestination.CenterPosition) @@ -355,6 +351,10 @@ TransferAbandonedBase = function() Utils.Do(baseBuildings, function(a) a.Owner = USSR end) + + Trigger.AfterDelay(1, function() + Actor.Create("QueueUpdaterDummy", true, { Owner = USSR }) + end) end TraitorHQKilledOrCaptured = function() diff --git a/mods/ca/missions/main-campaign/ca27-emancipation/emancipation.lua b/mods/ca/missions/main-campaign/ca27-emancipation/emancipation.lua index f36c305686..b604c3956f 100644 --- a/mods/ca/missions/main-campaign/ca27-emancipation/emancipation.lua +++ b/mods/ca/missions/main-campaign/ca27-emancipation/emancipation.lua @@ -202,10 +202,6 @@ WorldLoaded = function() UpdateObjectiveText() FreeSlaves(slaves) - Trigger.AfterDelay(1, function() - Actor.Create("QueueUpdaterDummy", true, { Owner = GDI }) - end) - if m == Mastermind4 then Utils.Do(MissionPlayers, function(p) Actor.Create("amcv.enabled", true, { Owner = p }) @@ -374,4 +370,8 @@ FreeSlaves = function(slaves) end) end end) + + Trigger.AfterDelay(1, function() + Actor.Create("QueueUpdaterDummy", true, { Owner = GDI }) + end) end From a1821ba2902c415d76151802eb2e3e3b40b41af6 Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Sun, 28 Dec 2025 22:05:57 +0000 Subject: [PATCH 07/28] - Tiger Guard will fire at buildings under fog without moving within vision. - Fixed Marauder not firing from Battle Fortress. --- mods/ca/rules/infantry.yaml | 3 ++- mods/ca/rules/vehicles.yaml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mods/ca/rules/infantry.yaml b/mods/ca/rules/infantry.yaml index d7d0ffa3b1..ce976af162 100644 --- a/mods/ca/rules/infantry.yaml +++ b/mods/ca/rules/infantry.yaml @@ -1381,7 +1381,7 @@ THF: Description: Steals enemy credits and hijacks enemy vehicles. TooltipExtras: Weaknesses: • Unarmed - Attributes: • Cloaked when not moving\n• Gains vision range while cloaked. + Attributes: • Cloaked when not moving\n• Gains vision range while cloaked Valued: Cost: 500 Tooltip: @@ -5091,6 +5091,7 @@ TIGR: FacingTolerance: 32 ChargeLevel: 0,2 PauseOnCondition: being-warped || blinded || reapersnare + TargetFrozenActors: true AttackMove: Voice: Move AutoTargetPriority@HeavyPrio: diff --git a/mods/ca/rules/vehicles.yaml b/mods/ca/rules/vehicles.yaml index 6504229823..934585568e 100644 --- a/mods/ca/rules/vehicles.yaml +++ b/mods/ca/rules/vehicles.yaml @@ -8615,6 +8615,7 @@ BATF: s3: loaded s4: loaded s6: loaded-repair + mrdr: loaded evis: loaded impl: loaded stlk: loaded From c5f9eadf5a63b65714a1ba89cbce0e701367068c Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Sun, 28 Dec 2025 22:06:19 +0000 Subject: [PATCH 08/28] Reckoning co-op improvements. --- .../ca36-reckoning-coop/reckoning-coop.lua | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/mods/ca/missions/coop-campaign/ca36-reckoning-coop/reckoning-coop.lua b/mods/ca/missions/coop-campaign/ca36-reckoning-coop/reckoning-coop.lua index 7973e2198a..9d503d8a58 100644 --- a/mods/ca/missions/coop-campaign/ca36-reckoning-coop/reckoning-coop.lua +++ b/mods/ca/missions/coop-campaign/ca36-reckoning-coop/reckoning-coop.lua @@ -31,6 +31,18 @@ AfterWorldLoaded = function() Utils.Do(Utils.Where({ Multi2, Multi5 }, function(p) return p ~= nil end), function(p) Actor.Create("rebel.allegiance", true, { Owner = p }) end) + + if (Multi1 ~= nil and Multi1.IsLocalPlayer) or (Multi4 ~= nil and Multi4.IsLocalPlayer) then + Camera.Position = GDIBase.CenterPosition + end + + if Multi2 ~= nil and Multi2.IsLocalPlayer then + Camera.Position = Exterminator3Patrol1.CenterPosition + end + + if Multi5 ~= nil and Multi5.IsLocalPlayer then + Camera.Position = R4.CenterPosition + end end AfterTick = function() @@ -60,6 +72,9 @@ DistributeUnitsAndBases = function() if Multi1 ~= nil or Multi4 ~= nil then GDIActive = true + local excess = GDIHostile.GetActorsByTypes({ "gtek", "hq", "afld.gdi", "titn", "htnk.ion", "htnk.drone", "atwr" }) + Utils.Do(excess, function(a) a.Destroy() end) + local firstGdiPlayer = Multi1 ~= nil and Multi1 or Multi4 TransferBaseToPlayer(GDIHostile, firstGdiPlayer) @@ -79,10 +94,13 @@ DistributeUnitsAndBases = function() InitAttackSquad(Squads.ScrinGDIKiller, Scrin, activeGdiPlayers) end - Trigger.AfterDelay(1, function() - -- transfer top Scrin base to first Scrin player (y = 0 -> 50) - if Multi2 ~= nil or Multi5 ~= nil then - ScrinRebelsActive = true + -- transfer top Scrin base to first Scrin player (y = 0 -> 50) + if Multi2 ~= nil or Multi5 ~= nil then + ScrinRebelsActive = true + + Trigger.AfterDelay(1, function() + local excess = ScrinRebels.GetActorsByTypes({ "scrt", "nerv", "grav", "sign", "tpod" }) + Utils.Do(excess, function(a) a.Destroy() end) local firstScrinPlayer = Multi2 ~= nil and Multi2 or Multi5 @@ -113,8 +131,9 @@ DistributeUnitsAndBases = function() local activeRebelPlayers = Utils.Where({ Multi1, Multi4 }, function(p) return p ~= nil end) Squads.ScrinRebelKiller.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 40, Max = 80 }) InitAttackSquad(Squads.ScrinRebelKiller, Scrin, activeRebelPlayers) - end - end) + CACoopQueueSyncer() + end) + end CACoopQueueSyncer() end From 67755cc9743e99021c508ef6577b879a95cdbc7b Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Tue, 30 Dec 2025 13:38:06 +0000 Subject: [PATCH 09/28] Banishment queue update. --- .../coop-campaign/ca48-banishment-coop/banishment-coop.lua | 2 ++ mods/ca/missions/main-campaign/ca48-banishment/banishment.lua | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/mods/ca/missions/coop-campaign/ca48-banishment-coop/banishment-coop.lua b/mods/ca/missions/coop-campaign/ca48-banishment-coop/banishment-coop.lua index cdf4540fb2..67e84cffd0 100644 --- a/mods/ca/missions/coop-campaign/ca48-banishment-coop/banishment-coop.lua +++ b/mods/ca/missions/coop-campaign/ca48-banishment-coop/banishment-coop.lua @@ -55,6 +55,8 @@ TransferBaseActors = function(base) Utils.Do(baseActors, function(a) a.Owner = recipientPlayer end) + + CACoopQueueSyncer() end DoMcvArrival = function() diff --git a/mods/ca/missions/main-campaign/ca48-banishment/banishment.lua b/mods/ca/missions/main-campaign/ca48-banishment/banishment.lua index 67b4051c2e..cac308ba60 100644 --- a/mods/ca/missions/main-campaign/ca48-banishment/banishment.lua +++ b/mods/ca/missions/main-campaign/ca48-banishment/banishment.lua @@ -488,6 +488,10 @@ TransferBaseActors = function(base) Utils.Do(baseActors, function(a) a.Owner = Greece end) + + Trigger.AfterDelay(1, function() + Actor.Create("QueueUpdaterDummy", true, { Owner = Greece }) + end) end -- overridden in co-op version From 9017a6c3d63309936a5e176cddc5e03d3beaba41 Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Wed, 31 Dec 2025 15:27:55 +0000 Subject: [PATCH 10/28] Enable tech on Reckoning Co-op. --- .../ca36-reckoning/reckoning-rules.yaml | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/mods/ca/missions/main-campaign/ca36-reckoning/reckoning-rules.yaml b/mods/ca/missions/main-campaign/ca36-reckoning/reckoning-rules.yaml index 8ecc5bb335..5b1339614a 100644 --- a/mods/ca/missions/main-campaign/ca36-reckoning/reckoning-rules.yaml +++ b/mods/ca/missions/main-campaign/ca36-reckoning/reckoning-rules.yaml @@ -119,6 +119,126 @@ hstk.upgrade: Buildable: Prerequisites: ~player.nod, tmpl +# Enable GDI subfaction specific tech + +STWR: + Buildable: + Prerequisites: vehicles.any, ~structures.gdi + +TITN: + Buildable: + Prerequisites: gtek, ~!railgun.upgrade, ~vehicles.gdi + +TITN.RAIL: + Buildable: + Prerequisites: gtek, ~railgun.upgrade, ~vehicles.gdi + +JUGG: + Buildable: + Prerequisites: upgc, ~vehicles.gdi + +HSAM: + Buildable: + Prerequisites: anyradar, ~vehicles.gdi + +MDRN: + Buildable: + Prerequisites: anyradar, ~vehicles.gdi + +GDRN: + Buildable: + Prerequisites: ~vehicles.gdi, ~!tow.upgrade + +GDRN.TOW: + Buildable: + Prerequisites: ~vehicles.gdi, ~tow.upgrade + +WOLV: + Buildable: + Prerequisites: gtek, ~vehicles.gdi + +XO: + Buildable: + Prerequisites: gtek, ~vehicles.gdi + +PBUL: + Buildable: + Prerequisites: gtek, ~vehicles.gdi + +AURO: + Buildable: + Prerequisites: afld.gdi, gtek, ~aircraft.gdi + +gyro.upgrade: + Buildable: + Prerequisites: gtek, ~player.gdi + +abur.upgrade: + Buildable: + Prerequisites: gtek, ~player.gdi + +bdrone.upgrade: + Buildable: + Prerequisites: gtek, ~player.gdi + +railgun.upgrade: + Buildable: + Prerequisites: upgc, ~player.gdi + +ionmam.upgrade: + Buildable: + Prerequisites: upgc, !mdrone.upgrade, !hovermam.upgrade, ~player.gdi + TooltipExtras: + Attributes: \n(!) Only ONE Mammoth Tank upgrade can be chosen + +hovermam.upgrade: + Buildable: + Prerequisites: upgc, !ionmam.upgrade, !mdrone.upgrade, ~player.gdi + TooltipExtras: + Attributes: \n(!) Only ONE Mammoth Tank upgrade can be chosen + +mdrone.upgrade: + Buildable: + Prerequisites: upgc, !ionmam.upgrade, !hovermam.upgrade, ~player.gdi + TooltipExtras: + Attributes: \n(!) Only ONE Mammoth Tank upgrade can be chosen + +# Enable Scrin subfaction specific tech + +STCR: + Buildable: + Prerequisites: anyradar, ~vehicles.scrin + +LCHR: + Buildable: + Prerequisites: anyradar, ~vehicles.scrin + +RUIN: + Buildable: + Prerequisites: scrt, ~vehicles.scrin + +ATMZ: + Buildable: + Prerequisites: scrt, ~vehicles.scrin + +stellar.upgrade: + Buildable: + Prerequisites: scrt, ~player.scrin + +coalescence.upgrade: + Buildable: + Prerequisites: scrt, ~player.scrin + +RTPD: + Buildable: + Prerequisites: scrt, ~vehicles.scrin + +ENRV: + Buildable: + Prerequisites: scrt, ~aircraft.scrin + +# Misc + NERV: DetonateWeaponPower@BUZZERSWARMAI: Prerequisites: nerv From 536b7567846c6774070653a2f0aef52d639bd25b Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Wed, 31 Dec 2025 16:28:14 +0000 Subject: [PATCH 11/28] Add guards to Exterminators on Very Hard/Brutal. --- .../ca36-reckoning/reckoning.lua | 42 +++++++++++++++---- .../ca47-ad-nihilum/ad-nihilum.lua | 19 +++++++-- mods/ca/scripts/campaign.lua | 18 ++++++++ 3 files changed, 67 insertions(+), 12 deletions(-) diff --git a/mods/ca/missions/main-campaign/ca36-reckoning/reckoning.lua b/mods/ca/missions/main-campaign/ca36-reckoning/reckoning.lua index 7a0c80de66..7bc5dfd6ff 100644 --- a/mods/ca/missions/main-campaign/ca36-reckoning/reckoning.lua +++ b/mods/ca/missions/main-campaign/ca36-reckoning/reckoning.lua @@ -384,15 +384,15 @@ end SendNextExterminator = function() if NextExterminatorIndex <= ExterminatorAttackCount[Difficulty] and not Victory then - local exterminator + local exterminatorLocations if Exterminators[NextExterminatorIndex] ~= nil then - exterminator = Exterminators[NextExterminatorIndex] + exterminatorLocations = Exterminators[NextExterminatorIndex] else - exterminator = { SpawnLocation = Utils.Random({ ExterminatorSpawnWest.Location, ExterminatorSpawnEast.Location }) } + exterminatorLocations = { SpawnLocation = Utils.Random({ ExterminatorSpawnWest.Location, ExterminatorSpawnEast.Location }) } end - local wormhole = Actor.Create("wormholelg", true, { Owner = Scrin, Location = exterminator.SpawnLocation }) + local wormhole = Actor.Create("wormholelg", true, { Owner = Scrin, Location = exterminatorLocations.SpawnLocation }) Trigger.AfterDelay(DateTime.Seconds(2), function() MediaCA.PlaySound("etpd-aggro.aud", 2) @@ -406,9 +406,9 @@ SendNextExterminator = function() Notification("Exterminator Tripod detected.") end - local reinforcements = Reinforcements.Reinforce(Scrin, { "etpd" }, { exterminator.SpawnLocation }, 10, function(a) - if exterminator.Path ~= nil then - local path = exterminator.Path + local exterminator = Reinforcements.Reinforce(Scrin, { "etpd" }, { exterminatorLocations.SpawnLocation }, 10, function(a) + if exterminatorLocations.Path ~= nil then + local path = exterminatorLocations.Path a.Patrol(path) Trigger.OnIdle(a, function(self) @@ -433,7 +433,33 @@ SendNextExterminator = function() Trigger.AfterDelay(DateTime.Seconds(10), function() wormhole.Kill() end) - end) + end)[1] + + if IsVeryHardOrAbove() then + local startGuardCount + local maxGuardCount + + if Difficulty == "brutal" then + startGuardCount = 4 + maxGuardCount = 10 + else + startGuardCount = 2 + maxGuardCount = 8 + end + + local guardsList = {} + + -- starting with startGuardCount, add a guard every 10 minutes + local numGuards = math.max(startGuardCount, math.min(maxGuardCount, startGuardCount + math.floor((DateTime.GameTime - DateTime.Minutes(10)) / DateTime.Minutes(10)))) + for i = 1, numGuards do + table.insert(guardsList, "gunw") + end + + local guards = Reinforcements.Reinforce(Scrin, guardsList, { exterminatorLocations.SpawnLocation }, 15, function(g) + FollowActor(g, exterminator) + IdleHunt(g) + end) + end NextExterminatorIndex = NextExterminatorIndex + 1 diff --git a/mods/ca/missions/main-campaign/ca47-ad-nihilum/ad-nihilum.lua b/mods/ca/missions/main-campaign/ca47-ad-nihilum/ad-nihilum.lua index 68c60ad415..de2a438fa6 100644 --- a/mods/ca/missions/main-campaign/ca47-ad-nihilum/ad-nihilum.lua +++ b/mods/ca/missions/main-campaign/ca47-ad-nihilum/ad-nihilum.lua @@ -354,17 +354,28 @@ SendNextVoidEngine = function() end) end)[1] - if Difficulty == "brutal" and DateTime.GameTime > DateTime.Minutes(30) then + if IsVeryHardOrAbove() then + local startGuardCount + local maxGuardCount + + if Difficulty == "brutal" then + startGuardCount = 4 + maxGuardCount = 10 + else + startGuardCount = 2 + maxGuardCount = 8 + end + local guardsList = {} - -- starting with 4, add a guard for every 10 minutes past 30 minutes, up to a max of 10 guards - local numGuards = math.min(10, 4 + math.floor((DateTime.GameTime - DateTime.Minutes(30)) / DateTime.Minutes(10))) + -- starting with startGuardCount, add a guard every 10 minutes + local numGuards = math.max(startGuardCount, math.min(maxGuardCount, startGuardCount + math.floor((DateTime.GameTime - DateTime.Minutes(10)) / DateTime.Minutes(10)))) for i = 1, numGuards do table.insert(guardsList, "gunw") end local guards = Reinforcements.Reinforce(MaleficScrin, guardsList, { spawnLoc }, 250, function(g) - g.Guard(voidEngine) + FollowActor(g, voidEngine) IdleHunt(g) end) end diff --git a/mods/ca/scripts/campaign.lua b/mods/ca/scripts/campaign.lua index 92cb275dd5..7c3f0541a1 100644 --- a/mods/ca/scripts/campaign.lua +++ b/mods/ca/scripts/campaign.lua @@ -1612,6 +1612,24 @@ FollowSquadLeader = function(actor, squad) end end +FollowActor = function(actor, actorToFollow) + if not actor.IsDead and actor.IsInWorld then + if not actorToFollow.IsDead and actorToFollow.IsInWorld then + local possibleCells = Utils.ExpandFootprint(Utils.ExpandFootprint({ actorToFollow.Location }, true)) + local cell = Utils.Random(possibleCells) + actor.Stop() + actor.AttackMove(cell, 1) + + Trigger.AfterDelay(Utils.RandomInteger(35,65), function() + FollowActor(actor, actorToFollow) + end) + else + actor.Stop() + Trigger.ClearAll(actor) + end + end +end + SetupRefAndSilosCaptureCredits = function(player) local silosAndRefineries = player.GetActorsByTypes(CashRewardOnCaptureTypes) Utils.Do(silosAndRefineries, function(a) From bd4123d0139dec6206d3022603cf9fb3422e058d Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Wed, 31 Dec 2025 20:21:48 +0000 Subject: [PATCH 12/28] Blind affects Exterminator (same as Void Engine). --- mods/ca/rules/custom/campaign-rules.yaml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/mods/ca/rules/custom/campaign-rules.yaml b/mods/ca/rules/custom/campaign-rules.yaml index 416f43eb1f..4e7dd1a755 100644 --- a/mods/ca/rules/custom/campaign-rules.yaml +++ b/mods/ca/rules/custom/campaign-rules.yaml @@ -673,6 +673,8 @@ ETPD: FireDelay: 22 MuzzleSequence: muzzle MuzzlePalette: scrin + AttackTurreted: + PauseOnCondition: empdisable || being-warped Health: HP: 900000 Shielded: @@ -707,8 +709,14 @@ ETPD: RequiresCondition: shields-up Targetable@DriverKillImmune: TargetTypes: DriverKillImmune - Targetable@BlindImmune: - TargetTypes: BlindImmune + RevealsShroudMultiplier@Blinded: + Modifier: 80 + RangeMultiplier@Blinded: + Modifier: 80 + RequiresCondition: blinded + FirepowerMultiplier@Blinded: + Modifier: 90 + RequiresCondition: blinded SpeedMultiplier@TEMPORAL: Modifier: 75 ReloadDelayMultiplier@TEMPORAL: From 965bbbb14788008a1380ebb88e9c8be2383b5cc7 Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Thu, 1 Jan 2026 10:02:25 +0000 Subject: [PATCH 13/28] Fix Juncture Co-op. --- .../ca-prologue-04-coop/juncture-coop.lua | 20 ++++++++++++++++ .../ca-prologue-04-coop/map.yaml | 2 +- .../ca-prologue-04/juncture-rules.yaml | 3 +++ .../main-campaign/ca-prologue-04/juncture.lua | 24 ++++++++++++------- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/mods/ca/missions/coop-campaign/ca-prologue-04-coop/juncture-coop.lua b/mods/ca/missions/coop-campaign/ca-prologue-04-coop/juncture-coop.lua index 49b20fc6b0..ebabfce80d 100644 --- a/mods/ca/missions/coop-campaign/ca-prologue-04-coop/juncture-coop.lua +++ b/mods/ca/missions/coop-campaign/ca-prologue-04-coop/juncture-coop.lua @@ -23,3 +23,23 @@ end AfterTick = function() end + +WarpInBanshees = function() + local firstActivePlayer = GetFirstActivePlayer() + if firstActivePlayer == nil then + return + end + + local hpad1 = Actor.Create("hpad.td", true, { Owner = firstActivePlayer, Location = HpadSpawn1.Location }) + local hpad2 = Actor.Create("hpad.td", true, { Owner = firstActivePlayer, Location = HpadSpawn2.Location }) + + Trigger.AfterDelay(10, function() + local useFirstHpad = true + for _, player in ipairs(MissionPlayers) do + local hpad = useFirstHpad and hpad1 or hpad2 + local banshee = Actor.Create("scrn", true, { Owner = player, Location = hpad.Location, CenterPosition = hpad.CenterPosition, Facing = Angle.NorthEast }) + banshee.Move(hpad.Location) + useFirstHpad = not useFirstHpad + end + end) +end diff --git a/mods/ca/missions/coop-campaign/ca-prologue-04-coop/map.yaml b/mods/ca/missions/coop-campaign/ca-prologue-04-coop/map.yaml index c4570d6b68..794513aeff 100644 --- a/mods/ca/missions/coop-campaign/ca-prologue-04-coop/map.yaml +++ b/mods/ca/missions/coop-campaign/ca-prologue-04-coop/map.yaml @@ -40,7 +40,7 @@ Players: Bot: campaign Faction: gdi Color: F2CF74 - Enemies: Nod, Creeps + Enemies: Nod, Multi0, Multi1, Multi2, Multi3, Multi4, Multi5, Creeps PlayerReference@Multi0: Name: Multi0 Playable: True diff --git a/mods/ca/missions/main-campaign/ca-prologue-04/juncture-rules.yaml b/mods/ca/missions/main-campaign/ca-prologue-04/juncture-rules.yaml index 1ca9d2f687..8785a1ded7 100644 --- a/mods/ca/missions/main-campaign/ca-prologue-04/juncture-rules.yaml +++ b/mods/ca/missions/main-campaign/ca-prologue-04/juncture-rules.yaml @@ -42,6 +42,9 @@ JJET: MECH: Inherits@CAMPAIGNDISABLED: ^Disabled +SAB: + Inherits@CAMPAIGNDISABLED: ^Disabled + STNK.Nod: Inherits@CAMPAIGNDISABLED: ^Disabled diff --git a/mods/ca/missions/main-campaign/ca-prologue-04/juncture.lua b/mods/ca/missions/main-campaign/ca-prologue-04/juncture.lua index ebb85db58e..b8eda34c91 100644 --- a/mods/ca/missions/main-campaign/ca-prologue-04/juncture.lua +++ b/mods/ca/missions/main-campaign/ca-prologue-04/juncture.lua @@ -48,7 +48,7 @@ WorldLoaded = function() ObjectiveDestroyFrigates = Nod.AddObjective("Destroy GDI naval blockade.") Trigger.AfterDelay(DateTime.Seconds(6), function() - WarpInBanshees() + InitReinforcements() end) Trigger.OnAllKilled(frigates, function() @@ -105,7 +105,7 @@ InitGDI = function() end) end -WarpInBanshees = function() +InitReinforcements = function() if BansheesWarped then return end @@ -119,13 +119,7 @@ WarpInBanshees = function() RocksToRemove1.Destroy() RocksToRemove2.Destroy() - local hpad1 = Actor.Create("hpad.td", true, { Owner = Nod, Location = HpadSpawn1.Location }) - local hpad2 = Actor.Create("hpad.td", true, { Owner = Nod, Location = HpadSpawn2.Location }) - - Trigger.AfterDelay(10, function() - Actor.Create("scrn", true, { Owner = Nod, Location = hpad1.Location, CenterPosition = hpad1.CenterPosition, Facing = Angle.NorthEast }) - Actor.Create("scrn", true, { Owner = Nod, Location = hpad1.Location, CenterPosition = hpad2.CenterPosition, Facing = Angle.NorthEast }) - end) + WarpInBanshees() Trigger.AfterDelay(DateTime.Seconds(2), function() PlaySpeechNotificationToMissionPlayers("ReinforcementsArrived") @@ -151,3 +145,15 @@ PanToBanshees = function() PanToBansheesComplete = true end end + +-- overridden in co-op version +WarpInBanshees = function() + local hpad1 = Actor.Create("hpad.td", true, { Owner = Nod, Location = HpadSpawn1.Location }) + local hpad2 = Actor.Create("hpad.td", true, { Owner = Nod, Location = HpadSpawn2.Location }) + Trigger.AfterDelay(10, function() + local banshee1 = Actor.Create("scrn", true, { Owner = Nod, Location = hpad1.Location, CenterPosition = hpad1.CenterPosition, Facing = Angle.NorthEast }) + local banshee2 = Actor.Create("scrn", true, { Owner = Nod, Location = hpad1.Location, CenterPosition = hpad2.CenterPosition, Facing = Angle.NorthEast }) + banshee1.Move(hpad1.Location) + banshee2.Move(hpad2.Location) + end) +end From 69bfedde3679c9180d69986ee833bc837dd8741d Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Thu, 1 Jan 2026 11:45:03 +0000 Subject: [PATCH 14/28] Fix AI aircraft limits. --- OpenRA.Mods.CA/AIUtils.cs | 10 +++ .../BotModules/Squads/States/AirStatesCA.cs | 38 ++++++----- .../BotModules/UnitBuilderBotModuleCA.cs | 66 +++++++++++++++---- 3 files changed, 83 insertions(+), 31 deletions(-) diff --git a/OpenRA.Mods.CA/AIUtils.cs b/OpenRA.Mods.CA/AIUtils.cs index 3bc31e9350..4cb11a4a03 100644 --- a/OpenRA.Mods.CA/AIUtils.cs +++ b/OpenRA.Mods.CA/AIUtils.cs @@ -49,6 +49,16 @@ public static ILookup FindQueuesByCategory(Player playe .ToLookup(pq => pq.Info.Type); } + public static ILookup FindQueuesByCategory(IEnumerable players) + { + var player = players.First(); + + return player.World.ActorsWithTrait() + .Where(a => players.Contains(a.Actor.Owner) && a.Trait.Enabled) + .Select(a => a.Trait) + .ToLookup(pq => pq.Info.Type); + } + public static IEnumerable GetActorsWithTrait(World world) { return world.ActorsHavingTrait(); diff --git a/OpenRA.Mods.CA/Traits/BotModules/Squads/States/AirStatesCA.cs b/OpenRA.Mods.CA/Traits/BotModules/Squads/States/AirStatesCA.cs index 1e632ef6f1..31bdaa4fa4 100644 --- a/OpenRA.Mods.CA/Traits/BotModules/Squads/States/AirStatesCA.cs +++ b/OpenRA.Mods.CA/Traits/BotModules/Squads/States/AirStatesCA.cs @@ -8,6 +8,7 @@ */ #endregion +using System; using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common; @@ -229,23 +230,6 @@ public void Tick(SquadCA owner) return; } - var firstUnit = owner.Units.FirstOrDefault(); - var buildableInfo = firstUnit.Info.TraitInfoOrDefault(); - var limitOne = buildableInfo != null && buildableInfo.BuildLimit == 1; - var canBuildMoreOfAircraft = firstUnit != null && !limitOne && owner.SquadManager.CanBuildMoreOfAircraft(firstUnit.Info); - var waitingCount = owner.WaitingUnits.Count; - - var waitingPatience = 99; - if (waitingCount > 7) - waitingPatience = 65; - else if (waitingCount > 3) - waitingPatience = 85; - else if (waitingCount > 1) - waitingPatience = 95; - - var impatience = owner.World.LocalRandom.Next(100); - var noPatience = impatience > waitingPatience; - foreach (var a in owner.Units) { var currentActivity = a.CurrentActivity; @@ -300,7 +284,25 @@ public void Tick(SquadCA owner) owner.WaitingUnits.Add(a); } - if ((!canBuildMoreOfAircraft || noPatience) && owner.WaitingUnits.Count > 0 && owner.WaitingUnits.Count >= owner.RearmingUnits.Count) + var firstUnit = owner.Units.FirstOrDefault(); + var buildableInfo = firstUnit.Info.TraitInfoOrDefault(); + var limitOne = buildableInfo != null && buildableInfo.BuildLimit == 1; + + var waitingCount = owner.WaitingUnits.Count; + + var waitingPatience = 99; + if (waitingCount > 7) + waitingPatience = 65; + else if (waitingCount > 3) + waitingPatience = 85; + else if (waitingCount > 1) + waitingPatience = 95; + + var impatience = owner.World.LocalRandom.Next(100); + var noPatience = impatience > waitingPatience; + Func canBuildMoreOfAircraft = () => firstUnit != null && !limitOne && owner.SquadManager.CanBuildMoreOfAircraft(firstUnit.Info); + + if (owner.WaitingUnits.Count > 0 && owner.WaitingUnits.Count >= owner.RearmingUnits.Count && (noPatience || !canBuildMoreOfAircraft())) { foreach (var a in owner.WaitingUnits) if (CanAttackTarget(a, owner.TargetActor)) diff --git a/OpenRA.Mods.CA/Traits/BotModules/UnitBuilderBotModuleCA.cs b/OpenRA.Mods.CA/Traits/BotModules/UnitBuilderBotModuleCA.cs index 6e958298e1..b42b99e712 100644 --- a/OpenRA.Mods.CA/Traits/BotModules/UnitBuilderBotModuleCA.cs +++ b/OpenRA.Mods.CA/Traits/BotModules/UnitBuilderBotModuleCA.cs @@ -302,28 +302,68 @@ bool CanBuildMoreOfAircraft(ActorInfo actorInfo) if (attackAircraftInfo == null) return true; + int currentCount; var limit = Info.MaxAircraft; - var currentCount = 0; + var isAirToAir = Info.AirToAirUnits.Contains(actorInfo.Name); - if (Info.MaintainAirSuperiority) + if (Info.MaintainAirSuperiority && isAirToAir) { - var numAirToAirUnits = AIUtils.GetActorsWithTrait(player.World).Count(a => a.Owner == player && Info.AirToAirUnits.Contains(a.Info.Name)); + // Get all production queues to count queued aircraft (for allies as well) + var queues = AIUtils.FindQueuesByCategory(player.World.Players.Where(p => p.IsAlliedWith(player))); - if (Info.AirToAirUnits.Contains(actorInfo.Name)) + // Count queued air-to-air units across all queues + var queuedAirToAirCount = queues.SelectMany(g => g).SelectMany(q => q.AllQueued()) + .Count(item => Info.AirToAirUnits.Contains(item.Item)); + + var friendlyAirToAirCount = player.World.Actors.Count(a => a.Owner.RelationshipWith(player) == PlayerRelationship.Ally + && Info.AirToAirUnits.Contains(a.Info.Name)); + + var enemyAirThreatCount = player.World.Actors.Count(a => a.Owner.RelationshipWith(player) == PlayerRelationship.Enemy + && Info.AirThreatUnits.Contains(a.Info.Name)); + + currentCount = friendlyAirToAirCount + queuedAirToAirCount; + limit = Math.Max(enemyAirThreatCount + 1, limit); + + if (Info.MaxAirSuperiority > 0) + limit = Math.Min(Info.MaxAirSuperiority, limit); + } + else + { + // Get all production queues to count queued aircraft + var queues = AIUtils.FindQueuesByCategory(player); + + // Non air-to-air aircraft uses the standard aircraft limit + if (Info.MaintainAirSuperiority) { - currentCount = numAirToAirUnits; - var numFriendlyAirToAirUnits = player.World.Actors.Count(a => a.Owner.RelationshipWith(player) == PlayerRelationship.Ally && Info.AirToAirUnits.Contains(a.Info.Name)); - var numEnemyAirThreatUnits = player.World.Actors.Count(a => a.Owner.RelationshipWith(player) == PlayerRelationship.Enemy && Info.AirThreatUnits.Contains(a.Info.Name)); - limit = Math.Max(numEnemyAirThreatUnits - numFriendlyAirToAirUnits + 1, limit); + var existingNonAirToAirCount = player.World.Actors.Count(a => + a.Owner == player && + a.Info.HasTraitInfo() && + a.Info.HasTraitInfo() && + !Info.AirToAirUnits.Contains(a.Info.Name)); - if (Info.MaxAirSuperiority > 0) - limit = Math.Min(Info.MaxAirSuperiority, limit); + var queuedNonAirToAirCount = queues.SelectMany(g => g).SelectMany(q => q.AllQueued()) + .Count(item => !Info.AirToAirUnits.Contains(item.Item) && + world.Map.Rules.Actors[item.Item].HasTraitInfo()); + + currentCount = existingNonAirToAirCount + queuedNonAirToAirCount; } else - currentCount = AIUtils.GetActorsWithTrait(player.World).Count(a => a.Owner == player && a.Info.HasTraitInfo()) - numAirToAirUnits; + { + var existingAircraftCount = player.World.Actors.Count(a => + a.Owner == player && + a.Info.HasTraitInfo() && + a.Info.HasTraitInfo()); + + // Count all queued aircraft + var queuedAircraft = queues.SelectMany(g => g).SelectMany(q => q.AllQueued()) + .Count(item => world.Map.Rules.Actors[item.Item].HasTraitInfo()); + + currentCount = existingAircraftCount + queuedAircraft; + } } - else - currentCount = AIUtils.GetActorsWithTrait(player.World).Count(a => a.Owner == player && a.Info.HasTraitInfo()); + + // bot debug + TextNotificationsManager.Debug("AI: {0} aircraft count for {1}: {2}/{3}", player, actorInfo.Name, currentCount, limit); return currentCount < limit; } From a6042297ce2533c46aa77a260ef500c43591553e Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Thu, 1 Jan 2026 12:59:22 +0000 Subject: [PATCH 15/28] Multipolarity fixes. --- .../ca45-multipolarity/multipolarity.lua | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/mods/ca/missions/main-campaign/ca45-multipolarity/multipolarity.lua b/mods/ca/missions/main-campaign/ca45-multipolarity/multipolarity.lua index a7a15bde0a..c28de04aab 100644 --- a/mods/ca/missions/main-campaign/ca45-multipolarity/multipolarity.lua +++ b/mods/ca/missions/main-campaign/ca45-multipolarity/multipolarity.lua @@ -407,7 +407,7 @@ FlipAlliedBase = function() end FlipNodBase = function() - if NodBaseFlipped or SovietBaseFlipped then + if NodBaseFlipped or SovietBaseFlipped or HawthorneClaimedNodBase then return end @@ -433,7 +433,7 @@ FlipNodBase = function() end FlipSovietBase = function() - if SovietBaseFlipped or NodBaseFlipped then + if SovietBaseFlipped or NodBaseFlipped or HawthorneClaimedSovietBase then return end @@ -459,12 +459,16 @@ FlipSovietBase = function() end HawthorneClaimRandomBase = function() - if HawthorneClaimedSovietBase and HawthorneClaimedNodBase then + -- no bases left to claim + if (HawthorneClaimedSovietBase or SovietBaseFlipped) and (HawthorneClaimedNodBase or NodBaseFlipped) then return - elseif HawthorneClaimedSovietBase then + -- soviet base claimed, claim nod base + elseif HawthorneClaimedSovietBase or SovietBaseFlipped then HawthorneClaimNodBase() - elseif HawthorneClaimedNodBase then + -- nod base claimed, claim soviet base + elseif HawthorneClaimedNodBase or NodBaseFlipped then HawthorneClaimSovietBase() + -- both available, randomly choose one else local choice = Utils.Random({ "Soviet", "Nod" }) if choice == "Soviet" then @@ -476,7 +480,7 @@ HawthorneClaimRandomBase = function() end HawthorneClaimSovietBase = function() - if HawthorneClaimedSovietBase then + if HawthorneClaimedSovietBase or SovietBaseFlipped then return end @@ -534,7 +538,7 @@ HawthorneClaimSovietBase = function() end HawthorneClaimNodBase = function() - if HawthorneClaimedNodBase then + if HawthorneClaimedNodBase or NodBaseFlipped then return end @@ -548,8 +552,6 @@ HawthorneClaimNodBase = function() MediaCA.PlaySound(MissionDir .. "/hth_nodequipauto.aud", 2) end - InitAttackSquad(Squads.Nod, GDI) - local nodBaseActors = Utils.Where(Nod.GetActors(), function(a) return not a.IsDead and a.Type ~= "player" end) From 2f748b9f0af1a840ed24d821d79101342f5f2db4 Mon Sep 17 00:00:00 2001 From: Paul Schultz Date: Wed, 31 Dec 2025 17:44:55 -0600 Subject: [PATCH 16/28] feat: add support powers and ifv variants to encyclopedia Signed-off-by: Paul Schultz --- mods/ca/mod.content.yaml | 1 + mods/ca/rules/encyclopedia.yaml | 1640 +++++++++++++++++++++++++++++++ mods/ca/rules/scrin.yaml | 6 + mods/ca/sequences/powers.yaml | 275 ++++++ 4 files changed, 1922 insertions(+) create mode 100644 mods/ca/sequences/powers.yaml diff --git a/mods/ca/mod.content.yaml b/mods/ca/mod.content.yaml index 398a463a53..432cb84528 100644 --- a/mods/ca/mod.content.yaml +++ b/mods/ca/mod.content.yaml @@ -42,6 +42,7 @@ Sequences: ca|sequences/decorations.yaml ca|sequences/scrin.yaml ca|sequences/upgrades.yaml + ca|sequences/powers.yaml TileSets: ca|tilesets/snow.yaml diff --git a/mods/ca/rules/encyclopedia.yaml b/mods/ca/rules/encyclopedia.yaml index 40aaec3766..d019edd034 100644 --- a/mods/ca/rules/encyclopedia.yaml +++ b/mods/ca/rules/encyclopedia.yaml @@ -38,6 +38,1646 @@ encyclopedia.tips.general: QuantizeFacingsFromSequence: Sequence: stand +# Support Power Template - Uses build icon like upgrades (no 3D preview background) +^SupportPowerPreview: + Inherits: ^InvisibleDummy + Buildable: + Icon: icon + Encyclopedia: + EncyclopediaExtras: + HideNotProducible: true + +encyclopedia.power.chronoshift: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: chrono.power + Tooltip: + Name: Chronoshift + Encyclopedia: + Category: Allies/Support Powers + EncyclopediaExtras: + Description: Teleports up to 9 selected vehicles to a targeted location, returning them to their original location after a short time.\n\n• If killed, units are returned with 20% health\n• Units with larger health pools take additional damage\n• Passengers cannot be unloaded\n• Enemy units are teleported for reduced duration + TooltipExtras: + Attributes: Cooldown: 3:00\nRequires: Chronosphere + +encyclopedia.power.forceshield: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: forceshield.power + Tooltip: + Name: Force Shield + Encyclopedia: + Category: Allies/Support Powers + EncyclopediaExtras: + Description: Makes selected friendly structures temporarily invulnerable.\n\nWarning: Causes power failure. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: Allied Tech Center + +encyclopedia.power.spysatellite: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: gps.power + Tooltip: + Name: Spy Satellite + Encyclopedia: + Category: Allies/Support Powers + EncyclopediaExtras: + Description: Periodically reveals the entire map for a short time (activated automatically). + TooltipExtras: + Attributes: Cooldown: 3:30\nRequires: Allied Tech Center + +encyclopedia.power.timewarp: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: timewarp.power + Tooltip: + Name: Time Warp + Encyclopedia: + Category: Allies/Support Powers + EncyclopediaExtras: + Description: Disrupts time and space at the target location. Affected units & structures are frozen in time, unable to act, but immune to damage. + TooltipExtras: + Attributes: Cooldown: 3:00\nRequires: Chronosphere + +encyclopedia.power.tempinc: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: tempinc.power + Tooltip: + Name: Temporal Incursion + Encyclopedia: + Category: Allies/Support Powers + EncyclopediaExtras: + Description: Summons reinforcements from the future. Units return to their origin time after a short time. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: Chronosphere + +encyclopedia.power.veilofwar: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: veilofwar.power + Tooltip: + Name: Veil of War + Encyclopedia: + Category: Allies/Support Powers + EncyclopediaExtras: + Description: Creates an expanding area of shroud, reducing the vision and weapon range of enemy units and defenses. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: Gap Generator + +encyclopedia.power.clustermines: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: cmines.power + Tooltip: + Name: Cluster Mines + Encyclopedia: + Category: Allies/Support Powers + EncyclopediaExtras: + Description: Sends a cargo plane to drop a minefield at the target location. + TooltipExtras: + Attributes: Cooldown: 4:30\nRequires: Airfield (France) + +encyclopedia.power.strafe: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: strafe.power + Tooltip: + Name: Strafing Run + Encyclopedia: + Category: Allies/Support Powers + EncyclopediaExtras: + Description: Calls in P51 ground attack planes to perform strafing runs on the target. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: Airfield + +encyclopedia.power.cryostorm: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: cryostorm.power + Tooltip: + Name: Cryostorm + Encyclopedia: + Category: Allies/Support Powers + EncyclopediaExtras: + Description: Creates a turbulent area of extreme cold, reducing movement speed and increasing damage taken. + TooltipExtras: + Attributes: Cooldown: 5:30\nRequires: Weather Controller + +encyclopedia.power.heliosbomb: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: heliosbomb.power + Tooltip: + Name: Helios Bomb + Encyclopedia: + Category: Allies/Support Powers + EncyclopediaExtras: + Description: Calls in a bomber which drops a Helios bomb, blinding units in a large area. + TooltipExtras: + Attributes: Cooldown: 7:00\nRequires: Airfield + +encyclopedia.power.bsky: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: bsky.power + Tooltip: + Name: Black Sky Strike + Encyclopedia: + Category: Allies/Support Powers + EncyclopediaExtras: + Description: Fires long range guided missiles at multiple ground targets prioritized by value. Tracking can be lost if targets move far enough from their initial location. + TooltipExtras: + Attributes: Cooldown: 6:00\nRequires: Black Sky Command + +# Soviets Support Powers +encyclopedia.power.ironcurtain: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: invuln.power + Tooltip: + Name: Iron Curtain + Encyclopedia: + Category: Soviets/Support Powers + EncyclopediaExtras: + Description: Makes selected friendly vehicles temporarily invulnerable. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: Iron Curtain + +encyclopedia.power.lightningstorm: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: storm.power + Tooltip: + Name: Lightning Storm + Encyclopedia: + Category: Soviets/Support Powers + EncyclopediaExtras: + Description: Initiate a Lightning Storm which deals heavy damage over a large area. + TooltipExtras: + Attributes: Cooldown: 9:00\nRequires: Weather Controller + +encyclopedia.power.atombomb: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: abomb.power + Tooltip: + Name: Atom Bomb + Encyclopedia: + Category: Soviets/Support Powers + EncyclopediaExtras: + Description: Launches a devastating atomic bomb at the target location, dealing heavy damage over a large area. + TooltipExtras: + Attributes: Cooldown: 9:00\nRequires: Missile Silo + +encyclopedia.power.spyplane: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: spyplane.power + Tooltip: + Name: Spy Plane + Encyclopedia: + Category: Soviets/Support Powers + EncyclopediaExtras: + Description: Dispatches a spy plane that reveals the target location for a limited time. + TooltipExtras: + Attributes: Cooldown: 2:00\nRequires: Radar + +encyclopedia.power.paratroopers: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: paratroopers.power + Tooltip: + Name: Paratroopers + Encyclopedia: + Category: Soviets/Support Powers + EncyclopediaExtras: + Description: Dispatches a Halo transport to drop a squad of infantry anywhere on the map. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: Airfield + +encyclopedia.power.stormtroopers: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: stormtroopers.power + Tooltip: + Name: Storm-troopers + Encyclopedia: + Category: Soviets/Support Powers + EncyclopediaExtras: + Description: Dispatches a Halo transport to drop a squad of Shock Troopers anywhere on the map. + TooltipExtras: + Attributes: Cooldown: 6:00\nRequires: Tesla Reactor + +encyclopedia.power.parabombs: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: parabombs.power + Tooltip: + Name: Parabombs + Encyclopedia: + Category: Soviets/Support Powers + EncyclopediaExtras: + Description: Calls in a Badger bomber which drops parachuted bombs on your target. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: Airfield + +encyclopedia.power.carpetbomb: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: carpetbomb.power + Tooltip: + Name: Carpet Bomb + Encyclopedia: + Category: Soviets/Support Powers + EncyclopediaExtras: + Description: Calls in a squad of Badgers which drop bombs on your target. + TooltipExtras: + Attributes: Cooldown: 7:00\nRequires: Soviet Tech Center + +encyclopedia.power.atomicbombair: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: abombair.power + Tooltip: + Name: Atomic Bomb (Air) + Encyclopedia: + Category: Soviets/Support Powers + EncyclopediaExtras: + Description: Calls in a Badger bomber which drops an atom bomb on your target. + TooltipExtras: + Attributes: Cooldown: 7:00\nRequires: Soviet Tech Center + +encyclopedia.power.mutabomb: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: mutabomb.power + Tooltip: + Name: Muta Bomb + Encyclopedia: + Category: Soviets/Support Powers + EncyclopediaExtras: + Description: Calls in a Badger bomber which drops a genetic mutation bomb on your target, transforming infantry into Brutes under your control. + TooltipExtras: + Attributes: Cooldown: 4:30\nRequires: Bio-Research Facility + +encyclopedia.power.chaosbombs: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: chaosbombs.power + Tooltip: + Name: Chaos Bombs + Encyclopedia: + Category: Soviets/Support Powers + EncyclopediaExtras: + Description: Calls in a Badger bomber which drops parachuted chaos bombs on your target. + TooltipExtras: + Attributes: Cooldown: 5:30\nRequires: Airfield + +encyclopedia.power.atomicammo: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: atomicammo.power + Tooltip: + Name: Atomic Shells + Encyclopedia: + Category: Soviets/Support Powers + EncyclopediaExtras: + Description: Grants tanks a limited supply of atomic shells (also for a limited duration).\n\nAffects Soviet tanks only (Heavy/Rhino Tank, Lasher/Thrasher Tank, Siege Tank, Mammoth Tank, Overlord Tank, Apocalypse Tank, Nuke Cannon) + TooltipExtras: + Attributes: Cooldown: 6:00\nRequires: Missile Silo + +encyclopedia.power.heroes: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: heroes.power + Tooltip: + Name: Heroes of the Union + Encyclopedia: + Category: Soviets/Support Powers + EncyclopediaExtras: + Description: Targeted basic infantry units become Heroes of the Union, significantly increasing their damage, speed, range and resilience.\n\nCan affect Rifle Infantry, Rocket Soldiers, Grenadiers and Flamethrowers. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: Soviet Tech Center + +encyclopedia.power.tankdrop: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: tankdrop.power + Tooltip: + Name: Tank Drop + Encyclopedia: + Category: Soviets/Support Powers + EncyclopediaExtras: + Description: Dispatches cargo planes to air drop tanks at the target location. + TooltipExtras: + Attributes: Cooldown: 9:00\nRequires: Soviet Tech Center + +encyclopedia.power.killzone: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: killzone.power + Tooltip: + Name: Kill Zone + Encyclopedia: + Category: Soviets/Support Powers + EncyclopediaExtras: + Description: Calls in a Spy Plane which marks a target area. Any enemy units within it take increased damage. + TooltipExtras: + Attributes: Cooldown: 3:00\nRequires: Radar + +# GDI Support Powers +encyclopedia.power.ioncannon: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: ioncannon.power + Tooltip: + Name: Ion Cannon + Encyclopedia: + Category: GDI/Support Powers + EncyclopediaExtras: + Description: Fires a devastating ion cannon beam at the target location. + TooltipExtras: + Attributes: Cooldown: 7:00\nRequires: Ion Cannon Uplink + +encyclopedia.power.recondrone: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: uavicon.power + Tooltip: + Name: Recon Drone + Encyclopedia: + Category: GDI/Support Powers + EncyclopediaExtras: + Description: A drone flies across the map, revealing the area as it passes.\n\nDetects cloaked units. + TooltipExtras: + Attributes: Cooldown: 2:30\nRequires: Radar + +encyclopedia.power.xodrop: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: xodrop.power + Tooltip: + Name: X-O Drop + Encyclopedia: + Category: GDI/Support Powers + EncyclopediaExtras: + Description: An Orca transport drops a squad of X-O Powersuits at the target location. + TooltipExtras: + Attributes: Cooldown: 6:30\nRequires: GDI Tech Center + +encyclopedia.power.droppods: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: droppods.power + Tooltip: + Name: Drop Pods + Encyclopedia: + Category: GDI/Support Powers + EncyclopediaExtras: + Description: Instantly deploys a small team of elite soldiers at the target location via orbital drop pods. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: GDI Tech Center + +encyclopedia.power.reinforcements: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: orcaca.power + Tooltip: + Name: Reinforcements + Encyclopedia: + Category: GDI/Support Powers + EncyclopediaExtras: + Description: An Orca Carryall drops an APC containing an infantry squad at the target location. + TooltipExtras: + Attributes: Cooldown: 4:00\nRequires: Airfield + +encyclopedia.power.interceptors: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: airsupport.power + Tooltip: + Name: Interceptors + Encyclopedia: + Category: GDI/Support Powers + EncyclopediaExtras: + Description: A squadron of Interceptors provides air cover over the target area, engaging any enemy aircraft within range. + TooltipExtras: + Attributes: Cooldown: 7:00\nRequires: Airfield + +encyclopedia.power.naniterepair: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: nrepair.power + Tooltip: + Name: Nanite Repair + Encyclopedia: + Category: GDI/Support Powers + EncyclopediaExtras: + Description: Repairs selected damaged vehicles over time. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: GDI Tech Center + +encyclopedia.power.firestorm: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: fstorm.power + Tooltip: + Name: Firestorm + Encyclopedia: + Category: GDI/Support Powers + EncyclopediaExtras: + Description: Fires a barrage of rockets at the target area. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: GDI Tech Center + +encyclopedia.power.advancedradar: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: arscan.power + Tooltip: + Name: Advanced Radar + Encyclopedia: + Category: GDI/Support Powers + EncyclopediaExtras: + Description: Reveals all enemy units on the radar for a limited time. + TooltipExtras: + Attributes: Cooldown: 4:00\nRequires: GDI Tech Center + +encyclopedia.power.naniteshield: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: nshield.power + Tooltip: + Name: Nanite Shield + Encyclopedia: + Category: GDI/Support Powers + EncyclopediaExtras: + Description: Creates a protective nanite shield around selected units. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: GDI Tech Center + +encyclopedia.power.empmissile: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: empmissile.power + Tooltip: + Name: EMP Missile + Encyclopedia: + Category: GDI/Support Powers + EncyclopediaExtras: + Description: Launches an EMP missile that disables vehicles and structures in the target area. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: EMP Cannon + +encyclopedia.power.surgicalstrike: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: surgicalstrike.power + Tooltip: + Name: Surgical Strike + Encyclopedia: + Category: GDI/Support Powers + EncyclopediaExtras: + Description: Calls in precision airstrikes on the target. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: Airfield + +# Nod Support Powers +encyclopedia.power.clustermissile: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: clustermissile.power + Tooltip: + Name: Cluster Missile + Encyclopedia: + Category: Nod/Support Powers + EncyclopediaExtras: + Description: Launches cluster missiles at the target area. + TooltipExtras: + Attributes: Cooldown: 6:00\nRequires: Missile Silo + +encyclopedia.power.chemmissile: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: chemmissile.power + Tooltip: + Name: Chemical Missile + Encyclopedia: + Category: Nod/Support Powers + EncyclopediaExtras: + Description: Launches a chemical missile that creates a toxic cloud at the target location. + TooltipExtras: + Attributes: Cooldown: 7:00\nRequires: Chemical Plant + +encyclopedia.power.tibstealth: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: invis.power + Tooltip: + Name: Tib Stealth + Encyclopedia: + Category: Nod/Support Powers + EncyclopediaExtras: + Description: Makes selected units invisible for a limited time. + TooltipExtras: + Attributes: Cooldown: 4:00\nRequires: Stealth Generator + +encyclopedia.power.airdrop: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: airdrop.power + Tooltip: + Name: Air Drop + Encyclopedia: + Category: Nod/Support Powers + EncyclopediaExtras: + Description: Air drops units at the target location. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: Airfield + +encyclopedia.power.hacksat: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: hacksat.power + Tooltip: + Name: Hack Satellite + Encyclopedia: + Category: Nod/Support Powers + EncyclopediaExtras: + Description: Hacks enemy satellites to reveal their base. + TooltipExtras: + Attributes: Cooldown: 4:00\nRequires: Nod Tech Center + +encyclopedia.power.infbomb: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: infbomb.power + Tooltip: + Name: Inferno Bomb + Encyclopedia: + Category: Nod/Support Powers + EncyclopediaExtras: + Description: Calls in a bomber to drop incendiary bombs on the target. + TooltipExtras: + Attributes: Cooldown: 6:00\nRequires: Airfield + +encyclopedia.power.cashhack: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: chack.power + Tooltip: + Name: Cash Hack + Encyclopedia: + Category: Nod/Support Powers + EncyclopediaExtras: + Description: Steals credits from enemy players. + TooltipExtras: + Attributes: Cooldown: 3:00\nRequires: Operations Center + +encyclopedia.power.shadowteam: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: shadteam.power + Tooltip: + Name: Shadow Team + Encyclopedia: + Category: Nod/Support Powers + EncyclopediaExtras: + Description: Deploys a team of Shadow operatives at the target location. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: Nod Tech Center + +encyclopedia.power.frenzy: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: frenzy.power + Tooltip: + Name: Frenzy + Encyclopedia: + Category: Nod/Support Powers + EncyclopediaExtras: + Description: Sends infantry into a frenzy, increasing their attack speed. + TooltipExtras: + Attributes: Cooldown: 4:00\nRequires: Temple of Nod + +encyclopedia.power.techhack: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: techhack.power + Tooltip: + Name: Tech Hack + Encyclopedia: + Category: Nod/Support Powers + EncyclopediaExtras: + Description: Temporarily disables enemy defenses. + TooltipExtras: + Attributes: Cooldown: 4:00\nRequires: Operations Center + +encyclopedia.power.assassinsquad: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: assassinsquad.power + Tooltip: + Name: Assassin Squad + Encyclopedia: + Category: Nod/Support Powers + EncyclopediaExtras: + Description: Deploys a squad of assassins at the target location. + TooltipExtras: + Attributes: Cooldown: 6:00\nRequires: Temple of Nod + +encyclopedia.power.hackercell: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: hackercell.power + Tooltip: + Name: Hacker Cell + Encyclopedia: + Category: Nod/Support Powers + EncyclopediaExtras: + Description: Deploys a cell of hackers at the target location. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: Operations Center + +encyclopedia.power.confessorcabal: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: confessorcabal.power + Tooltip: + Name: Confessor Cabal + Encyclopedia: + Category: Nod/Support Powers + EncyclopediaExtras: + Description: Deploys a group of Confessors at the target location. + TooltipExtras: + Attributes: Cooldown: 6:00\nRequires: Temple of Nod + +encyclopedia.power.substrike: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: substrike.power + Tooltip: + Name: Subterranean Strike + Encyclopedia: + Category: Nod/Support Powers + EncyclopediaExtras: + Description: Launches a subterranean attack at the target location. + TooltipExtras: + Attributes: Cooldown: 6:00\nRequires: Nod Tech Center + +# Scrin Support Powers +encyclopedia.power.rift: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: riftpower.power + Tooltip: + Name: Rift + Encyclopedia: + Category: Scrin/Support Powers + EncyclopediaExtras: + Description: Opens a devastating rift at the target location, pulling in and damaging nearby units. + TooltipExtras: + Attributes: Cooldown: 7:00\nRequires: Rift Generator + +encyclopedia.power.suppression: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: spresspower.power + Tooltip: + Name: Suppression + Encyclopedia: + Category: Scrin/Support Powers + EncyclopediaExtras: + Description: Suppresses enemy units in the target area, reducing their effectiveness. + TooltipExtras: + Attributes: Cooldown: 4:00\nRequires: Nerve Center + +encyclopedia.power.gateway: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: gateway.power + Tooltip: + Name: Gateway + Encyclopedia: + Category: Scrin/Support Powers + EncyclopediaExtras: + Description: Opens a gateway that allows units to teleport to the target location. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: Signal Transmitter + +encyclopedia.power.anathemapower: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: anathema.power + Tooltip: + Name: Anathema + Encyclopedia: + Category: Scrin/Support Powers + EncyclopediaExtras: + Description: Marks enemy units for destruction, gradually increasing their damage output and speed but destroying them when the effect ends. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: Technology Assembler + +encyclopedia.power.owrath: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: owrath.power + Tooltip: + Name: Overlord's Wrath + Encyclopedia: + Category: Scrin/Support Powers + EncyclopediaExtras: + Description: Calls down a devastating beam attack from orbit. + TooltipExtras: + Attributes: Cooldown: 8:00\nRequires: Mothership + +encyclopedia.power.ionsurge: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: ionsurgepower.power + Tooltip: + Name: Ion Surge + Encyclopedia: + Category: Scrin/Support Powers + EncyclopediaExtras: + Description: Creates an ion storm that damages enemies in the area. + TooltipExtras: + Attributes: Cooldown: 6:00\nRequires: Storm Column + +encyclopedia.power.stormspikepower: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: stormspikepower.power + Tooltip: + Name: Storm Spike + Encyclopedia: + Category: Scrin/Support Powers + EncyclopediaExtras: + Description: Deploys a Storm Spike at the target location. + TooltipExtras: + Attributes: Cooldown: 5:00\nRequires: Nerve Center + +encyclopedia.power.buzzerswarm: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: buzzpower.power + Tooltip: + Name: Buzzer Swarm + Encyclopedia: + Category: Scrin/Support Powers + EncyclopediaExtras: + Description: Deploys a swarm of Buzzers at the target location. + TooltipExtras: + Attributes: Cooldown: 3:00\nRequires: Warp Sphere + +encyclopedia.power.ichorseed: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: ichorpower.power + Tooltip: + Name: Ichor Seed + Encyclopedia: + Category: Scrin/Support Powers + EncyclopediaExtras: + Description: Plants an Ichor seed that grows Tiberium at the target location. + TooltipExtras: + Attributes: Cooldown: 4:00\nRequires: Growth Accelerator + +encyclopedia.power.greatercoalescence: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: grclpower.power + Tooltip: + Name: Greater Coalescence + Encyclopedia: + Category: Scrin/Support Powers + EncyclopediaExtras: + Description: Summons a powerful Coalescence entity at the target location. + TooltipExtras: + Attributes: Cooldown: 7:00\nRequires: Technology Assembler + +encyclopedia.power.fleetrecall: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: fleetrecall.power + Tooltip: + Name: Fleet Recall + Encyclopedia: + Category: Scrin/Support Powers + EncyclopediaExtras: + Description: Teleports selected units back to the Mothership. + TooltipExtras: + Attributes: Cooldown: 4:00\nRequires: Mothership + +encyclopedia.power.resourcescan: + Inherits: ^SupportPowerPreview + RenderSprites: + Image: rescanpower.power + Tooltip: + Name: Resource Scan + Encyclopedia: + Category: Scrin/Support Powers + EncyclopediaExtras: + Description: Reveals Tiberium deposits on the map. + TooltipExtras: + Attributes: Cooldown: 2:00\nRequires: Extractor + +# IFV Variant Template +^IFVVariantPreview: + Inherits: ^EffectPreview + RenderSprites: + Image: ifv + WithFacingSpriteBody: + WithSpriteTurret: + Sequence: turret + Turreted: + Encyclopedia: + Scale: 1.5 + +encyclopedia.ifv.missile: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-rocket + Tooltip: + Name: Missile IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Rocket Soldier. Fires anti-air/anti-armor missiles. + TooltipExtras: + Strengths: Strong vs Aircraft, Heavy Armor + Weaknesses: Weak vs Infantry + Attributes: Passenger: Rocket Soldier + +encyclopedia.ifv.ggi: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-ggi + Tooltip: + Name: GGI IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Guardian GI. Fires powerful anti-armor rounds. + TooltipExtras: + Strengths: Strong vs Vehicles, Heavy Armor + Weaknesses: Weak vs Infantry + Attributes: Passenger: Guardian GI + +encyclopedia.ifv.mg: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-mg + Tooltip: + Name: Machine Gun IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Rifle Infantry. Fires a rapid machine gun. + TooltipExtras: + Strengths: Strong vs Infantry, Light Vehicles + Weaknesses: Weak vs Heavy Armor + Attributes: Passenger: Rifle Infantry + +encyclopedia.ifv.grenade: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-frag + Tooltip: + Name: Grenade IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Grenadier. Launches fragmentation grenades. + TooltipExtras: + Strengths: Strong vs Infantry, Buildings + Weaknesses: Weak vs Vehicles + Attributes: Passenger: Grenadier + +encyclopedia.ifv.flame: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-flame + Tooltip: + Name: Flamethrower IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Flamethrower. Projects devastating flames. + TooltipExtras: + Strengths: Strong vs Infantry, Buildings + Weaknesses: Weak vs Vehicles + Attributes: Passenger: Flamethrower + +encyclopedia.ifv.chem: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-chem + Tooltip: + Name: Chemical IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Toxin Soldier. Sprays toxic chemicals. + TooltipExtras: + Strengths: Strong vs Infantry + Weaknesses: Weak vs Vehicles, Buildings + Attributes: Passenger: Toxin Soldier + +encyclopedia.ifv.laser: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-laser + Tooltip: + Name: Laser IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with an Acolyte. Fires a powerful laser beam. + TooltipExtras: + Strengths: Strong vs Infantry, Light Vehicles + Weaknesses: Moderate vs Heavy Armor + Attributes: Passenger: Acolyte + +encyclopedia.ifv.rad: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-rad + Tooltip: + Name: Desolator IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Desolator. Sprays deadly radiation. + TooltipExtras: + Strengths: Strong vs Infantry, Vehicles + Weaknesses: Damages friendly units in range + Attributes: Passenger: Desolator + +encyclopedia.ifv.cmdo: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-cmdo + Tooltip: + Name: Commando IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Commando or SEAL. Fires a powerful sniper rifle. + TooltipExtras: + Strengths: Strong vs Infantry + Weaknesses: Weak vs Vehicles + Attributes: Passenger: Commando/SEAL + +encyclopedia.ifv.mech: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-mech + Tooltip: + Name: Repair IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Mechanic. Repairs nearby friendly vehicles. + TooltipExtras: + Strengths: Repairs vehicles + Attributes: Passenger: Mechanic + +encyclopedia.ifv.snip: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-snip + Tooltip: + Name: Sniper IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Sniper. Fires long-range sniper rounds. + TooltipExtras: + Strengths: Strong vs Infantry + Weaknesses: Weak vs Vehicles + Attributes: Passenger: Sniper + +encyclopedia.ifv.enli: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-enli + Tooltip: + Name: Enlightened IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with an Enlightened. Fires a powerful laser. + TooltipExtras: + Strengths: Strong vs Infantry, Vehicles + Attributes: Passenger: Enlightened + +encyclopedia.ifv.rmbc: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-rmbc + Tooltip: + Name: Cyborg Elite IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Cyborg Elite. Fires plasma bolts. + TooltipExtras: + Strengths: Strong vs Infantry, Vehicles + Attributes: Passenger: Cyborg Elite + +encyclopedia.ifv.bh: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-bh + Tooltip: + Name: Black Hand IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Black Hand. Projects devastating flames. + TooltipExtras: + Strengths: Strong vs Infantry, Buildings + Weaknesses: Weak vs Vehicles + Attributes: Passenger: Black Hand + +encyclopedia.ifv.pdisc: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-pdisc + Tooltip: + Name: Plasma Disc IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Disc Thrower. Fires plasma discs. + TooltipExtras: + Strengths: Strong vs Infantry, Light Vehicles + Attributes: Passenger: Disc Thrower + +encyclopedia.ifv.shard: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-shard + Tooltip: + Name: Shard IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Shard Trooper. Fires Tiberium shards. + TooltipExtras: + Strengths: Strong vs Infantry, Light Vehicles + Attributes: Passenger: Shard Trooper + +encyclopedia.ifv.mort: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-mort + Tooltip: + Name: Mortar IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Mortar Infantry. Fires mortar rounds. + TooltipExtras: + Strengths: Strong vs Infantry, Buildings + Weaknesses: Slow rate of fire + Attributes: Passenger: Mortar Infantry + +encyclopedia.ifv.cryo: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-cryo + Tooltip: + Name: Cryo IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Cryo Legionnaire. Fires freezing blasts. + TooltipExtras: + Strengths: Slows and freezes enemies + Attributes: Passenger: Cryo Legionnaire + +encyclopedia.ifv.enfo: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-enfo + Tooltip: + Name: Enforcer IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with an Enforcer. Fires shotgun blasts. + TooltipExtras: + Strengths: Strong vs Infantry at close range + Attributes: Passenger: Enforcer + +encyclopedia.ifv.engi: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-engi + Tooltip: + Name: Engineer IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with an Engineer. Can capture enemy structures. + TooltipExtras: + Strengths: Captures structures + Attributes: Passenger: Engineer + +encyclopedia.ifv.ztrp: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-ztrp + Tooltip: + Name: Zone Trooper IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Zone Trooper. Fires powerful railgun shots. + TooltipExtras: + Strengths: Strong vs Vehicles, Heavy Armor + Attributes: Passenger: Zone Trooper + +encyclopedia.ifv.zdef: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-zdef + Tooltip: + Name: Zone Defender IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Zone Defender. Fires anti-air missiles. + TooltipExtras: + Strengths: Strong vs Aircraft + Attributes: Passenger: Zone Defender + +encyclopedia.ifv.zrai: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-zrai + Tooltip: + Name: Zone Raider IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Zone Raider. Fires sonic blasts. + TooltipExtras: + Strengths: Strong vs Infantry, Structures + Attributes: Passenger: Zone Raider + +encyclopedia.ifv.tigr: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-tigr + Tooltip: + Name: Tiger Guard IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Tiger Guard. Fires dual machine guns. + TooltipExtras: + Strengths: Strong vs Infantry + Attributes: Passenger: Tiger Guard + +encyclopedia.ifv.hopl: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-hopl + Tooltip: + Name: Hoplite IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Hoplite. Fires EMP rounds. + TooltipExtras: + Strengths: Disables vehicles + Attributes: Passenger: Hoplite + +encyclopedia.ifv.impl: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-impl + Tooltip: + Name: Impaler IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with an Impaler. Fires spikes. + TooltipExtras: + Strengths: Strong vs Infantry + Attributes: Passenger: Impaler + +encyclopedia.ifv.stlk: + Inherits: ^IFVVariantPreview + RenderSprites: + Image: ifv + Palette: playerscrin + WithSpriteTurret: + Sequence: turret-stlk + Tooltip: + Name: Stalker IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Stalker. Fires plasma bolts. + TooltipExtras: + Strengths: Strong vs Infantry, Light Vehicles + Attributes: Passenger: Stalker (Scrin) + +encyclopedia.ifv.disin: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-disin + Tooltip: + Name: Disintegrator IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Disintegrator. Fires disintegration beams. + TooltipExtras: + Strengths: Strong vs Vehicles, Buildings + Attributes: Passenger: Disintegrator (Scrin) + +encyclopedia.ifv.conf: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-conf + Tooltip: + Name: Confessor IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Confessor. Fires hallucinogenic rounds. + TooltipExtras: + Strengths: Strong vs Infantry + Attributes: Passenger: Confessor (Nod) + +encyclopedia.ifv.reap: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-reap + Tooltip: + Name: Reaper IFV + Encyclopedia: + Category: Allies/Vehicles/IFV Variants + EncyclopediaExtras: + Description: IFV loaded with a Reaper. Fires Tiberium-infused rounds. + TooltipExtras: + Strengths: Strong vs Infantry + Attributes: Passenger: Reaper (Nod) + +# Reckoner Variant Template +^ReckonerVariantPreview: + Inherits: ^EffectPreview + RenderSprites: + Image: reck + WithFacingSpriteBody: + WithSpriteTurret: + Sequence: turret + Turreted: + Encyclopedia: + Scale: 1.5 + +encyclopedia.reck.missile: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-rocket + Tooltip: + Name: Missile Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Rocket Soldier. Fires anti-air/anti-armor missiles. + TooltipExtras: + Strengths: Strong vs Aircraft, Heavy Armor + Weaknesses: Weak vs Infantry + Attributes: Passenger: Rocket Soldier + +encyclopedia.reck.mg: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-mg + Tooltip: + Name: Machine Gun Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Rifle Infantry. Fires a rapid machine gun. + TooltipExtras: + Strengths: Strong vs Infantry, Light Vehicles + Weaknesses: Weak vs Heavy Armor + Attributes: Passenger: Rifle Infantry + +encyclopedia.reck.grenade: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-frag + Tooltip: + Name: Grenade Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Grenadier. Launches fragmentation grenades. + TooltipExtras: + Strengths: Strong vs Infantry, Buildings + Weaknesses: Weak vs Vehicles + Attributes: Passenger: Grenadier + +encyclopedia.reck.flame: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-flame + Tooltip: + Name: Flamethrower Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Flamethrower. Projects devastating flames. + TooltipExtras: + Strengths: Strong vs Infantry, Buildings + Weaknesses: Weak vs Vehicles + Attributes: Passenger: Flamethrower + +encyclopedia.reck.chem: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-chem + Tooltip: + Name: Chemical Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Toxin Soldier. Sprays toxic chemicals. + TooltipExtras: + Strengths: Strong vs Infantry + Weaknesses: Weak vs Vehicles, Buildings + Attributes: Passenger: Toxin Soldier + +encyclopedia.reck.laser: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-laser + Tooltip: + Name: Laser Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with an Acolyte. Fires a powerful laser beam. + TooltipExtras: + Strengths: Strong vs Infantry, Light Vehicles + Weaknesses: Moderate vs Heavy Armor + Attributes: Passenger: Acolyte + +encyclopedia.reck.rad: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-rad + Tooltip: + Name: Desolator Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Desolator. Sprays deadly radiation. + TooltipExtras: + Strengths: Strong vs Infantry, Vehicles + Weaknesses: Damages friendly units in range + Attributes: Passenger: Desolator + +encyclopedia.reck.cmdo: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-cmdo + Tooltip: + Name: Commando Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Commando. Fires a powerful sniper rifle. + TooltipExtras: + Strengths: Strong vs Infantry + Weaknesses: Weak vs Vehicles + Attributes: Passenger: Commando + +encyclopedia.reck.mech: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-mech + Tooltip: + Name: Repair Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Mechanic. Repairs nearby friendly vehicles. + TooltipExtras: + Strengths: Repairs vehicles + Attributes: Passenger: Mechanic + +encyclopedia.reck.snip: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-snip + Tooltip: + Name: Sniper Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Sniper. Fires long-range sniper rounds. + TooltipExtras: + Strengths: Strong vs Infantry + Weaknesses: Weak vs Vehicles + Attributes: Passenger: Sniper + +encyclopedia.reck.enli: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-enli + Tooltip: + Name: Enlightened Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with an Enlightened. Fires a powerful laser. + TooltipExtras: + Strengths: Strong vs Infantry, Vehicles + Attributes: Passenger: Enlightened + +encyclopedia.reck.rmbc: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-rmbc + Tooltip: + Name: Cyborg Elite Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Cyborg Elite. Fires plasma bolts. + TooltipExtras: + Strengths: Strong vs Infantry, Vehicles + Attributes: Passenger: Cyborg Elite + +encyclopedia.reck.bh: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-bh + Tooltip: + Name: Black Hand Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Black Hand. Projects devastating flames. + TooltipExtras: + Strengths: Strong vs Infantry, Buildings + Weaknesses: Weak vs Vehicles + Attributes: Passenger: Black Hand + +encyclopedia.reck.pdisc: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-pdisc + Tooltip: + Name: Plasma Disc Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Disc Thrower. Fires plasma discs. + TooltipExtras: + Strengths: Strong vs Infantry, Light Vehicles + Attributes: Passenger: Disc Thrower + +encyclopedia.reck.shard: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-shard + Tooltip: + Name: Shard Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Shard Trooper. Fires Tiberium shards. + TooltipExtras: + Strengths: Strong vs Infantry, Light Vehicles + Attributes: Passenger: Shard Trooper + +encyclopedia.reck.mort: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-mort + Tooltip: + Name: Mortar Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Mortar Infantry. Fires mortar rounds. + TooltipExtras: + Strengths: Strong vs Infantry, Buildings + Weaknesses: Slow rate of fire + Attributes: Passenger: Mortar Infantry + +encyclopedia.reck.cryo: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-cryo + Tooltip: + Name: Cryo Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Cryo Legionnaire. Fires freezing blasts. + TooltipExtras: + Strengths: Slows and freezes enemies + Attributes: Passenger: Cryo Legionnaire + +encyclopedia.reck.enfo: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-enfo + Tooltip: + Name: Enforcer Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with an Enforcer. Fires shotgun blasts. + TooltipExtras: + Strengths: Strong vs Infantry at close range + Attributes: Passenger: Enforcer + +encyclopedia.reck.engi: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-engi + Tooltip: + Name: Engineer Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with an Engineer. Can capture enemy structures. + TooltipExtras: + Strengths: Captures structures + Attributes: Passenger: Engineer + +encyclopedia.reck.conf: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-conf + Tooltip: + Name: Confessor Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Confessor. Fires hallucinogenic rounds. + TooltipExtras: + Strengths: Strong vs Infantry + Attributes: Passenger: Confessor + +encyclopedia.reck.reap: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-reap + Tooltip: + Name: Reaper Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Reaper. Fires Tiberium-infused rounds. + TooltipExtras: + Strengths: Strong vs Infantry + Attributes: Passenger: Reaper + +encyclopedia.reck.stlk: + Inherits: ^ReckonerVariantPreview + RenderSprites: + Image: reck + Palette: playerscrin + WithSpriteTurret: + Sequence: turret-stlk + Tooltip: + Name: Stalker Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Stalker. Fires plasma bolts. + TooltipExtras: + Strengths: Strong vs Infantry, Light Vehicles + Attributes: Passenger: Stalker (Scrin) + +encyclopedia.reck.disin: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-disin + Tooltip: + Name: Disintegrator Reckoner + Encyclopedia: + Category: Nod/Vehicles/Reckoner Variants + EncyclopediaExtras: + Description: Reckoner loaded with a Disintegrator. Fires disintegration beams. + TooltipExtras: + Strengths: Strong vs Vehicles, Buildings + Attributes: Passenger: Disintegrator (Scrin) + encyclopedia.buffs.anathema: Inherits: ^VehicleEffectPreview Tooltip: diff --git a/mods/ca/rules/scrin.yaml b/mods/ca/rules/scrin.yaml index 656f68d416..f009d50052 100644 --- a/mods/ca/rules/scrin.yaml +++ b/mods/ca/rules/scrin.yaml @@ -5889,6 +5889,8 @@ SSPK: Inherits: SCOL Tooltip: Name: Storm Spike + Encyclopedia: + Category: Scrin/Support Powers -Sellable: -Buildable: Health: @@ -5976,6 +5978,8 @@ VSPK: TooltipExtras: Attributes: • Corrupts resources\n• Damages nearby enemies over time\n• Direct damage is reflected back to the attacker Description: Gradually transforms nearby resources into Black Tiberium, which is devoid of most of its useful properties. + Encyclopedia: + Category: Scrin/Support Powers RevealsShroud: MinRange: 6c0 Range: 8c0 @@ -6033,6 +6037,8 @@ ISPK: TooltipExtras: Attributes: • Enriches Tiberium trees\n• Empowers units with Resource Conversion upgrade Description: Enriches nearby Tiberium trees causing them to seed Tiberium more quickly. + Encyclopedia: + Category: Scrin/Support Powers RevealsShroud: MinRange: 6c0 Range: 8c0 diff --git a/mods/ca/sequences/powers.yaml b/mods/ca/sequences/powers.yaml new file mode 100644 index 0000000000..06655bfa1a --- /dev/null +++ b/mods/ca/sequences/powers.yaml @@ -0,0 +1,275 @@ +# Support Power Icons for Encyclopedia +# Uses 'icon' sequence name (like upgrades) + +# Allies +chrono.power: + icon: + Filename: warpicon.shp + +forceshield.power: + icon: + Filename: forceshieldicon.shp + +gps.power: + icon: + Filename: gpssicon.shp + +timewarp.power: + icon: + Filename: timewarpicon.shp + +tempinc.power: + icon: + Filename: tempincicon.shp + +veilofwar.power: + icon: + Filename: veilofwaricnh.shp + +cmines.power: + icon: + Filename: cmineicon.shp + +strafe.power: + icon: + Filename: strafeicon.shp + +cryostorm.power: + icon: + Filename: cryostormicon.shp + +heliosbomb.power: + icon: + Filename: heliosicon.shp + +bsky.power: + icon: + Filename: bskyicon.shp + +# Soviets +invuln.power: + icon: + Filename: infxicon.shp + +storm.power: + icon: + Filename: stormicon.shp + +abomb.power: + icon: + Filename: atomicon.shp + +spyplane.power: + icon: + Filename: smigicon.shp + +paratroopers.power: + icon: + Filename: pinficon.shp + +stormtroopers.power: + icon: + Filename: stroopicon.shp + +parabombs.power: + icon: + Filename: pbmbicon.shp + +carpetbomb.power: + icon: + Filename: cbombicon.shp + +abombair.power: + icon: + Filename: abombicon.shp + +mutabomb.power: + icon: + Filename: gmutationicon.shp + +chaosbombs.power: + icon: + Filename: chaosbombicon.shp + +atomicammo.power: + icon: + Filename: atomicammoicon.shp + +heroes.power: + icon: + Filename: heroesicon.shp + +tankdrop.power: + icon: + Filename: tankdropicon.shp + +killzone.power: + icon: + Filename: killzoneicon.shp + +# GDI +ioncannon.power: + icon: + Filename: ionicon.shp + +uavicon.power: + icon: + Filename: uavicon.shp + +xodrop.power: + icon: + Filename: xodropicon.shp + +droppods.power: + icon: + Filename: droppodicnh.shp + +orcaca.power: + icon: + Filename: orcacarein.shp + +airsupport.power: + icon: + Filename: airsupicon.shp + +nrepair.power: + icon: + Filename: nrepairicon.shp + +fstorm.power: + icon: + Filename: fstormicnh.shp + +arscan.power: + icon: + Filename: arscanicnh.shp + +nshield.power: + icon: + Filename: nshieldicon.shp + +empmissile.power: + icon: + Filename: empicon.shp + +surgicalstrike.power: + icon: + Filename: surgicalsicnh.shp + +# Nod +clustermissile.power: + icon: + Filename: clustericnh.shp + +chemmissile.power: + icon: + Filename: cmissicnh.shp + +invis.power: + icon: + Filename: invisicnh.shp + +airdrop.power: + icon: + Filename: airdropicon.shp + +hacksat.power: + icon: + Filename: hacksaticon.shp + +infbomb.power: + icon: + Filename: b2bicnh.shp + +chack.power: + icon: + Filename: chackicon.shp + +shadteam.power: + icon: + Filename: shadteamicon.shp + +frenzy.power: + icon: + Filename: frenzyicon.shp + +techhack.power: + icon: + Filename: techhackicon.shp + +assassinsquad.power: + icon: + Filename: assaicnh.shp + +hackercell.power: + icon: + Filename: hackercellicon.shp + +confessorcabal.power: + icon: + Filename: confcabalicnh.shp + +substrike.power: + icon: + Filename: substrikeicon.shp + +# Scrin +riftpower.power: + icon: + Filename: riftpowericon.shp + +spresspower.power: + icon: + Filename: spresspowericon.shp + +gateway.power: + icon: + Filename: gatewayicon.shp + +anathema.power: + icon: + Filename: anathemaicon.shp + +owrath.power: + icon: + Filename: owrathicon.shp + +ionsurgepower.power: + icon: + Filename: ionsurgeicon.shp + +stormspikepower.power: + icon: + Filename: stormspikeicon.shp + +buzzpower.power: + icon: + Filename: buzzicon.shp + +ichorpower.power: + icon: + Filename: ichorpowericon.shp + +grclpower.power: + icon: + Filename: grclpowericon.shp + +fleetrecall.power: + icon: + Filename: fleetrecallicon.shp + +rescanpower.power: + icon: + Filename: rescanpowericon.shp + +ichorspike.power: + icon: + Filename: ispkicon.shp + +colonyspike.power: + icon: + Filename: cspkicon.shp + +voidspike.power: + icon: + Filename: vspkicon.shp From 4e714466aa67067374df44f5e806a4cc8beb1d74 Mon Sep 17 00:00:00 2001 From: Paul Schultz Date: Wed, 31 Dec 2025 19:02:15 -0600 Subject: [PATCH 17/28] feat: add dropdown for entity variants Signed-off-by: Paul Schultz --- OpenRA.Mods.CA/Traits/EncyclopediaExtras.cs | 6 + .../Widgets/Logic/EncyclopediaLogicCA.cs | 270 +++++- mods/ca/chrome/encyclopedia.yaml | 10 +- mods/ca/rules/encyclopedia.yaml | 793 ++++++++++++++---- 4 files changed, 927 insertions(+), 152 deletions(-) diff --git a/OpenRA.Mods.CA/Traits/EncyclopediaExtras.cs b/OpenRA.Mods.CA/Traits/EncyclopediaExtras.cs index 8556c11b27..44a02ea73e 100644 --- a/OpenRA.Mods.CA/Traits/EncyclopediaExtras.cs +++ b/OpenRA.Mods.CA/Traits/EncyclopediaExtras.cs @@ -31,6 +31,12 @@ public class EncyclopediaExtrasInfo : TraitInfo [Desc("If no Buildable Description exists, this will be shown instead.")] public readonly string Description = ""; + [Desc("Actor name this entry is a variant of (e.g., 'IFV'). Hides entry from main list.")] + public readonly string VariantOf = null; + + [Desc("Group name for variant dropdown (e.g., 'Allies Infantry').")] + public readonly string VariantGroup = null; + public override object Create(ActorInitializer init) { return new EncyclopediaExtras(init, this); } } diff --git a/OpenRA.Mods.CA/Widgets/Logic/EncyclopediaLogicCA.cs b/OpenRA.Mods.CA/Widgets/Logic/EncyclopediaLogicCA.cs index 4aafd84c12..db6fbd1057 100644 --- a/OpenRA.Mods.CA/Widgets/Logic/EncyclopediaLogicCA.cs +++ b/OpenRA.Mods.CA/Widgets/Logic/EncyclopediaLogicCA.cs @@ -80,6 +80,18 @@ public class EncyclopediaLogicCA : ChromeLogic // Build icon widget readonly SpriteWidget buildIconWidget; + // Variant dropdown widget + readonly DropDownButtonWidget variantDropdown; + + // Variant lookup - maps parent actor name to list of variant actors (case-insensitive) + readonly Dictionary> variantsByParent = new(StringComparer.OrdinalIgnoreCase); + + // Variant group order - tracks order groups were first encountered during file scan + readonly Dictionary variantGroupOrder = new(); + + // Currently selected variant (if any) + ActorInfo selectedVariant; + // Folder structure tracking readonly Dictionary folderNodes = new(); readonly Dictionary folderExpanded = new(); @@ -173,6 +185,8 @@ public EncyclopediaLogicCA(Widget widget, World world, ModData modData, Action o buildIconWidget = widget.GetOrNull("BUILD_ICON"); + variantDropdown = widget.GetOrNull("VARIANT_DROPDOWN"); + foreach (var actor in modData.DefaultRules.Actors.Values) { var statistics = actor.TraitInfoOrDefault(); @@ -189,6 +203,9 @@ public EncyclopediaLogicCA(Widget widget, World world, ModData modData, Action o foreach (var faction in world.Map.Rules.Actors[SystemActors.World].TraitInfos().Where(f => f.Selectable)) factions.Add(faction.InternalName, faction); + // Build variant lookup - find all actors that are variants of other actors + BuildVariantLookup(); + // Build folder hierarchy BuildFolderHierarchy(); @@ -260,6 +277,27 @@ void SelectFirstItem() return (null, null); } + void BuildVariantLookup() + { + foreach (var actorInfo in info) + { + var actor = actorInfo.Key; + var extras = actor.TraitInfoOrDefault(); + + if (extras?.VariantOf != null) + { + if (!variantsByParent.ContainsKey(extras.VariantOf)) + variantsByParent[extras.VariantOf] = new List(); + + variantsByParent[extras.VariantOf].Add(actor); + + // Track group order based on first encounter during file scan (only for non-null groups) + if (extras.VariantGroup != null && !variantGroupOrder.ContainsKey(extras.VariantGroup)) + variantGroupOrder[extras.VariantGroup] = variantGroupOrder.Count; + } + } + } + void BuildFolderHierarchy() { // Group actors by their category paths (actors can have multiple categories) @@ -271,6 +309,11 @@ void BuildFolderHierarchy() var encyclopedia = actorInfo.Value; var categories = encyclopedia.Category ?? ""; + // Skip variants - they are accessed via dropdown only + var extras = actor.TraitInfoOrDefault(); + if (extras?.VariantOf != null) + continue; + // Split by semicolon to allow multiple categories per actor var categoryPaths = ParseCategoryPaths(categories); @@ -508,6 +551,7 @@ void SelectActor(ActorInfo actor, string categoryPath = null) LoadExtras(actor); var selectedInfo = info[actor]; selectedActor = actor; + selectedVariant = null; currentCategoryPath = categoryPath; // Remember this actor for the current top-level category @@ -516,9 +560,11 @@ void SelectActor(ActorInfo actor, string categoryPath = null) lastSelectedActorByCategory[selectedTopLevelCategory] = actor; } + // Setup variant dropdown + SetupVariantDropdown(actor); + // Update the encyclopedia color palette with the faction color var previewColor = GetPreviewColorFromCategory(categoryPath); - EncyclopediaColorPalette.SetPreviewColor(previewColor); var previewOwner = GetPreviewOwner(selectedInfo); var typeDictionary = CreatePreviewTypeDictionary(previewOwner); @@ -921,6 +967,228 @@ void SelectActor(ActorInfo actor, string categoryPath = null) descriptionPanel.ScrollToTop(); } + void SetupVariantDropdown(ActorInfo actor) + { + if (variantDropdown == null) + return; + + // Check if this actor has variants + if (!variantsByParent.TryGetValue(actor.Name, out var variants) || variants.Count == 0) + { + variantDropdown.IsVisible = () => false; + return; + } + + variantDropdown.IsVisible = () => true; + variantDropdown.GetText = () => selectedVariant != null + ? GetActorDisplayName(selectedVariant) + : "Select Variant..."; + + variantDropdown.OnMouseDown = _ => + { + // Separate variants into grouped and ungrouped + var variantsWithGroups = variants + .Select(v => new + { + Actor = v, + Group = v.TraitInfoOrDefault()?.VariantGroup + }) + .ToList(); + + var hasAnyGroups = variantsWithGroups.Any(v => v.Group != null); + + ScrollItemWidget SetupItem(ActorInfo variantActor, ScrollItemWidget template) + { + bool IsSelected() => selectedVariant == variantActor; + void OnClick() => SelectVariant(variantActor); + + var scrollItem = ScrollItemWidget.Setup(template, IsSelected, OnClick); + var label = scrollItem.Get("LABEL"); + label.GetText = () => GetActorDisplayName(variantActor); + return scrollItem; + } + + if (!hasAnyGroups) + { + // No groups - use simple flat dropdown without headers + var flatList = variants.OrderBy(v => GetActorDisplayName(v)).ToList(); + var itemHeight = 25; + var totalHeight = Math.Min(flatList.Count * itemHeight, 300); + + variantDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", totalHeight, flatList, SetupItem); + } + else + { + // Has groups - use grouped dropdown with headers + var groupedVariants = variantsWithGroups + .Where(v => v.Group != null) + .GroupBy(v => v.Group) + .OrderBy(g => GetVariantGroupSortOrder(g.Key)) + .ToDictionary( + g => g.Key, + g => g.OrderBy(v => GetActorDisplayName(v.Actor)).Select(v => v.Actor).AsEnumerable() + ); + + // Add ungrouped variants first (with empty key, handled specially) + var ungrouped = variantsWithGroups.Where(v => v.Group == null).Select(v => v.Actor).ToList(); + if (ungrouped.Any()) + { + var orderedGrouped = new Dictionary> + { + { "", ungrouped.OrderBy(v => GetActorDisplayName(v)) } + }; + foreach (var kvp in groupedVariants) + orderedGrouped[kvp.Key] = kvp.Value; + groupedVariants = orderedGrouped; + } + + // Calculate dropdown height + var itemHeight = 25; + var headerHeight = 13; + var totalHeight = groupedVariants.Sum(g => (string.IsNullOrEmpty(g.Key) ? 0 : headerHeight) + g.Value.Count() * itemHeight); + totalHeight = Math.Min(totalHeight, 300); // Cap at 300px + + variantDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", totalHeight, groupedVariants, SetupItem); + } + }; + } + + void SelectVariant(ActorInfo variant) + { + if (variant == null || selectedActor == null) + return; + + selectedVariant = variant; + + // Update the preview to show the variant + LoadExtras(variant); + var selectedInfo = info.ContainsKey(variant) ? info[variant] : info[selectedActor]; + + var previewColor = GetPreviewColorFromCategory(currentCategoryPath); + EncyclopediaColorPalette.SetPreviewColor(previewColor); + + var previewOwner = GetPreviewOwner(selectedInfo); + var typeDictionary = CreatePreviewTypeDictionary(previewOwner); + + if (previewBackground.IsVisible()) + { + previewWidget.SetPreview(renderActor, typeDictionary, previewColor); + previewWidget.GetScale = () => selectedInfo.Scale; + } + + // Update title to show variant name + if (titleLabel != null) + titleLabel.Text = GetActorDisplayName(variant); + + // Update description from variant's traits + UpdateVariantDescription(variant); + } + + void UpdateVariantDescription(ActorInfo variant) + { + var currentY = 0; + + // Hide production container for variants + if (productionContainer != null) + productionContainer.Visible = false; + + currentY = 10; + + // Get description from variant's EncyclopediaExtras or TooltipExtras + var variantExtras = variant.TraitInfoOrDefault(); + var variantTooltipExtras = variant.TraitInfos().FirstOrDefault(t => t.IsStandard); + + var descriptionText = ""; + if (variantExtras != null && !string.IsNullOrEmpty(variantExtras.Description)) + { + descriptionText = WidgetUtilsCA.WrapTextWithIndent( + FluentProvider.GetMessage(variantExtras.Description.Replace("\\n", "\n")), + descriptionLabel.Bounds.Width, + descriptionFont); + } + else if (variantTooltipExtras != null && !string.IsNullOrEmpty(variantTooltipExtras.Description)) + { + descriptionText = WidgetUtilsCA.WrapTextWithIndent( + FluentProvider.GetMessage(variantTooltipExtras.Description.Replace("\\n", "\n")), + descriptionLabel.Bounds.Width, + descriptionFont); + } + + var descriptionHeight = string.IsNullOrEmpty(descriptionText) ? 0 : descriptionFont.Measure(descriptionText).Y; + descriptionLabel.GetText = () => descriptionText; + descriptionLabel.Bounds.Height = descriptionHeight; + descriptionLabel.Visible = !string.IsNullOrEmpty(descriptionText); + + if (!string.IsNullOrEmpty(descriptionText)) + { + descriptionLabel.Bounds.Y = currentY; + currentY += descriptionHeight + 8; + } + + // Hide prerequisites for variants + prerequisitesLabel.Visible = false; + + // Hide subfaction info + if (subfactionLabel != null) + subfactionLabel.Visible = false; + if (subfactionFlagImage != null) + subfactionFlagImage.Visible = false; + if (additionalInfoLabel != null) + additionalInfoLabel.Visible = false; + + // Get strengths/weaknesses/attributes from variant + var strengthsText = ""; + var weaknessesText = ""; + var attributesText = ""; + + if (variantTooltipExtras != null) + { + strengthsText = WidgetUtilsCA.WrapTextWithIndent(variantTooltipExtras.Strengths.Replace("\\n", "\n"), strengthsLabel.Bounds.Width, descriptionFont, 6); + weaknessesText = WidgetUtilsCA.WrapTextWithIndent(variantTooltipExtras.Weaknesses.Replace("\\n", "\n"), weaknessesLabel.Bounds.Width, descriptionFont, 6); + attributesText = WidgetUtilsCA.WrapTextWithIndent(variantTooltipExtras.Attributes.Replace("\\n", "\n"), attributesLabel.Bounds.Width, descriptionFont, 6); + } + + if (!string.IsNullOrEmpty(strengthsText) && strengthsLabel != null) + { + SetupTextLabel(strengthsLabel, strengthsText, ref currentY, 0); + } + else if (strengthsLabel != null) + { + strengthsLabel.Visible = false; + } + + if (!string.IsNullOrEmpty(weaknessesText) && weaknessesLabel != null) + { + SetupTextLabel(weaknessesLabel, weaknessesText, ref currentY, 0); + } + else if (weaknessesLabel != null) + { + weaknessesLabel.Visible = false; + } + + if (!string.IsNullOrEmpty(attributesText) && attributesLabel != null) + { + SetupTextLabel(attributesLabel, attributesText, ref currentY, 8); + } + else if (attributesLabel != null) + { + attributesLabel.Visible = false; + } + + // Hide encyclopedia description for variants + if (encyclopediaDescriptionLabel != null) + encyclopediaDescriptionLabel.Visible = false; + + actorDetailsContainer.Bounds.Height = currentY; + descriptionPanel.Layout.AdjustChildren(); + descriptionPanel.ScrollToTop(); + } + + int GetVariantGroupSortOrder(string groupName) + { + return variantGroupOrder.TryGetValue(groupName, out var order) ? order : int.MaxValue; + } + void RotatePreview() { if (selectedActor == null) diff --git a/mods/ca/chrome/encyclopedia.yaml b/mods/ca/chrome/encyclopedia.yaml index c8dcf19a4e..536546d7f1 100644 --- a/mods/ca/chrome/encyclopedia.yaml +++ b/mods/ca/chrome/encyclopedia.yaml @@ -79,9 +79,17 @@ Background@ENCYCLOPEDIA_PANEL: Width: PARENT_WIDTH - 250 - 10 Height: PARENT_HEIGHT - 65 Children: + DropDownButton@VARIANT_DROPDOWN: + X: 0 + Y: 0 + Width: PARENT_WIDTH - 148 - 10 + Height: 25 + Font: Regular + Text: Select Variant... ScrollPanel@ACTOR_DESCRIPTION_PANEL: + Y: 30 Width: PARENT_WIDTH - 148 - 10 - Height: PARENT_HEIGHT + Height: PARENT_HEIGHT - 30 TopBottomSpacing: 8 Background: observer-scrollpanel-button-pressed Children: diff --git a/mods/ca/rules/encyclopedia.yaml b/mods/ca/rules/encyclopedia.yaml index d019edd034..75334d09c4 100644 --- a/mods/ca/rules/encyclopedia.yaml +++ b/mods/ca/rules/encyclopedia.yaml @@ -883,7 +883,7 @@ encyclopedia.power.resourcescan: TooltipExtras: Attributes: Cooldown: 2:00\nRequires: Extractor -# IFV Variant Template +# IFV Variants ^IFVVariantPreview: Inherits: ^EffectPreview RenderSprites: @@ -901,9 +901,9 @@ encyclopedia.ifv.missile: Sequence: turret-rocket Tooltip: Name: Missile IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Allies Description: IFV loaded with a Rocket Soldier. Fires anti-air/anti-armor missiles. TooltipExtras: Strengths: Strong vs Aircraft, Heavy Armor @@ -916,9 +916,9 @@ encyclopedia.ifv.ggi: Sequence: turret-ggi Tooltip: Name: GGI IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Allies Description: IFV loaded with a Guardian GI. Fires powerful anti-armor rounds. TooltipExtras: Strengths: Strong vs Vehicles, Heavy Armor @@ -931,9 +931,9 @@ encyclopedia.ifv.mg: Sequence: turret-mg Tooltip: Name: Machine Gun IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Allies Description: IFV loaded with a Rifle Infantry. Fires a rapid machine gun. TooltipExtras: Strengths: Strong vs Infantry, Light Vehicles @@ -946,9 +946,9 @@ encyclopedia.ifv.grenade: Sequence: turret-frag Tooltip: Name: Grenade IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Soviet Description: IFV loaded with a Grenadier. Launches fragmentation grenades. TooltipExtras: Strengths: Strong vs Infantry, Buildings @@ -961,9 +961,9 @@ encyclopedia.ifv.flame: Sequence: turret-flame Tooltip: Name: Flamethrower IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Soviet Description: IFV loaded with a Flamethrower. Projects devastating flames. TooltipExtras: Strengths: Strong vs Infantry, Buildings @@ -976,9 +976,9 @@ encyclopedia.ifv.chem: Sequence: turret-chem Tooltip: Name: Chemical IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Nod Description: IFV loaded with a Toxin Soldier. Sprays toxic chemicals. TooltipExtras: Strengths: Strong vs Infantry @@ -991,9 +991,9 @@ encyclopedia.ifv.laser: Sequence: turret-laser Tooltip: Name: Laser IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Nod Description: IFV loaded with an Acolyte. Fires a powerful laser beam. TooltipExtras: Strengths: Strong vs Infantry, Light Vehicles @@ -1006,9 +1006,9 @@ encyclopedia.ifv.rad: Sequence: turret-rad Tooltip: Name: Desolator IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Soviet Description: IFV loaded with a Desolator. Sprays deadly radiation. TooltipExtras: Strengths: Strong vs Infantry, Vehicles @@ -1021,14 +1021,29 @@ encyclopedia.ifv.cmdo: Sequence: turret-cmdo Tooltip: Name: Commando IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: - Description: IFV loaded with a Commando or SEAL. Fires a powerful sniper rifle. + VariantOf: IFV + VariantGroup: Allies + Description: IFV loaded with a Commando (Tanya/Boris). Fires powerful sniper bursts with high damage per shot. TooltipExtras: Strengths: Strong vs Infantry Weaknesses: Weak vs Vehicles - Attributes: Passenger: Commando/SEAL + Attributes: Passenger: Tanya, Boris + +encyclopedia.ifv.seal: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-cmdo + Tooltip: + Name: SEAL IFV + EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Allies + Description: IFV loaded with a Navy SEAL. Fires a rapid-fire MP5 submachine gun. + TooltipExtras: + Strengths: Strong vs Infantry + Weaknesses: Weak vs Vehicles + Attributes: Passenger: Navy SEAL encyclopedia.ifv.mech: Inherits: ^IFVVariantPreview @@ -1036,9 +1051,9 @@ encyclopedia.ifv.mech: Sequence: turret-mech Tooltip: Name: Repair IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Utility Description: IFV loaded with a Mechanic. Repairs nearby friendly vehicles. TooltipExtras: Strengths: Repairs vehicles @@ -1050,9 +1065,9 @@ encyclopedia.ifv.snip: Sequence: turret-snip Tooltip: Name: Sniper IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Allies Description: IFV loaded with a Sniper. Fires long-range sniper rounds. TooltipExtras: Strengths: Strong vs Infantry @@ -1065,9 +1080,9 @@ encyclopedia.ifv.enli: Sequence: turret-enli Tooltip: Name: Enlightened IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Nod Description: IFV loaded with an Enlightened. Fires a powerful laser. TooltipExtras: Strengths: Strong vs Infantry, Vehicles @@ -1079,9 +1094,9 @@ encyclopedia.ifv.rmbc: Sequence: turret-rmbc Tooltip: Name: Cyborg Elite IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Nod Description: IFV loaded with a Cyborg Elite. Fires plasma bolts. TooltipExtras: Strengths: Strong vs Infantry, Vehicles @@ -1093,9 +1108,9 @@ encyclopedia.ifv.bh: Sequence: turret-bh Tooltip: Name: Black Hand IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Nod Description: IFV loaded with a Black Hand. Projects devastating flames. TooltipExtras: Strengths: Strong vs Infantry, Buildings @@ -1108,9 +1123,9 @@ encyclopedia.ifv.pdisc: Sequence: turret-pdisc Tooltip: Name: Plasma Disc IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Scrin Description: IFV loaded with a Disc Thrower. Fires plasma discs. TooltipExtras: Strengths: Strong vs Infantry, Light Vehicles @@ -1122,38 +1137,23 @@ encyclopedia.ifv.shard: Sequence: turret-shard Tooltip: Name: Shard IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Scrin Description: IFV loaded with a Shard Trooper. Fires Tiberium shards. TooltipExtras: Strengths: Strong vs Infantry, Light Vehicles Attributes: Passenger: Shard Trooper -encyclopedia.ifv.mort: - Inherits: ^IFVVariantPreview - WithSpriteTurret: - Sequence: turret-mort - Tooltip: - Name: Mortar IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants - EncyclopediaExtras: - Description: IFV loaded with a Mortar Infantry. Fires mortar rounds. - TooltipExtras: - Strengths: Strong vs Infantry, Buildings - Weaknesses: Slow rate of fire - Attributes: Passenger: Mortar Infantry - encyclopedia.ifv.cryo: Inherits: ^IFVVariantPreview WithSpriteTurret: Sequence: turret-cryo Tooltip: Name: Cryo IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Allies Description: IFV loaded with a Cryo Legionnaire. Fires freezing blasts. TooltipExtras: Strengths: Slows and freezes enemies @@ -1165,9 +1165,9 @@ encyclopedia.ifv.enfo: Sequence: turret-enfo Tooltip: Name: Enforcer IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Soviet Description: IFV loaded with an Enforcer. Fires shotgun blasts. TooltipExtras: Strengths: Strong vs Infantry at close range @@ -1179,9 +1179,9 @@ encyclopedia.ifv.engi: Sequence: turret-engi Tooltip: Name: Engineer IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Utility Description: IFV loaded with an Engineer. Can capture enemy structures. TooltipExtras: Strengths: Captures structures @@ -1193,9 +1193,9 @@ encyclopedia.ifv.ztrp: Sequence: turret-ztrp Tooltip: Name: Zone Trooper IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: GDI Description: IFV loaded with a Zone Trooper. Fires powerful railgun shots. TooltipExtras: Strengths: Strong vs Vehicles, Heavy Armor @@ -1207,9 +1207,9 @@ encyclopedia.ifv.zdef: Sequence: turret-zdef Tooltip: Name: Zone Defender IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: GDI Description: IFV loaded with a Zone Defender. Fires anti-air missiles. TooltipExtras: Strengths: Strong vs Aircraft @@ -1221,9 +1221,9 @@ encyclopedia.ifv.zrai: Sequence: turret-zrai Tooltip: Name: Zone Raider IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: GDI Description: IFV loaded with a Zone Raider. Fires sonic blasts. TooltipExtras: Strengths: Strong vs Infantry, Structures @@ -1235,9 +1235,9 @@ encyclopedia.ifv.tigr: Sequence: turret-tigr Tooltip: Name: Tiger Guard IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Soviet Description: IFV loaded with a Tiger Guard. Fires dual machine guns. TooltipExtras: Strengths: Strong vs Infantry @@ -1249,9 +1249,9 @@ encyclopedia.ifv.hopl: Sequence: turret-hopl Tooltip: Name: Hoplite IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: GDI Description: IFV loaded with a Hoplite. Fires EMP rounds. TooltipExtras: Strengths: Disables vehicles @@ -1263,9 +1263,9 @@ encyclopedia.ifv.impl: Sequence: turret-impl Tooltip: Name: Impaler IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Scrin Description: IFV loaded with an Impaler. Fires spikes. TooltipExtras: Strengths: Strong vs Infantry @@ -1280,9 +1280,9 @@ encyclopedia.ifv.stlk: Sequence: turret-stlk Tooltip: Name: Stalker IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Scrin Description: IFV loaded with a Stalker. Fires plasma bolts. TooltipExtras: Strengths: Strong vs Infantry, Light Vehicles @@ -1294,9 +1294,9 @@ encyclopedia.ifv.disin: Sequence: turret-disin Tooltip: Name: Disintegrator IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Scrin Description: IFV loaded with a Disintegrator. Fires disintegration beams. TooltipExtras: Strengths: Strong vs Vehicles, Buildings @@ -1308,9 +1308,9 @@ encyclopedia.ifv.conf: Sequence: turret-conf Tooltip: Name: Confessor IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Nod Description: IFV loaded with a Confessor. Fires hallucinogenic rounds. TooltipExtras: Strengths: Strong vs Infantry @@ -1322,15 +1322,166 @@ encyclopedia.ifv.reap: Sequence: turret-reap Tooltip: Name: Reaper IFV - Encyclopedia: - Category: Allies/Vehicles/IFV Variants EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Nod Description: IFV loaded with a Reaper. Fires Tiberium-infused rounds. TooltipExtras: Strengths: Strong vs Infantry Attributes: Passenger: Reaper (Nod) -# Reckoner Variant Template +encyclopedia.ifv.med: + Inherits: ^IFVVariantPreview + Tooltip: + Name: Medical IFV + EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Utility + Description: IFV loaded with a Medic. Heals nearby friendly infantry. + TooltipExtras: + Strengths: Heals infantry + Weaknesses: Unarmed + Attributes: Passenger: Medic + +encyclopedia.ifv.ivan: + Inherits: ^IFVVariantPreview + Tooltip: + Name: Explosive IFV + EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Soviet + Description: IFV loaded with a Crazy Ivan. Self-destructs on contact with the target, dealing massive damage. + TooltipExtras: + Strengths: Strong vs Everything + Weaknesses: Self-destructs on attack + Attributes: Passenger: Crazy Ivan + +encyclopedia.ifv.spy: + Inherits: ^IFVVariantPreview + Tooltip: + Name: Spy IFV + EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Allies + Description: IFV loaded with a Spy. Can infiltrate enemy structures and detects cloaked units. + TooltipExtras: + Strengths: Infiltrates structures, detects cloaked + Weaknesses: Cannot deal damage + Attributes: Passenger: Spy + +encyclopedia.ifv.tes: + Inherits: ^IFVVariantPreview + Tooltip: + Name: Tesla IFV + EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Soviet + Description: IFV loaded with a Shock Trooper or Tesla Trooper. Fires a powerful tesla bolt. + TooltipExtras: + Strengths: Strong vs Infantry, Vehicles + Weaknesses: Cannot attack Aircraft + Attributes: Passenger: Shock Trooper, Tesla Trooper + +encyclopedia.ifv.psy: + Inherits: ^IFVVariantPreview + Tooltip: + Name: Psychic IFV + EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Special + Description: IFV loaded with a Yuri Prime (Yuri) or Mastermind (Scrin). Can mind control enemy units. Control is lost if passenger exits. + TooltipExtras: + Strengths: Mind controls enemies + Weaknesses: Cannot deal damage, control lost on exit + Attributes: Passenger: Yuri Prime (Yuri), Mastermind (Scrin) + +encyclopedia.ifv.cmsr: + Inherits: ^IFVVariantPreview + Tooltip: + Name: Commissar IFV + EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Soviet + Description: IFV loaded with a Commissar. Inspires nearby friendly units, boosting their combat effectiveness. + TooltipExtras: + Strengths: Inspires nearby allies + Weaknesses: Unarmed + Attributes: Passenger: Commissar + +encyclopedia.ifv.shad: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-cmdo + Tooltip: + Name: Shadow IFV + EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Nod + Description: IFV loaded with a Shadow. Fires a sniper rifle effective against infantry. + TooltipExtras: + Strengths: Strong vs Infantry + Weaknesses: Weak vs Vehicles + Attributes: Passenger: Shadow + +encyclopedia.ifv.hack: + Inherits: ^IFVVariantPreview + Tooltip: + Name: Hacker IFV + EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Nod + Description: IFV loaded with a Hacker. Can mind control enemy defenses and buildings. Control is lost if passenger exits. + TooltipExtras: + Strengths: Strong vs Defenses, Buildings + Weaknesses: Cannot deal direct damage, control lost on exit + Attributes: Passenger: Hacker + +encyclopedia.ifv.mortchem: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-mort + Tooltip: + Name: Chemical Mortar IFV + EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Nod + Description: IFV loaded with a Chemical Mortar Infantry. Fires chemical mortar rounds that leave toxic residue. + TooltipExtras: + Strengths: Strong vs Infantry, area denial + Weaknesses: Minimum range + Attributes: Passenger: Chemical Mortar + +encyclopedia.ifv.mortcryo: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-mort + Tooltip: + Name: Cryo Mortar IFV + EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Allies + Description: IFV loaded with a Cryo Mortar Infantry. Fires cryo mortar rounds that slow and freeze enemies. + TooltipExtras: + Strengths: Slows and freezes enemies + Weaknesses: Minimum range + Attributes: Passenger: Cryo Mortar + +encyclopedia.ifv.mortsonic: + Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-mort + Tooltip: + Name: Sonic Mortar IFV + EncyclopediaExtras: + VariantOf: IFV + VariantGroup: GDI + Description: IFV loaded with a Sonic Mortar Infantry. Fires sonic mortar rounds that deal splash damage. + TooltipExtras: + Strengths: Strong vs Infantry, Buildings + Weaknesses: Minimum range + Attributes: Passenger: Sonic Mortar + +# Reckoner Variants ^ReckonerVariantPreview: Inherits: ^EffectPreview RenderSprites: @@ -1348,9 +1499,9 @@ encyclopedia.reck.missile: Sequence: turret-rocket Tooltip: Name: Missile Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Allies Description: Reckoner loaded with a Rocket Soldier. Fires anti-air/anti-armor missiles. TooltipExtras: Strengths: Strong vs Aircraft, Heavy Armor @@ -1363,9 +1514,9 @@ encyclopedia.reck.mg: Sequence: turret-mg Tooltip: Name: Machine Gun Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Allies Description: Reckoner loaded with a Rifle Infantry. Fires a rapid machine gun. TooltipExtras: Strengths: Strong vs Infantry, Light Vehicles @@ -1378,9 +1529,9 @@ encyclopedia.reck.grenade: Sequence: turret-frag Tooltip: Name: Grenade Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Soviet Description: Reckoner loaded with a Grenadier. Launches fragmentation grenades. TooltipExtras: Strengths: Strong vs Infantry, Buildings @@ -1393,9 +1544,9 @@ encyclopedia.reck.flame: Sequence: turret-flame Tooltip: Name: Flamethrower Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Soviet Description: Reckoner loaded with a Flamethrower. Projects devastating flames. TooltipExtras: Strengths: Strong vs Infantry, Buildings @@ -1408,9 +1559,9 @@ encyclopedia.reck.chem: Sequence: turret-chem Tooltip: Name: Chemical Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Nod Description: Reckoner loaded with a Toxin Soldier. Sprays toxic chemicals. TooltipExtras: Strengths: Strong vs Infantry @@ -1423,9 +1574,9 @@ encyclopedia.reck.laser: Sequence: turret-laser Tooltip: Name: Laser Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Nod Description: Reckoner loaded with an Acolyte. Fires a powerful laser beam. TooltipExtras: Strengths: Strong vs Infantry, Light Vehicles @@ -1438,9 +1589,9 @@ encyclopedia.reck.rad: Sequence: turret-rad Tooltip: Name: Desolator Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Soviet Description: Reckoner loaded with a Desolator. Sprays deadly radiation. TooltipExtras: Strengths: Strong vs Infantry, Vehicles @@ -1453,14 +1604,29 @@ encyclopedia.reck.cmdo: Sequence: turret-cmdo Tooltip: Name: Commando Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: - Description: Reckoner loaded with a Commando. Fires a powerful sniper rifle. + VariantOf: RECK + VariantGroup: Allies + Description: Reckoner loaded with a Commando (Tanya/Boris). Fires powerful sniper bursts with high damage per shot. + TooltipExtras: + Strengths: Strong vs Infantry + Weaknesses: Weak vs Vehicles + Attributes: Passenger: Tanya, Boris + +encyclopedia.reck.seal: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-cmdo + Tooltip: + Name: SEAL Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Allies + Description: Reckoner loaded with a Navy SEAL. Fires a rapid-fire MP5 submachine gun. TooltipExtras: Strengths: Strong vs Infantry Weaknesses: Weak vs Vehicles - Attributes: Passenger: Commando + Attributes: Passenger: Navy SEAL encyclopedia.reck.mech: Inherits: ^ReckonerVariantPreview @@ -1468,9 +1634,9 @@ encyclopedia.reck.mech: Sequence: turret-mech Tooltip: Name: Repair Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Utility Description: Reckoner loaded with a Mechanic. Repairs nearby friendly vehicles. TooltipExtras: Strengths: Repairs vehicles @@ -1482,9 +1648,9 @@ encyclopedia.reck.snip: Sequence: turret-snip Tooltip: Name: Sniper Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Allies Description: Reckoner loaded with a Sniper. Fires long-range sniper rounds. TooltipExtras: Strengths: Strong vs Infantry @@ -1497,9 +1663,9 @@ encyclopedia.reck.enli: Sequence: turret-enli Tooltip: Name: Enlightened Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Nod Description: Reckoner loaded with an Enlightened. Fires a powerful laser. TooltipExtras: Strengths: Strong vs Infantry, Vehicles @@ -1511,9 +1677,9 @@ encyclopedia.reck.rmbc: Sequence: turret-rmbc Tooltip: Name: Cyborg Elite Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Nod Description: Reckoner loaded with a Cyborg Elite. Fires plasma bolts. TooltipExtras: Strengths: Strong vs Infantry, Vehicles @@ -1525,9 +1691,9 @@ encyclopedia.reck.bh: Sequence: turret-bh Tooltip: Name: Black Hand Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Nod Description: Reckoner loaded with a Black Hand. Projects devastating flames. TooltipExtras: Strengths: Strong vs Infantry, Buildings @@ -1540,9 +1706,9 @@ encyclopedia.reck.pdisc: Sequence: turret-pdisc Tooltip: Name: Plasma Disc Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Scrin Description: Reckoner loaded with a Disc Thrower. Fires plasma discs. TooltipExtras: Strengths: Strong vs Infantry, Light Vehicles @@ -1554,52 +1720,23 @@ encyclopedia.reck.shard: Sequence: turret-shard Tooltip: Name: Shard Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Scrin Description: Reckoner loaded with a Shard Trooper. Fires Tiberium shards. TooltipExtras: Strengths: Strong vs Infantry, Light Vehicles Attributes: Passenger: Shard Trooper -encyclopedia.reck.mort: - Inherits: ^ReckonerVariantPreview - WithSpriteTurret: - Sequence: turret-mort - Tooltip: - Name: Mortar Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants - EncyclopediaExtras: - Description: Reckoner loaded with a Mortar Infantry. Fires mortar rounds. - TooltipExtras: - Strengths: Strong vs Infantry, Buildings - Weaknesses: Slow rate of fire - Attributes: Passenger: Mortar Infantry - -encyclopedia.reck.cryo: - Inherits: ^ReckonerVariantPreview - WithSpriteTurret: - Sequence: turret-cryo - Tooltip: - Name: Cryo Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants - EncyclopediaExtras: - Description: Reckoner loaded with a Cryo Legionnaire. Fires freezing blasts. - TooltipExtras: - Strengths: Slows and freezes enemies - Attributes: Passenger: Cryo Legionnaire - encyclopedia.reck.enfo: Inherits: ^ReckonerVariantPreview WithSpriteTurret: Sequence: turret-enfo Tooltip: Name: Enforcer Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Soviet Description: Reckoner loaded with an Enforcer. Fires shotgun blasts. TooltipExtras: Strengths: Strong vs Infantry at close range @@ -1611,9 +1748,9 @@ encyclopedia.reck.engi: Sequence: turret-engi Tooltip: Name: Engineer Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Utility Description: Reckoner loaded with an Engineer. Can capture enemy structures. TooltipExtras: Strengths: Captures structures @@ -1625,9 +1762,9 @@ encyclopedia.reck.conf: Sequence: turret-conf Tooltip: Name: Confessor Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Nod Description: Reckoner loaded with a Confessor. Fires hallucinogenic rounds. TooltipExtras: Strengths: Strong vs Infantry @@ -1639,9 +1776,9 @@ encyclopedia.reck.reap: Sequence: turret-reap Tooltip: Name: Reaper Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Nod Description: Reckoner loaded with a Reaper. Fires Tiberium-infused rounds. TooltipExtras: Strengths: Strong vs Infantry @@ -1656,9 +1793,9 @@ encyclopedia.reck.stlk: Sequence: turret-stlk Tooltip: Name: Stalker Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Scrin Description: Reckoner loaded with a Stalker. Fires plasma bolts. TooltipExtras: Strengths: Strong vs Infantry, Light Vehicles @@ -1670,14 +1807,370 @@ encyclopedia.reck.disin: Sequence: turret-disin Tooltip: Name: Disintegrator Reckoner - Encyclopedia: - Category: Nod/Vehicles/Reckoner Variants EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Scrin Description: Reckoner loaded with a Disintegrator. Fires disintegration beams. TooltipExtras: Strengths: Strong vs Vehicles, Buildings Attributes: Passenger: Disintegrator (Scrin) +encyclopedia.reck.med: + Inherits: ^ReckonerVariantPreview + Tooltip: + Name: Medical Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Utility + Description: Reckoner loaded with a Medic. Heals nearby friendly infantry. + TooltipExtras: + Strengths: Heals infantry + Weaknesses: Unarmed + Attributes: Passenger: Medic + +encyclopedia.reck.ivan: + Inherits: ^ReckonerVariantPreview + Tooltip: + Name: Explosive Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Soviet + Description: Reckoner loaded with a Crazy Ivan. Self-destructs on contact with the target, dealing massive damage. + TooltipExtras: + Strengths: Strong vs Everything + Weaknesses: Self-destructs on attack + Attributes: Passenger: Crazy Ivan + +encyclopedia.reck.spy: + Inherits: ^ReckonerVariantPreview + Tooltip: + Name: Spy Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Allies + Description: Reckoner loaded with a Spy. Can infiltrate enemy structures and detects cloaked units. + TooltipExtras: + Strengths: Infiltrates structures, detects cloaked + Weaknesses: Cannot deal damage + Attributes: Passenger: Spy + +encyclopedia.reck.tes: + Inherits: ^ReckonerVariantPreview + Tooltip: + Name: Tesla Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Soviet + Description: Reckoner loaded with a Shock Trooper or Tesla Trooper. Fires a powerful tesla bolt. + TooltipExtras: + Strengths: Strong vs Infantry, Vehicles + Weaknesses: Cannot attack Aircraft + Attributes: Passenger: Shock Trooper, Tesla Trooper + +encyclopedia.reck.psy: + Inherits: ^ReckonerVariantPreview + Tooltip: + Name: Psychic Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Special + Description: Reckoner loaded with a Yuri Prime (Yuri) or Mastermind (Scrin). Can mind control enemy units. Control is lost if passenger exits. + TooltipExtras: + Strengths: Mind controls enemies + Weaknesses: Cannot deal damage, control lost on exit + Attributes: Passenger: Yuri Prime (Yuri), Mastermind (Scrin) + +encyclopedia.reck.cmsr: + Inherits: ^ReckonerVariantPreview + Tooltip: + Name: Commissar Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Soviet + Description: Reckoner loaded with a Commissar. Inspires nearby friendly units, boosting their combat effectiveness. + TooltipExtras: + Strengths: Inspires nearby allies + Weaknesses: Unarmed + Attributes: Passenger: Commissar + +encyclopedia.reck.shad: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-cmdo + Tooltip: + Name: Shadow Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Nod + Description: Reckoner loaded with a Shadow. Fires a sniper rifle effective against infantry. + TooltipExtras: + Strengths: Strong vs Infantry + Weaknesses: Weak vs Vehicles + Attributes: Passenger: Shadow + +encyclopedia.reck.hack: + Inherits: ^ReckonerVariantPreview + Tooltip: + Name: Hacker Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Nod + Description: Reckoner loaded with a Hacker. Can mind control enemy defenses and buildings. Control is lost if passenger exits. + TooltipExtras: + Strengths: Strong vs Defenses, Buildings + Weaknesses: Cannot deal direct damage, control lost on exit + Attributes: Passenger: Hacker + +encyclopedia.reck.mortchem: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-mort + Tooltip: + Name: Chemical Mortar Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Nod + Description: Reckoner loaded with a Chemical Mortar Infantry. Fires chemical mortar rounds that leave toxic residue. + TooltipExtras: + Strengths: Strong vs Infantry, area denial + Weaknesses: Minimum range + Attributes: Passenger: Chemical Mortar + +encyclopedia.reck.mortcryo: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-mort + Tooltip: + Name: Cryo Mortar Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Allies + Description: Reckoner loaded with a Cryo Mortar Infantry. Fires cryo mortar rounds that slow and freeze enemies. + TooltipExtras: + Strengths: Slows and freezes enemies + Weaknesses: Minimum range + Attributes: Passenger: Cryo Mortar + +encyclopedia.reck.mortsonic: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-mort + Tooltip: + Name: Sonic Mortar Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: GDI + Description: Reckoner loaded with a Sonic Mortar Infantry. Fires sonic mortar rounds that deal splash damage. + TooltipExtras: + Strengths: Strong vs Infantry, Buildings + Weaknesses: Minimum range + Attributes: Passenger: Sonic Mortar + +encyclopedia.reck.ggi: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-ggi + Tooltip: + Name: GGI Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Allies + Description: Reckoner loaded with a Guardian GI. Fires powerful anti-armor rounds. + TooltipExtras: + Strengths: Strong vs Vehicles, Heavy Armor + Weaknesses: Weak vs Infantry + Attributes: Passenger: Guardian GI + +encyclopedia.reck.ztrp: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-ztrp + Tooltip: + Name: Zone Trooper Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: GDI + Description: Reckoner loaded with a Zone Trooper. Fires powerful railgun shots. + TooltipExtras: + Strengths: Strong vs Vehicles, Heavy Armor + Attributes: Passenger: Zone Trooper + +encyclopedia.reck.zdef: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-zdef + Tooltip: + Name: Zone Defender Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: GDI + Description: Reckoner loaded with a Zone Defender. Fires anti-air missiles. + TooltipExtras: + Strengths: Strong vs Aircraft + Attributes: Passenger: Zone Defender + +encyclopedia.reck.zrai: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-zrai + Tooltip: + Name: Zone Raider Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: GDI + Description: Reckoner loaded with a Zone Raider. Fires sonic blasts. + TooltipExtras: + Strengths: Strong vs Infantry, Structures + Attributes: Passenger: Zone Raider + +encyclopedia.reck.tigr: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-tigr + Tooltip: + Name: Tiger Guard Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Soviet + Description: Reckoner loaded with a Tiger Guard. Fires dual machine guns. + TooltipExtras: + Strengths: Strong vs Infantry + Attributes: Passenger: Tiger Guard + +encyclopedia.reck.hopl: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-hopl + Tooltip: + Name: Hoplite Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: GDI + Description: Reckoner loaded with a Hoplite. Fires EMP rounds. + TooltipExtras: + Strengths: Disables vehicles + Attributes: Passenger: Hoplite + +encyclopedia.reck.impl: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-impl + Tooltip: + Name: Impaler Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Scrin + Description: Reckoner loaded with an Impaler. Fires spikes. + TooltipExtras: + Strengths: Strong vs Infantry + Attributes: Passenger: Impaler + +# Battle Fortress Variants +^BattleFortressVariantPreview: + Inherits: ^EffectPreview + RenderSprites: + Image: batf + WithFacingSpriteBody: + Encyclopedia: + Scale: 1.5 + +encyclopedia.batf.spy: + Inherits: ^BattleFortressVariantPreview + Tooltip: + Name: Spy Battle Fortress + EncyclopediaExtras: + VariantOf: BATF + VariantGroup: Utility + Description: Battle Fortress loaded with Spies, Thieves, or Saboteurs. Gains the ability to detect cloaked and disguised units within a 6 cell radius. + TooltipExtras: + Strengths: Detects cloaked/disguised units + Attributes: Passengers: Spy, Thief, Saboteur + +encyclopedia.batf.medic: + Inherits: ^BattleFortressVariantPreview + Tooltip: + Name: Medic Battle Fortress + EncyclopediaExtras: + VariantOf: BATF + VariantGroup: Utility + Description: Battle Fortress loaded with Medics. Provides a healing aura that restores health to nearby allied infantry. + TooltipExtras: + Strengths: Heals nearby infantry + Attributes: Passengers: Medic, Soviet Medic + +encyclopedia.batf.repair: + Inherits: ^BattleFortressVariantPreview + Tooltip: + Name: Repair Battle Fortress + EncyclopediaExtras: + VariantOf: BATF + VariantGroup: Utility + Description: Battle Fortress loaded with Engineers or Mechanics. Slowly repairs itself when damaged. + TooltipExtras: + Strengths: Self-repairs when damaged + Attributes: Passengers: Engineer, Mechanic, Artificer + +encyclopedia.batf.aa: + Inherits: ^BattleFortressVariantPreview + Tooltip: + Name: Anti-Air Battle Fortress + EncyclopediaExtras: + VariantOf: BATF + VariantGroup: Combat + Description: Battle Fortress loaded with GGI or Rocket Soldiers. Gains the ability to attack aircraft using their anti-air missiles. + TooltipExtras: + Strengths: Can attack Aircraft + Attributes: Passengers: GGI, Rocket Soldier, Flak Trooper + +encyclopedia.batf.flame: + Inherits: ^BattleFortressVariantPreview + Tooltip: + Name: Flame Battle Fortress + EncyclopediaExtras: + VariantOf: BATF + VariantGroup: Combat + Description: Battle Fortress loaded with Black Hand troopers. Infantry fire their flamethrowers from the ports, devastating infantry and light vehicles. + TooltipExtras: + Strengths: Strong vs Infantry, Light Vehicles, Buildings + Attributes: Passengers: Black Hand + +encyclopedia.batf.cryo: + Inherits: ^BattleFortressVariantPreview + Tooltip: + Name: Cryo Battle Fortress + EncyclopediaExtras: + VariantOf: BATF + VariantGroup: Combat + Description: Battle Fortress loaded with Cryo Troopers. Infantry fire their freeze rays from the ports, slowing and freezing enemies. + TooltipExtras: + Strengths: Slows and freezes enemies + Attributes: Passengers: Cryo Trooper + +encyclopedia.batf.cmdo: + Inherits: ^BattleFortressVariantPreview + Tooltip: + Name: Commando Battle Fortress + EncyclopediaExtras: + VariantOf: BATF + VariantGroup: Special + Description: Battle Fortress loaded with Commandos (Tanya, Boris, Commando, Yuri Prime, or Mastermind). Becomes immune to mind control. + TooltipExtras: + Strengths: Immune to mind control + Attributes: Passengers: Tanya, Boris, Commando, Yuri Prime, Mastermind + +encyclopedia.batf.seal: + Inherits: ^BattleFortressVariantPreview + Tooltip: + Name: SEAL Battle Fortress + EncyclopediaExtras: + VariantOf: BATF + VariantGroup: Combat + Description: Battle Fortress loaded with Navy SEALs. SEALs fire their weapons from the ports, but unlike Commandos, do not provide mind control immunity. + TooltipExtras: + Strengths: Strong vs Infantry + Weaknesses: No mind control immunity + Attributes: Passengers: Navy SEAL + encyclopedia.buffs.anathema: Inherits: ^VehicleEffectPreview Tooltip: From 7f446c66f0a54c890bdb480637a63e70d40e03bc Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Thu, 1 Jan 2026 14:44:48 +0000 Subject: [PATCH 18/28] Encyclopedia corrections. --- .../Widgets/Logic/EncyclopediaLogicCA.cs | 8 +- mods/ca/mod.content.yaml | 1 - mods/ca/rules/encyclopedia.yaml | 2047 +++++++++-------- mods/ca/rules/scrin.yaml | 4 - mods/ca/sequences/powers.yaml | 275 --- 5 files changed, 1074 insertions(+), 1261 deletions(-) delete mode 100644 mods/ca/sequences/powers.yaml diff --git a/OpenRA.Mods.CA/Widgets/Logic/EncyclopediaLogicCA.cs b/OpenRA.Mods.CA/Widgets/Logic/EncyclopediaLogicCA.cs index db6fbd1057..fdf5e99c52 100644 --- a/OpenRA.Mods.CA/Widgets/Logic/EncyclopediaLogicCA.cs +++ b/OpenRA.Mods.CA/Widgets/Logic/EncyclopediaLogicCA.cs @@ -565,6 +565,7 @@ void SelectActor(ActorInfo actor, string categoryPath = null) // Update the encyclopedia color palette with the faction color var previewColor = GetPreviewColorFromCategory(categoryPath); + EncyclopediaColorPalette.SetPreviewColor(previewColor); var previewOwner = GetPreviewOwner(selectedInfo); var typeDictionary = CreatePreviewTypeDictionary(previewOwner); @@ -975,14 +976,15 @@ void SetupVariantDropdown(ActorInfo actor) // Check if this actor has variants if (!variantsByParent.TryGetValue(actor.Name, out var variants) || variants.Count == 0) { - variantDropdown.IsVisible = () => false; + variantDropdown.IsDisabled = () => true; + variantDropdown.GetText = () => ""; return; } - variantDropdown.IsVisible = () => true; + variantDropdown.IsDisabled = () => false; variantDropdown.GetText = () => selectedVariant != null ? GetActorDisplayName(selectedVariant) - : "Select Variant..."; + : "Select variant..."; variantDropdown.OnMouseDown = _ => { diff --git a/mods/ca/mod.content.yaml b/mods/ca/mod.content.yaml index 432cb84528..398a463a53 100644 --- a/mods/ca/mod.content.yaml +++ b/mods/ca/mod.content.yaml @@ -42,7 +42,6 @@ Sequences: ca|sequences/decorations.yaml ca|sequences/scrin.yaml ca|sequences/upgrades.yaml - ca|sequences/powers.yaml TileSets: ca|tilesets/snow.yaml diff --git a/mods/ca/rules/encyclopedia.yaml b/mods/ca/rules/encyclopedia.yaml index 75334d09c4..4b19d7df8e 100644 --- a/mods/ca/rules/encyclopedia.yaml +++ b/mods/ca/rules/encyclopedia.yaml @@ -42,846 +42,945 @@ encyclopedia.tips.general: ^SupportPowerPreview: Inherits: ^InvisibleDummy Buildable: - Icon: icon + BuildDurationModifier: 100 + RenderSprites: + Image: icon Encyclopedia: EncyclopediaExtras: HideNotProducible: true +encyclopedia.power.lightningstorm: + Inherits: ^SupportPowerPreview + Buildable: + Icon: storm + Prerequisites: weat + BuildDuration: 13500 + Tooltip: + Name: Lightning Storm + Encyclopedia: + Category: Allies/Support Powers + EncyclopediaExtras: + Description: Initiate a Lightning Storm which deals heavy damage over a large area. + encyclopedia.power.chronoshift: Inherits: ^SupportPowerPreview - RenderSprites: - Image: chrono.power + Buildable: + Icon: chrono + Prerequisites: pdox + BuildDuration: 4500 Tooltip: Name: Chronoshift Encyclopedia: Category: Allies/Support Powers EncyclopediaExtras: Description: Teleports up to 9 selected vehicles to a targeted location, returning them to their original location after a short time.\n\n• If killed, units are returned with 20% health\n• Units with larger health pools take additional damage\n• Passengers cannot be unloaded\n• Enemy units are teleported for reduced duration - TooltipExtras: - Attributes: Cooldown: 3:00\nRequires: Chronosphere encyclopedia.power.forceshield: Inherits: ^SupportPowerPreview - RenderSprites: - Image: forceshield.power + Buildable: + Icon: forceshield + Prerequisites: techcenter.any + BuildDuration: 7500 Tooltip: Name: Force Shield Encyclopedia: - Category: Allies/Support Powers + Category: Allies/Support Powers; Soviets/Support Powers; GDI/Support Powers; Nod/Support Powers; Scrin/Support Powers EncyclopediaExtras: Description: Makes selected friendly structures temporarily invulnerable.\n\nWarning: Causes power failure. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: Allied Tech Center encyclopedia.power.spysatellite: Inherits: ^SupportPowerPreview - RenderSprites: - Image: gps.power + Buildable: + Icon: gps + Prerequisites: atek + BuildDuration: 5250 Tooltip: Name: Spy Satellite Encyclopedia: Category: Allies/Support Powers EncyclopediaExtras: Description: Periodically reveals the entire map for a short time (activated automatically). - TooltipExtras: - Attributes: Cooldown: 3:30\nRequires: Allied Tech Center encyclopedia.power.timewarp: Inherits: ^SupportPowerPreview - RenderSprites: - Image: timewarp.power + Buildable: + Icon: timewarp + Prerequisites: pdox + BuildDuration: 4500 Tooltip: Name: Time Warp Encyclopedia: Category: Allies/Support Powers EncyclopediaExtras: Description: Disrupts time and space at the target location. Affected units & structures are frozen in time, unable to act, but immune to damage. - TooltipExtras: - Attributes: Cooldown: 3:00\nRequires: Chronosphere + Subfaction: germany encyclopedia.power.tempinc: Inherits: ^SupportPowerPreview - RenderSprites: - Image: tempinc.power + Buildable: + Icon: tempinc + Prerequisites: pdox + BuildDuration: 7500 Tooltip: Name: Temporal Incursion Encyclopedia: Category: Allies/Support Powers EncyclopediaExtras: Description: Summons reinforcements from the future. Units return to their origin time after a short time. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: Chronosphere + Subfaction: germany encyclopedia.power.veilofwar: Inherits: ^SupportPowerPreview - RenderSprites: - Image: veilofwar.power + Buildable: + Icon: veilofwar + IconPalette: chrometd + Prerequisites: anyradar + BuildDuration: 7500 Tooltip: Name: Veil of War Encyclopedia: Category: Allies/Support Powers EncyclopediaExtras: Description: Creates an expanding area of shroud, reducing the vision and weapon range of enemy units and defenses. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: Gap Generator + Subfaction: england encyclopedia.power.clustermines: Inherits: ^SupportPowerPreview - RenderSprites: - Image: cmines.power + Buildable: + Icon: cmines + Prerequisites: anyradar + BuildDuration: 6750 Tooltip: Name: Cluster Mines Encyclopedia: Category: Allies/Support Powers EncyclopediaExtras: Description: Sends a cargo plane to drop a minefield at the target location. - TooltipExtras: - Attributes: Cooldown: 4:30\nRequires: Airfield (France) + Subfaction: france encyclopedia.power.strafe: Inherits: ^SupportPowerPreview - RenderSprites: - Image: strafe.power + Buildable: + Icon: strafe + Prerequisites: hpad + BuildDuration: 7500 Tooltip: Name: Strafing Run Encyclopedia: Category: Allies/Support Powers EncyclopediaExtras: Description: Calls in P51 ground attack planes to perform strafing runs on the target. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: Airfield + Subfaction: usa encyclopedia.power.cryostorm: Inherits: ^SupportPowerPreview - RenderSprites: - Image: cryostorm.power + Buildable: + Icon: cryostorm + Prerequisites: alhq + BuildDuration: 8250 Tooltip: Name: Cryostorm Encyclopedia: Category: Allies/Support Powers EncyclopediaExtras: Description: Creates a turbulent area of extreme cold, reducing movement speed and increasing damage taken. - TooltipExtras: - Attributes: Cooldown: 5:30\nRequires: Weather Controller + AdditionalInfo: Requires Sweden Coalition. encyclopedia.power.heliosbomb: Inherits: ^SupportPowerPreview - RenderSprites: - Image: heliosbomb.power + Buildable: + Icon: heliosbomb + IconPalette: caneon + Prerequisites: alhq + BuildDuration: 10500 Tooltip: Name: Helios Bomb Encyclopedia: Category: Allies/Support Powers EncyclopediaExtras: Description: Calls in a bomber which drops a Helios bomb, blinding units in a large area. - TooltipExtras: - Attributes: Cooldown: 7:00\nRequires: Airfield + AdditionalInfo: Requires Greece Coalition. encyclopedia.power.bsky: Inherits: ^SupportPowerPreview - RenderSprites: - Image: bsky.power + Buildable: + Icon: bsky + Prerequisites: alhq + BuildDuration: 9000 Tooltip: Name: Black Sky Strike Encyclopedia: Category: Allies/Support Powers EncyclopediaExtras: Description: Fires long range guided missiles at multiple ground targets prioritized by value. Tracking can be lost if targets move far enough from their initial location. - TooltipExtras: - Attributes: Cooldown: 6:00\nRequires: Black Sky Command + AdditionalInfo: Requires Korea Coalition. # Soviets Support Powers encyclopedia.power.ironcurtain: Inherits: ^SupportPowerPreview - RenderSprites: - Image: invuln.power + Buildable: + Icon: invuln + Prerequisites: iron + BuildDuration: 4500 Tooltip: Name: Iron Curtain Encyclopedia: Category: Soviets/Support Powers EncyclopediaExtras: Description: Makes selected friendly vehicles temporarily invulnerable. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: Iron Curtain - -encyclopedia.power.lightningstorm: - Inherits: ^SupportPowerPreview - RenderSprites: - Image: storm.power - Tooltip: - Name: Lightning Storm - Encyclopedia: - Category: Soviets/Support Powers - EncyclopediaExtras: - Description: Initiate a Lightning Storm which deals heavy damage over a large area. - TooltipExtras: - Attributes: Cooldown: 9:00\nRequires: Weather Controller encyclopedia.power.atombomb: Inherits: ^SupportPowerPreview - RenderSprites: - Image: abomb.power + Buildable: + Icon: abomb + Prerequisites: mslo + BuildDuration: 13500 Tooltip: Name: Atom Bomb Encyclopedia: Category: Soviets/Support Powers EncyclopediaExtras: Description: Launches a devastating atomic bomb at the target location, dealing heavy damage over a large area. - TooltipExtras: - Attributes: Cooldown: 9:00\nRequires: Missile Silo encyclopedia.power.spyplane: Inherits: ^SupportPowerPreview - RenderSprites: - Image: spyplane.power + Buildable: + Icon: spyplane + Prerequisites: anyradar + BuildDuration: 3750 Tooltip: Name: Spy Plane Encyclopedia: Category: Soviets/Support Powers EncyclopediaExtras: Description: Dispatches a spy plane that reveals the target location for a limited time. - TooltipExtras: - Attributes: Cooldown: 2:00\nRequires: Radar encyclopedia.power.paratroopers: Inherits: ^SupportPowerPreview - RenderSprites: - Image: paratroopers.power + Buildable: + Icon: paratroopers + Prerequisites: anyradar + BuildDuration: 7500 Tooltip: Name: Paratroopers Encyclopedia: Category: Soviets/Support Powers EncyclopediaExtras: Description: Dispatches a Halo transport to drop a squad of infantry anywhere on the map. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: Airfield encyclopedia.power.stormtroopers: Inherits: ^SupportPowerPreview - RenderSprites: - Image: stormtroopers.power + Buildable: + Icon: stormtroopers + Prerequisites: anyradar + BuildDuration: 9000 Tooltip: Name: Storm-troopers Encyclopedia: Category: Soviets/Support Powers EncyclopediaExtras: Description: Dispatches a Halo transport to drop a squad of Shock Troopers anywhere on the map. - TooltipExtras: - Attributes: Cooldown: 6:00\nRequires: Tesla Reactor + Subfaction: russia encyclopedia.power.parabombs: Inherits: ^SupportPowerPreview - RenderSprites: - Image: parabombs.power + Buildable: + Icon: parabombs + Prerequisites: afld + BuildDuration: 7500 Tooltip: Name: Parabombs Encyclopedia: Category: Soviets/Support Powers EncyclopediaExtras: Description: Calls in a Badger bomber which drops parachuted bombs on your target. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: Airfield encyclopedia.power.carpetbomb: Inherits: ^SupportPowerPreview - RenderSprites: - Image: carpetbomb.power + Buildable: + Icon: carpetbomb + Prerequisites: afld + BuildDuration: 10500 Tooltip: Name: Carpet Bomb Encyclopedia: Category: Soviets/Support Powers EncyclopediaExtras: Description: Calls in a squad of Badgers which drop bombs on your target. - TooltipExtras: - Attributes: Cooldown: 7:00\nRequires: Soviet Tech Center + Subfaction: ukraine encyclopedia.power.atomicbombair: Inherits: ^SupportPowerPreview - RenderSprites: - Image: abombair.power + Buildable: + Icon: abombair + Prerequisites: afld + BuildDuration: 10500 Tooltip: Name: Atomic Bomb (Air) Encyclopedia: Category: Soviets/Support Powers EncyclopediaExtras: Description: Calls in a Badger bomber which drops an atom bomb on your target. - TooltipExtras: - Attributes: Cooldown: 7:00\nRequires: Soviet Tech Center + Subfaction: iraq encyclopedia.power.mutabomb: Inherits: ^SupportPowerPreview - RenderSprites: - Image: mutabomb.power + Buildable: + Icon: mutabomb + IconPalette: chromes + Prerequisites: anyradar + BuildDuration: 6750 Tooltip: Name: Muta Bomb Encyclopedia: Category: Soviets/Support Powers EncyclopediaExtras: Description: Calls in a Badger bomber which drops a genetic mutation bomb on your target, transforming infantry into Brutes under your control. - TooltipExtras: - Attributes: Cooldown: 4:30\nRequires: Bio-Research Facility + Subfaction: yuri encyclopedia.power.chaosbombs: Inherits: ^SupportPowerPreview - RenderSprites: - Image: chaosbombs.power + Buildable: + Icon: chaosbombs + Prerequisites: afld + BuildDuration: 8250 Tooltip: Name: Chaos Bombs Encyclopedia: Category: Soviets/Support Powers EncyclopediaExtras: Description: Calls in a Badger bomber which drops parachuted chaos bombs on your target. - TooltipExtras: - Attributes: Cooldown: 5:30\nRequires: Airfield + Subfaction: yuri encyclopedia.power.atomicammo: Inherits: ^SupportPowerPreview - RenderSprites: - Image: atomicammo.power + Buildable: + Icon: atomicammo + Prerequisites: npwr + BuildDuration: 4500 Tooltip: Name: Atomic Shells Encyclopedia: Category: Soviets/Support Powers EncyclopediaExtras: Description: Grants tanks a limited supply of atomic shells (also for a limited duration).\n\nAffects Soviet tanks only (Heavy/Rhino Tank, Lasher/Thrasher Tank, Siege Tank, Mammoth Tank, Overlord Tank, Apocalypse Tank, Nuke Cannon) - TooltipExtras: - Attributes: Cooldown: 6:00\nRequires: Missile Silo encyclopedia.power.heroes: Inherits: ^SupportPowerPreview - RenderSprites: - Image: heroes.power + Buildable: + Icon: heroes + BuildDuration: 6000 Tooltip: Name: Heroes of the Union Encyclopedia: Category: Soviets/Support Powers EncyclopediaExtras: Description: Targeted basic infantry units become Heroes of the Union, significantly increasing their damage, speed, range and resilience.\n\nCan affect Rifle Infantry, Rocket Soldiers, Grenadiers and Flamethrowers. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: Soviet Tech Center + AdditionalInfo: Requires Infantry Doctrine. encyclopedia.power.tankdrop: Inherits: ^SupportPowerPreview - RenderSprites: - Image: tankdrop.power + Buildable: + Icon: tankdrop + BuildDuration: 13500 Tooltip: Name: Tank Drop Encyclopedia: Category: Soviets/Support Powers EncyclopediaExtras: Description: Dispatches cargo planes to air drop tanks at the target location. - TooltipExtras: - Attributes: Cooldown: 9:00\nRequires: Soviet Tech Center + AdditionalInfo: Requires Armor Doctrine. encyclopedia.power.killzone: Inherits: ^SupportPowerPreview - RenderSprites: - Image: killzone.power + Buildable: + Icon: killzone + BuildDuration: 4500 Tooltip: Name: Kill Zone Encyclopedia: Category: Soviets/Support Powers EncyclopediaExtras: Description: Calls in a Spy Plane which marks a target area. Any enemy units within it take increased damage. - TooltipExtras: - Attributes: Cooldown: 3:00\nRequires: Radar + AdditionalInfo: Requires Artillery Doctrine. # GDI Support Powers encyclopedia.power.ioncannon: Inherits: ^SupportPowerPreview - RenderSprites: - Image: ioncannon.power + Buildable: + Icon: ioncannon + Prerequisites: eye + BuildDuration: 13500 Tooltip: Name: Ion Cannon Encyclopedia: Category: GDI/Support Powers EncyclopediaExtras: Description: Fires a devastating ion cannon beam at the target location. - TooltipExtras: - Attributes: Cooldown: 7:00\nRequires: Ion Cannon Uplink encyclopedia.power.recondrone: Inherits: ^SupportPowerPreview - RenderSprites: - Image: uavicon.power + Buildable: + Icon: uavicon + Prerequisites: anyradar + BuildDuration: 3750 Tooltip: Name: Recon Drone Encyclopedia: Category: GDI/Support Powers EncyclopediaExtras: Description: A drone flies across the map, revealing the area as it passes.\n\nDetects cloaked units. - TooltipExtras: - Attributes: Cooldown: 2:30\nRequires: Radar encyclopedia.power.xodrop: Inherits: ^SupportPowerPreview - RenderSprites: - Image: xodrop.power + Buildable: + Icon: xodrop + Prerequisites: anyradar + BuildDuration: 9750 Tooltip: Name: X-O Drop Encyclopedia: Category: GDI/Support Powers EncyclopediaExtras: Description: An Orca transport drops a squad of X-O Powersuits at the target location. - TooltipExtras: - Attributes: Cooldown: 6:30\nRequires: GDI Tech Center + Subfaction: talon encyclopedia.power.droppods: Inherits: ^SupportPowerPreview - RenderSprites: - Image: droppods.power + Buildable: + Icon: droppods + IconPalette: chrometd + Prerequisites: anyradar + BuildDuration: 4500 Tooltip: Name: Drop Pods Encyclopedia: Category: GDI/Support Powers EncyclopediaExtras: Description: Instantly deploys a small team of elite soldiers at the target location via orbital drop pods. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: GDI Tech Center + Subfaction: zocom encyclopedia.power.reinforcements: Inherits: ^SupportPowerPreview - RenderSprites: - Image: orcaca.power + Buildable: + Icon: orcaca + IconPalette: chrometd + Prerequisites: anyradar + BuildDuration: 6000 Tooltip: Name: Reinforcements Encyclopedia: Category: GDI/Support Powers EncyclopediaExtras: Description: An Orca Carryall drops an APC containing an infantry squad at the target location. - TooltipExtras: - Attributes: Cooldown: 4:00\nRequires: Airfield + Subfaction: eagle encyclopedia.power.interceptors: Inherits: ^SupportPowerPreview - RenderSprites: - Image: airsupport.power + Buildable: + Icon: airsupport + Prerequisites: afld.gdi + BuildDuration: 10500 Tooltip: Name: Interceptors Encyclopedia: Category: GDI/Support Powers EncyclopediaExtras: Description: A squadron of Interceptors provides air cover over the target area, engaging any enemy aircraft within range. - TooltipExtras: - Attributes: Cooldown: 7:00\nRequires: Airfield encyclopedia.power.naniterepair: Inherits: ^SupportPowerPreview - RenderSprites: - Image: nrepair.power + Buildable: + Icon: nrepair + Prerequisites: gtek + BuildDuration: 6750 Tooltip: Name: Nanite Repair Encyclopedia: Category: GDI/Support Powers EncyclopediaExtras: Description: Repairs selected damaged vehicles over time. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: GDI Tech Center + Subfaction: arc encyclopedia.power.firestorm: Inherits: ^SupportPowerPreview - RenderSprites: - Image: fstorm.power + Buildable: + Icon: fstorm + IconPalette: chrometd + Prerequisites: upgc + BuildDuration: 7500 Tooltip: Name: Firestorm Encyclopedia: Category: GDI/Support Powers EncyclopediaExtras: Description: Fires a barrage of rockets at the target area. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: GDI Tech Center + AdditionalInfo: Requires Bombardment Strategy. encyclopedia.power.advancedradar: Inherits: ^SupportPowerPreview - RenderSprites: - Image: arscan.power + Buildable: + Icon: arscan + IconPalette: chrometd + Prerequisites: upgc + BuildDuration: 4500 Tooltip: Name: Advanced Radar Encyclopedia: Category: GDI/Support Powers EncyclopediaExtras: - Description: Reveals all enemy units on the radar for a limited time. - TooltipExtras: - Attributes: Cooldown: 4:00\nRequires: GDI Tech Center + Description: Reveals the location of enemy structures & units through the fog of war. + AdditionalInfo: Requires Seek & Destroy Strategy. encyclopedia.power.naniteshield: Inherits: ^SupportPowerPreview - RenderSprites: - Image: nshield.power + Buildable: + Icon: nshield + Prerequisites: upgc + BuildDuration: 6000 Tooltip: Name: Nanite Shield Encyclopedia: Category: GDI/Support Powers EncyclopediaExtras: - Description: Creates a protective nanite shield around selected units. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: GDI Tech Center + Description: Reduces the damage taken by all vehicles in the target area. + AdditionalInfo: Requires Hold the Line Strategy. encyclopedia.power.empmissile: Inherits: ^SupportPowerPreview - RenderSprites: - Image: empmissile.power + Buildable: + Icon: empmissile + Prerequisites: patr + BuildDuration: 4500 Tooltip: Name: EMP Missile Encyclopedia: Category: GDI/Support Powers EncyclopediaExtras: Description: Launches an EMP missile that disables vehicles and structures in the target area. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: EMP Cannon encyclopedia.power.surgicalstrike: Inherits: ^SupportPowerPreview - RenderSprites: - Image: surgicalstrike.power + Buildable: + Icon: surgicalstrike + IconPalette: chrometd + Prerequisites: eye + BuildDuration: 6000 Tooltip: Name: Surgical Strike Encyclopedia: Category: GDI/Support Powers EncyclopediaExtras: - Description: Calls in precision airstrikes on the target. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: Airfield + Description: Initiate an precision Ion Cannon strike which deals instant damage to a small area. + Subfaction: zocom # Nod Support Powers encyclopedia.power.clustermissile: Inherits: ^SupportPowerPreview - RenderSprites: - Image: clustermissile.power + Buildable: + Icon: clustermissile + IconPalette: chrometd + Prerequisites: tmpl + BuildDuration: 10500 Tooltip: Name: Cluster Missile Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Launches cluster missiles at the target area. - TooltipExtras: - Attributes: Cooldown: 6:00\nRequires: Missile Silo + Description: Launches a Cluster Missile which deals heavy damage at the target location. encyclopedia.power.chemmissile: Inherits: ^SupportPowerPreview - RenderSprites: - Image: chemmissile.power + Buildable: + Icon: chemmissile + IconPalette: chrometd + Prerequisites: mslo.nod + BuildDuration: 13500 Tooltip: Name: Chemical Missile Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Launches a chemical missile that creates a toxic cloud at the target location. - TooltipExtras: - Attributes: Cooldown: 7:00\nRequires: Chemical Plant + Description: Launches an deadly Chemical Missile. Deals heavy damage and creates toxic clouds which are extremely harmful to infantry.\n encyclopedia.power.tibstealth: Inherits: ^SupportPowerPreview - RenderSprites: - Image: invis.power + Buildable: + Icon: invis + IconPalette: chrometd + Prerequisites: sgen + BuildDuration: 4500 Tooltip: - Name: Tib Stealth + Name: Tiberium Stealth Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Makes selected units invisible for a limited time. - TooltipExtras: - Attributes: Cooldown: 4:00\nRequires: Stealth Generator + Description: Makes selected vehicles and structures temporarily invisible.\n\nWarning: Harmful to non-cyborg infantry.\n\nHazmat Suits upgrade makes allies immune to damage and enemies take 50% damage. encyclopedia.power.airdrop: Inherits: ^SupportPowerPreview - RenderSprites: - Image: airdrop.power + Buildable: + Icon: airdropicon + Prerequisites: airs + BuildDuration: 13500 Tooltip: Name: Air Drop Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Air drops units at the target location. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: Airfield + Description: Dispatches cargo planes to air drop tanks at the target location. Units dependent on subfaction. encyclopedia.power.hacksat: Inherits: ^SupportPowerPreview - RenderSprites: - Image: hacksat.power + Buildable: + Icon: hacksat + Prerequisites: anyradar + BuildDuration: 4500 Tooltip: Name: Hack Satellite Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Hacks enemy satellites to reveal their base. - TooltipExtras: - Attributes: Cooldown: 4:00\nRequires: Nod Tech Center + Description: Reveals the targeted area for a short time. encyclopedia.power.infbomb: Inherits: ^SupportPowerPreview - RenderSprites: - Image: infbomb.power + Buildable: + Icon: infbomb + IconPalette: chrometd + Prerequisites: anyradar + BuildDuration: 9750 Tooltip: Name: Inferno Bomb Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Calls in a bomber to drop incendiary bombs on the target. - TooltipExtras: - Attributes: Cooldown: 6:00\nRequires: Airfield + Description: A B2 Stealth Bomber drops inferno bombs on your target. + Subfaction: blackh encyclopedia.power.cashhack: Inherits: ^SupportPowerPreview - RenderSprites: - Image: chack.power + Buildable: + Icon: chack + Prerequisites: anyradar + BuildDuration: 6000 Tooltip: Name: Cash Hack Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Steals credits from enemy players. - TooltipExtras: - Attributes: Cooldown: 3:00\nRequires: Operations Center + Description: Steal up to $2000 credits from a targeted enemy Refinery. + Subfaction: legion encyclopedia.power.shadowteam: Inherits: ^SupportPowerPreview - RenderSprites: - Image: shadteam.power + Buildable: + Icon: shadteam + Prerequisites: anyradar + BuildDuration: 6750 Tooltip: Name: Shadow Team Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Deploys a team of Shadow operatives at the target location. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: Nod Tech Center + Description: Calls for a Shadow Team; three stealth infantry that arrive via glider armed with grenades and a machine pistol.\n\nOperatives can be ordered to land via the deploy command. + Subfaction: shadow encyclopedia.power.frenzy: Inherits: ^SupportPowerPreview - RenderSprites: - Image: frenzy.power + Buildable: + Icon: frenzy + Prerequisites: tmpl + BuildDuration: 6750 Tooltip: Name: Frenzy Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Sends infantry into a frenzy, increasing their attack speed. - TooltipExtras: - Attributes: Cooldown: 4:00\nRequires: Temple of Nod + Description: Increases the movement speed and rate of fire of targeted units for a limited time.\n\nWarning: Units are weakened for a short time after frenzy wears off. + Subfaction: marked encyclopedia.power.techhack: Inherits: ^SupportPowerPreview - RenderSprites: - Image: techhack.power + Buildable: + Icon: techhack + Prerequisites: tmpl + BuildDuration: 18000 Tooltip: Name: Tech Hack Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Temporarily disables enemy defenses. - TooltipExtras: - Attributes: Cooldown: 4:00\nRequires: Operations Center + Description: Hack the targeted enemy production structure, providing access to a unit developed using enemy technology.\n\nFor infantry, vehicle and air production respectively: \n\n• Allies: Cryo Mortar, Reckoner, Phantom\n• Soviets: Cyberdog, Cyclops, Kamov\n• GDI: Sonic Mortar, Basilisk, Shade\n• Nod: Chem Mortar, Mantis, Vertigo\n• Scrin: Cyberscrin, Viper, Manticore + Subfaction: legion encyclopedia.power.assassinsquad: Inherits: ^SupportPowerPreview - RenderSprites: - Image: assassinsquad.power + Buildable: + Icon: assassinsquad + IconPalette: chrometd + BuildDuration: 6000 Tooltip: Name: Assassin Squad Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Deploys a squad of assassins at the target location. - TooltipExtras: - Attributes: Cooldown: 6:00\nRequires: Temple of Nod + Description: Deploy a squad of Assassins that can snipe infantry from long range and destroy buildings with C4.\n\nTarget infantry production structure to deploy. + AdditionalInfo: Requires Wrath Covenant. encyclopedia.power.hackercell: Inherits: ^SupportPowerPreview - RenderSprites: - Image: hackercell.power + Buildable: + Icon: hackercell + BuildDuration: 6000 Tooltip: Name: Hacker Cell Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Deploys a cell of hackers at the target location. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: Operations Center + Description: Deploy a squad of Hackers that can remotely capture buildings and take control of defenses.\n\nTarget infantry production structure to deploy. + AdditionalInfo: Requires Unity Covenant. encyclopedia.power.confessorcabal: Inherits: ^SupportPowerPreview - RenderSprites: - Image: confessorcabal.power + Buildable: + Icon: confessorcabal + IconPalette: chrometd + BuildDuration: 6000 Tooltip: Name: Confessor Cabal Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Deploys a group of Confessors at the target location. - TooltipExtras: - Attributes: Cooldown: 6:00\nRequires: Temple of Nod + Description: Deploy a Confessor Cabal which is able to construct Idols of Kane that buffs allies and debuffs enemies.\n\nTarget infantry production structure to deploy. + AdditionalInfo: Requires Zeal Covenant. encyclopedia.power.substrike: Inherits: ^SupportPowerPreview - RenderSprites: - Image: substrike.power + Buildable: + Icon: substrike + Prerequisites: weap.td + BuildDuration: 12000 Tooltip: Name: Subterranean Strike Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Launches a subterranean attack at the target location. - TooltipExtras: - Attributes: Cooldown: 6:00\nRequires: Nod Tech Center + Description: Deploys a squad of infantry via Subterranean APC. + Subfaction: marked # Scrin Support Powers encyclopedia.power.rift: Inherits: ^SupportPowerPreview - RenderSprites: - Image: riftpower.power + Buildable: + Icon: riftpower + Prerequisites: rfgn + BuildDuration: 13500 Tooltip: Name: Rift Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Opens a devastating rift at the target location, pulling in and damaging nearby units. - TooltipExtras: - Attributes: Cooldown: 7:00\nRequires: Rift Generator + Description: Initiate a Rift which deals heavy damage over time to a large area. encyclopedia.power.suppression: Inherits: ^SupportPowerPreview - RenderSprites: - Image: spresspower.power + Buildable: + Icon: spresspower + Prerequisites: mani + BuildDuration: 4500 Tooltip: Name: Suppression Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Suppresses enemy units in the target area, reducing their effectiveness. - TooltipExtras: - Attributes: Cooldown: 4:00\nRequires: Nerve Center + Description: Applies a suppression field to the target area, slowing unit movement and rate of fire. encyclopedia.power.gateway: Inherits: ^SupportPowerPreview - RenderSprites: - Image: gateway.power + Buildable: + Icon: gateway + IconPalette: chromes + Prerequisites: scrt + BuildDuration: 6000 Tooltip: Name: Gateway Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Opens a gateway that allows units to teleport to the target location. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: Signal Transmitter + Description: Creates a Gateway at the target location. Acts as the exit for any\n targeted infantry or vehicle production structure.\n\nRequires target location to be within vision. + AdditionalInfo: Requires Rebel Allegiance. encyclopedia.power.anathemapower: Inherits: ^SupportPowerPreview - RenderSprites: - Image: anathema.power + Buildable: + Icon: anathema + IconPalette: chromes + Prerequisites: scrt + BuildDuration: 3000 Tooltip: Name: Anathema Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Marks enemy units for destruction, gradually increasing their damage output and speed but destroying them when the effect ends. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: Technology Assembler + Description: Targeted vehicle greatly increases in power over time. After 30 seconds the unit explodes. + AdditionalInfo: Requires Malefic Allegiance. encyclopedia.power.owrath: Inherits: ^SupportPowerPreview - RenderSprites: - Image: owrath.power + Buildable: + Icon: owrath + IconPalette: chromes + Prerequisites: scrt + BuildDuration: 10500 Tooltip: Name: Overlord's Wrath Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Calls down a devastating beam attack from orbit. - TooltipExtras: - Attributes: Cooldown: 8:00\nRequires: Mothership + Description: A Tiberium meteor strikes the target location dealing heavy damage and creating a patch of Tiberium. + AdditionalInfo: Requires Loyalist Allegiance. encyclopedia.power.ionsurge: Inherits: ^SupportPowerPreview - RenderSprites: - Image: ionsurgepower.power + Buildable: + Icon: ionsurgepower + IconPalette: chromes + Prerequisites: anyradar + BuildDuration: 6750 Tooltip: Name: Ion Surge Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Creates an ion storm that damages enemies in the area. - TooltipExtras: - Attributes: Cooldown: 6:00\nRequires: Storm Column + Description: Increases the movement speed of all friendly units passing through the target area. + Subfaction: traveler encyclopedia.power.stormspikepower: Inherits: ^SupportPowerPreview - RenderSprites: - Image: stormspikepower.power + Buildable: + Icon: stormspikepower + IconPalette: chromes + Prerequisites: anyradar + BuildDuration: 7500 Tooltip: Name: Storm Spike Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Deploys a Storm Spike at the target location. - TooltipExtras: - Attributes: Cooldown: 5:00\nRequires: Nerve Center + Description: Creates a temporary Storm Column defensive structure at the target location. + Subfaction: reaper encyclopedia.power.buzzerswarm: Inherits: ^SupportPowerPreview - RenderSprites: - Image: buzzpower.power + Buildable: + Icon: buzzpower + IconPalette: chromes + Prerequisites: anyradar + BuildDuration: 7500 Tooltip: Name: Buzzer Swarm Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Deploys a swarm of Buzzers at the target location. - TooltipExtras: - Attributes: Cooldown: 3:00\nRequires: Warp Sphere + Description: Spawns a controllable swarm which blinds and damages anything nearby; particularly harmful to infantry. + Subfaction: harbinger encyclopedia.power.ichorseed: Inherits: ^SupportPowerPreview - RenderSprites: - Image: ichorpower.power + Buildable: + Icon: ichorpower + Prerequisites: scrt + BuildDuration: 6000 Tooltip: Name: Ichor Seed Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Plants an Ichor seed that grows Tiberium at the target location. - TooltipExtras: - Attributes: Cooldown: 4:00\nRequires: Growth Accelerator + Description: Grows Tiberium at selected location. encyclopedia.power.greatercoalescence: Inherits: ^SupportPowerPreview - RenderSprites: - Image: grclpower.power + Buildable: + Icon: grclpower + Prerequisites: scrt + BuildDuration: 6000 Tooltip: Name: Greater Coalescence Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Summons a powerful Coalescence entity at the target location. - TooltipExtras: - Attributes: Cooldown: 7:00\nRequires: Technology Assembler + Description: Spawns a controllable biomass which heals nearby allies and sustains itself by feeding off enemies.\n\nFeeding from power plants will shut them down temporarily. + Subfaction: collector + +encyclopedia.power.ichorspike: + Inherits: ^SupportPowerPreview + Buildable: + Icon: ichorspike + IconPalette: chromes + BuildDuration: 6000 + Tooltip: + Name: Ichor Spike + Encyclopedia: + Category: Scrin/Support Powers + EncyclopediaExtras: + Description: Creates an Ichor Spike at the target location. Resources within its area of influence grow and spread faster.\n\nAlso empowers units with Resource Conversion upgrade. + AdditionalInfo: Requires Loyalist Allegiance. + +encyclopedia.power.colonyspike: + Inherits: ^SupportPowerPreview + Buildable: + Icon: colonyspike + IconPalette: chromes + BuildDuration: 7500 + Tooltip: + Name: Colony Spike + Encyclopedia: + Category: Scrin/Support Powers + EncyclopediaExtras: + Description: Creates a Colony Spike at the target location. Enables production of non-defensive structures.\n\nWhen fully charged allows non-defensive structures to be built nearby.\n\nMust be built within range of a Colony Platform. + AdditionalInfo: Requires Rebel Allegiance. + +encyclopedia.power.voidspike: + Inherits: ^SupportPowerPreview + Buildable: + Icon: voidspike + IconPalette: chromes + BuildDuration: 6000 + Tooltip: + Name: Voidspike + Encyclopedia: + Category: Scrin/Support Powers + EncyclopediaExtras: + Description: Creates a Voidspike at the target location. Gradually transforms nearby resources into Black Tiberium which is devoid of most of its useful properties.\n\nDamages nearby enemy units. + AdditionalInfo: Requires Malefic Allegiance. encyclopedia.power.fleetrecall: Inherits: ^SupportPowerPreview - RenderSprites: - Image: fleetrecall.power + Buildable: + Icon: fleetrecall + IconPalette: chromes + Prerequisites: sign + BuildDuration: 5250 Tooltip: Name: Fleet Recall Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Teleports selected units back to the Mothership. - TooltipExtras: - Attributes: Cooldown: 4:00\nRequires: Mothership + Description: Recalls all selected Scrin fleet vessels back to the Signal Transmitter.\n\nApplies to: Mothership, Planetary Assault Carrier, Devastator encyclopedia.power.resourcescan: Inherits: ^SupportPowerPreview - RenderSprites: - Image: rescanpower.power + Buildable: + Icon: rescanpower + Prerequisites: anyradar + BuildDuration: 7500 Tooltip: Name: Resource Scan Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Reveals Tiberium deposits on the map. - TooltipExtras: - Attributes: Cooldown: 2:00\nRequires: Extractor + Description: Reveals the area surrounding all resources on the map. # IFV Variants ^IFVVariantPreview: @@ -893,7 +992,7 @@ encyclopedia.power.resourcescan: Sequence: turret Turreted: Encyclopedia: - Scale: 1.5 + Scale: 2 encyclopedia.ifv.missile: Inherits: ^IFVVariantPreview @@ -906,24 +1005,9 @@ encyclopedia.ifv.missile: VariantGroup: Allies Description: IFV loaded with a Rocket Soldier. Fires anti-air/anti-armor missiles. TooltipExtras: - Strengths: Strong vs Aircraft, Heavy Armor - Weaknesses: Weak vs Infantry - Attributes: Passenger: Rocket Soldier - -encyclopedia.ifv.ggi: - Inherits: ^IFVVariantPreview - WithSpriteTurret: - Sequence: turret-ggi - Tooltip: - Name: GGI IFV - EncyclopediaExtras: - VariantOf: IFV - VariantGroup: Allies - Description: IFV loaded with a Guardian GI. Fires powerful anti-armor rounds. - TooltipExtras: - Strengths: Strong vs Vehicles, Heavy Armor - Weaknesses: Weak vs Infantry - Attributes: Passenger: Guardian GI + Strengths: • Strong vs Aircraft, Heavy Armor + Weaknesses: • Weak vs Infantry + Attributes: • Passenger: Rocket Soldier encyclopedia.ifv.mg: Inherits: ^IFVVariantPreview @@ -936,99 +1020,115 @@ encyclopedia.ifv.mg: VariantGroup: Allies Description: IFV loaded with a Rifle Infantry. Fires a rapid machine gun. TooltipExtras: - Strengths: Strong vs Infantry, Light Vehicles - Weaknesses: Weak vs Heavy Armor - Attributes: Passenger: Rifle Infantry + Strengths: • Strong vs Infantry, Light Armor + Weaknesses: • Weak vs Heavy Armor, Defenses\n• Cannot attack Aircraft + Attributes: • Passenger: Rifle Infantry -encyclopedia.ifv.grenade: +encyclopedia.ifv.engi: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-frag + Sequence: turret-engi Tooltip: - Name: Grenade IFV + Name: Engineer IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Soviet - Description: IFV loaded with a Grenadier. Launches fragmentation grenades. + VariantGroup: Allies + Description: IFV loaded with an Engineer. Can capture enemy structures. TooltipExtras: - Strengths: Strong vs Infantry, Buildings - Weaknesses: Weak vs Vehicles - Attributes: Passenger: Grenadier + Weaknesses: • Unarmed + Attributes: • Captures structures\n• Passenger: Engineer -encyclopedia.ifv.flame: +encyclopedia.ifv.med: Inherits: ^IFVVariantPreview - WithSpriteTurret: - Sequence: turret-flame Tooltip: - Name: Flamethrower IFV + Name: Medical IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Soviet - Description: IFV loaded with a Flamethrower. Projects devastating flames. + VariantGroup: Allies + Description: IFV loaded with a Medic. Heals nearby friendly infantry. TooltipExtras: - Strengths: Strong vs Infantry, Buildings - Weaknesses: Weak vs Vehicles - Attributes: Passenger: Flamethrower + Weaknesses: • Unarmed + Attributes: • Heals friendly infantry\n• Passenger: Medic + -WithSpriteTurret: + WithIdleOverlay@MEDIC: + Sequence: medic + Offset: 0,0,40 + IsDecoration: True -encyclopedia.ifv.chem: +encyclopedia.ifv.mech: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-chem + Sequence: turret-mech Tooltip: - Name: Chemical IFV + Name: Repair IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Nod - Description: IFV loaded with a Toxin Soldier. Sprays toxic chemicals. + VariantGroup: Allies + Description: IFV loaded with a Mechanic. Repairs nearby friendly vehicles. TooltipExtras: - Strengths: Strong vs Infantry - Weaknesses: Weak vs Vehicles, Buildings - Attributes: Passenger: Toxin Soldier + Weaknesses: • Unarmed + Attributes: • Repairs friendly vehicles\n• Passenger: Mechanic -encyclopedia.ifv.laser: +encyclopedia.ifv.spy: + Inherits: ^IFVVariantPreview + Tooltip: + Name: Spy IFV + EncyclopediaExtras: + VariantOf: IFV + VariantGroup: Allies + Description: IFV loaded with a Spy. Can sabotage enemy structures and detect cloaked units. + TooltipExtras: + Weaknesses: • Cannot deal damage + Attributes: • Sabotages enemy structures:\n • Power Plants: Power outage\n • Barracks/Factory: Infantry/vehicles produced as veteran\n • Superweapons: Reset timer\n • Radar: Reset shroud\n • Helipad: Single-use paratroopers\n • Airfield/Gravity Stabilizer: Single-use airstrike\n• Passenger: Spy + -WithSpriteTurret: + WithIdleOverlay@SPINNER: + Sequence: spinner + Offset: 0,0,200 + +encyclopedia.ifv.snip: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-laser + Sequence: turret-snip Tooltip: - Name: Laser IFV + Name: Sniper IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Nod - Description: IFV loaded with an Acolyte. Fires a powerful laser beam. + VariantGroup: Allies + Description: IFV loaded with a Sniper. Fires long-range sniper rounds. TooltipExtras: - Strengths: Strong vs Infantry, Light Vehicles - Weaknesses: Moderate vs Heavy Armor - Attributes: Passenger: Acolyte + Strengths: • Strong vs Infantry, Light Armor, Heavy Armor + Weaknesses: • Weak vs Defenses, Buildings\n• Cannot attack Aircraft + Attributes: • Passenger: Sniper -encyclopedia.ifv.rad: +encyclopedia.ifv.enfo: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-rad + Sequence: turret-enfo Tooltip: - Name: Desolator IFV + Name: Enforcer IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Soviet - Description: IFV loaded with a Desolator. Sprays deadly radiation. + VariantGroup: Allies + Description: IFV loaded with an Enforcer. Fires double shotgun blasts. TooltipExtras: - Strengths: Strong vs Infantry, Vehicles - Weaknesses: Damages friendly units in range - Attributes: Passenger: Desolator + Strengths: • Strong vs Infantry, Light Armor + Weaknesses: • Weak vs Heavy Armor, Defenses\n• Cannot attack Aircraft + Attributes: • Passenger: Enforcer -encyclopedia.ifv.cmdo: +encyclopedia.ifv.ggi: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-cmdo + Sequence: turret-ggi Tooltip: - Name: Commando IFV + Name: GGI IFV EncyclopediaExtras: VariantOf: IFV VariantGroup: Allies - Description: IFV loaded with a Commando (Tanya/Boris). Fires powerful sniper bursts with high damage per shot. + Description: IFV loaded with a Guardian GI. Fires powerful anti-armor rounds. TooltipExtras: - Strengths: Strong vs Infantry - Weaknesses: Weak vs Vehicles - Attributes: Passenger: Tanya, Boris + Strengths: • Strong vs Aircraft, Heavy Armor + Weaknesses: • Weak vs Infantry + Attributes: • Passenger: Guardian GI encyclopedia.ifv.seal: Inherits: ^IFVVariantPreview @@ -1039,402 +1139,443 @@ encyclopedia.ifv.seal: EncyclopediaExtras: VariantOf: IFV VariantGroup: Allies - Description: IFV loaded with a Navy SEAL. Fires a rapid-fire MP5 submachine gun. + Description: IFV loaded with a Navy SEAL. Fires heavy anti-infantry rounds. TooltipExtras: - Strengths: Strong vs Infantry - Weaknesses: Weak vs Vehicles - Attributes: Passenger: Navy SEAL + Strengths: • Strong vs Infantry + Weaknesses: • Cannot attack Aircraft, Vehicles, Buildings + Attributes: • Passenger: Navy SEAL -encyclopedia.ifv.mech: +encyclopedia.ifv.cryo: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-mech + Sequence: turret-cryo Tooltip: - Name: Repair IFV + Name: Cryo IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Utility - Description: IFV loaded with a Mechanic. Repairs nearby friendly vehicles. + VariantGroup: Allies + Description: IFV loaded with a Cryo Trooper. Fires freezing blasts. TooltipExtras: - Strengths: Repairs vehicles - Attributes: Passenger: Mechanic + Strengths: • Strong vs Infantry, Light Armor + Weaknesses: • Weak vs Buildings, Defenses\n• Cannot attack Aircraft + Attributes: • Slows enemy units and makes them take increased damage\n• Passenger: Cryo Trooper -encyclopedia.ifv.snip: +encyclopedia.ifv.tigr: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-snip + Sequence: turret-tigr Tooltip: - Name: Sniper IFV + Name: Tiger Guard IFV EncyclopediaExtras: VariantOf: IFV VariantGroup: Allies - Description: IFV loaded with a Sniper. Fires long-range sniper rounds. + Description: IFV loaded with a Tiger Guard. Fires long range missiles. TooltipExtras: - Strengths: Strong vs Infantry - Weaknesses: Weak vs Vehicles - Attributes: Passenger: Sniper + Strengths: • Strong vs Aircraft, Heavy Armor + Weaknesses: • Weak vs Infantry + Attributes: • Passenger: Tiger Guard -encyclopedia.ifv.enli: +encyclopedia.ifv.hopl: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-enli + Sequence: turret-hopl Tooltip: - Name: Enlightened IFV + Name: Hoplite IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Nod - Description: IFV loaded with an Enlightened. Fires a powerful laser. + VariantGroup: Allies + Description: IFV loaded with a Hoplite. Fires a prism beam. TooltipExtras: - Strengths: Strong vs Infantry, Vehicles - Attributes: Passenger: Enlightened + Strengths: • Strong vs Infantry, Light Armor, Buildings + Weaknesses: • Weak vs Heavy Armor\n• Cannot attack Aircraft + Attributes: • Passenger: Hoplite -encyclopedia.ifv.rmbc: +encyclopedia.ifv.cmdo: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-rmbc + Sequence: turret-cmdo Tooltip: - Name: Cyborg Elite IFV + Name: Commando IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Nod - Description: IFV loaded with a Cyborg Elite. Fires plasma bolts. + VariantGroup: Allies + Description: IFV loaded with a Commando (Tanya/Boris/Commando). Fires rapid and powerful sniper bursts with high damage per shot. TooltipExtras: - Strengths: Strong vs Infantry, Vehicles - Attributes: Passenger: Cyborg Elite + Strengths: • Strong vs Infantry + Weaknesses: • Cannot attack Aircraft, Vehicles, Buildings + Attributes: • Passenger: Tanya, Boris, Commando -encyclopedia.ifv.bh: +encyclopedia.ifv.grenade: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-bh + Sequence: turret-frag Tooltip: - Name: Black Hand IFV + Name: Grenade IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Nod - Description: IFV loaded with a Black Hand. Projects devastating flames. + VariantGroup: Other Factions + Description: IFV loaded with a Grenadier. Launches fragmentation grenades. TooltipExtras: - Strengths: Strong vs Infantry, Buildings - Weaknesses: Weak vs Vehicles - Attributes: Passenger: Black Hand + Strengths: • Strong vs Buildings, Defenses, Light Armor + Weaknesses: • Weak vs Heavy Armor\n• Cannot attack Aircraft + Attributes: • Passenger: Grenadier -encyclopedia.ifv.pdisc: +encyclopedia.ifv.flame: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-pdisc + Sequence: turret-flame Tooltip: - Name: Plasma Disc IFV + Name: Flamethrower IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Scrin - Description: IFV loaded with a Disc Thrower. Fires plasma discs. + VariantGroup: Other Factions + Description: IFV loaded with a Flamethrower. Projects devastating flames. TooltipExtras: - Strengths: Strong vs Infantry, Light Vehicles - Attributes: Passenger: Disc Thrower + Strengths: • Strong vs Infantry, Light Armor, Buildings + Weaknesses: • Weak vs Heavy Armor\n• Cannot attack Aircraft + Attributes: • Passenger: Flamethrower -encyclopedia.ifv.shard: +encyclopedia.ifv.chem: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-shard + Sequence: turret-chem Tooltip: - Name: Shard IFV + Name: Chemical IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Scrin - Description: IFV loaded with a Shard Trooper. Fires Tiberium shards. + VariantGroup: Other Factions + Description: IFV loaded with a Toxin Soldier. Sprays toxic chemicals. TooltipExtras: - Strengths: Strong vs Infantry, Light Vehicles - Attributes: Passenger: Shard Trooper + Strengths: • Strong vs Infantry, Light Armor, Buildings + Weaknesses: • Weak vs Heavy Armor\n• Cannot attack Aircraft + Attributes: • Passenger: Chem Warrior -encyclopedia.ifv.cryo: +encyclopedia.ifv.rad: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-cryo + Sequence: turret-rad Tooltip: - Name: Cryo IFV + Name: Desolator IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Allies - Description: IFV loaded with a Cryo Legionnaire. Fires freezing blasts. + VariantGroup: Other Factions + Description: IFV loaded with a Desolator. Fires a radiation beam. TooltipExtras: - Strengths: Slows and freezes enemies - Attributes: Passenger: Cryo Legionnaire + Strengths: • Strong vs Infantry, Light Armor + Weaknesses: • Weak vs Heavy Armor, Defenses, Buildings\n• Cannot attack Aircraft + Attributes: • Passenger: Desolator, Rad Trooper -encyclopedia.ifv.enfo: +encyclopedia.ifv.ivan: Inherits: ^IFVVariantPreview - WithSpriteTurret: - Sequence: turret-enfo Tooltip: - Name: Enforcer IFV + Name: Explosive IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Soviet - Description: IFV loaded with an Enforcer. Fires shotgun blasts. + VariantGroup: Other Factions + Description: IFV loaded with a Crazy Ivan. Self-destructs on contact with the target, dealing massive damage. TooltipExtras: - Strengths: Strong vs Infantry at close range - Attributes: Passenger: Enforcer + Strengths: • Strong vs Everything + Weaknesses: • Self-destructs + Attributes: • Passengers: Crazy Ivan, Burster, Terror Dog + -WithSpriteTurret: + WithIdleOverlay@IVAN: + Sequence: nuke + Offset: 0,0,40 -encyclopedia.ifv.engi: +encyclopedia.ifv.tes: Inherits: ^IFVVariantPreview - WithSpriteTurret: - Sequence: turret-engi Tooltip: - Name: Engineer IFV + Name: Tesla IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Utility - Description: IFV loaded with an Engineer. Can capture enemy structures. + VariantGroup: Other Factions + Description: IFV loaded with a Shock Trooper or Tesla Trooper. Fires a powerful tesla bolt. TooltipExtras: - Strengths: Captures structures - Attributes: Passenger: Engineer + Strengths: • Strong vs Infantry, Heavy Armor, Light Armor + Weaknesses: • Cannot attack Aircraft + Attributes: • Passenger: Shock Trooper, Tesla Trooper + -WithSpriteTurret: + WithIdleOverlay@TESLA: + Sequence: tesla -encyclopedia.ifv.ztrp: +encyclopedia.ifv.psy: Inherits: ^IFVVariantPreview - WithSpriteTurret: - Sequence: turret-ztrp Tooltip: - Name: Zone Trooper IFV + Name: Psychic IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: GDI - Description: IFV loaded with a Zone Trooper. Fires powerful railgun shots. + VariantGroup: Other Factions + Description: IFV loaded with Yuri or a Mastermind. Can mind control enemy units. Control is lost if passenger exits. TooltipExtras: - Strengths: Strong vs Vehicles, Heavy Armor - Attributes: Passenger: Zone Trooper + Strengths: • Strong vs Infantry, Vehicles + Weaknesses: • Cannot deal damage\n• Cannot attack Aircraft + Attributes: • Can mind control enemy units\n• Control lost if passenger exits\n• Passenger: Yuri, Mastermind + -WithSpriteTurret: + WithIdleOverlay@PSYCHIC: + Sequence: psy + Palette: scrin -encyclopedia.ifv.zdef: +encyclopedia.ifv.cmsr: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-zdef + Sequence: turret-mg Tooltip: - Name: Zone Defender IFV + Name: Commissar IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: GDI - Description: IFV loaded with a Zone Defender. Fires anti-air missiles. + VariantGroup: Other Factions + Description: IFV loaded with a Commissar. Inspires nearby friendly infantry, boosting their combat effectiveness. TooltipExtras: - Strengths: Strong vs Aircraft - Attributes: Passenger: Zone Defender + Strengths: Inspires nearby allies + Weaknesses: Unarmed + Attributes: • Passenger: Commissar + WithPreviewDecoration@CMSR: + Image: pips + Sequence: pip-cmsr + Position: TopLeft -encyclopedia.ifv.zrai: +encyclopedia.ifv.laser: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-zrai + Sequence: turret-laser Tooltip: - Name: Zone Raider IFV + Name: Laser IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: GDI - Description: IFV loaded with a Zone Raider. Fires sonic blasts. + VariantGroup: Other Factions + Description: IFV loaded with an Acolyte or Templar. Fires a powerful laser beam. TooltipExtras: - Strengths: Strong vs Infantry, Structures - Attributes: Passenger: Zone Raider + Strengths: • Strong vs Infantry, Light Armor + Weaknesses: • Weak vs Heavy Armor, Defenses, Buildings\n• Cannot attack Aircraft + Attributes: • Passenger: Acolyte, Templar -encyclopedia.ifv.tigr: +encyclopedia.ifv.enli: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-tigr + Sequence: turret-enli Tooltip: - Name: Tiger Guard IFV + Name: Enlightened IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Soviet - Description: IFV loaded with a Tiger Guard. Fires dual machine guns. + VariantGroup: Other Factions + Description: IFV loaded with an Enlightened. Fires a powerful particle beam. TooltipExtras: - Strengths: Strong vs Infantry - Attributes: Passenger: Tiger Guard + Strengths: • Strong vs Heavy Armor, Light Armor + Weaknesses: • Weak vs Infantry\n• Cannot attack Aircraft + Attributes: • Passenger: Enlightened -encyclopedia.ifv.hopl: +encyclopedia.ifv.rmbc: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-hopl + Sequence: turret-rmbc Tooltip: - Name: Hoplite IFV + Name: Cyborg Elite IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: GDI - Description: IFV loaded with a Hoplite. Fires EMP rounds. + VariantGroup: Other Factions + Description: IFV loaded with a Cyborg Elite. Fires a plasma cannon. TooltipExtras: - Strengths: Disables vehicles - Attributes: Passenger: Hoplite + Strengths: • Strong vs Heavy Armor, Light Armor, Infantry + Weaknesses: • Cannot attack Aircraft -encyclopedia.ifv.impl: +encyclopedia.ifv.bh: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-impl + Sequence: turret-bh Tooltip: - Name: Impaler IFV + Name: Black Hand IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Scrin - Description: IFV loaded with an Impaler. Fires spikes. + VariantGroup: Other Factions + Description: IFV loaded with a Black Hand. Projects devastating anti-vehicle flames. TooltipExtras: - Strengths: Strong vs Infantry - Attributes: Passenger: Impaler + Strengths: • Strong vs Heavy Armor, Light Armor + Weaknesses: • Weak vs Buildings, Defenses\n• Cannot attack Aircraft + Attributes: • Passenger: Black Hand -encyclopedia.ifv.stlk: +encyclopedia.ifv.conf: Inherits: ^IFVVariantPreview - RenderSprites: - Image: ifv - Palette: playerscrin WithSpriteTurret: - Sequence: turret-stlk + Sequence: turret-conf Tooltip: - Name: Stalker IFV + Name: Confessor IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Scrin - Description: IFV loaded with a Stalker. Fires plasma bolts. + VariantGroup: Other Factions + Description: IFV loaded with a Confessor. Fires hallucinogenic rounds. TooltipExtras: - Strengths: Strong vs Infantry, Light Vehicles - Attributes: Passenger: Stalker (Scrin) + Strengths: • Strong vs Infantry, Light Armor + Weaknesses: • Weak vs Heavy Armor, Defenses, Buildings\n• Cannot attack Aircraft + Attributes: • Explosion causes units to attack indiscriminately\n• Passenger: Confessor -encyclopedia.ifv.disin: +encyclopedia.ifv.reap: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-disin + Sequence: turret-reap Tooltip: - Name: Disintegrator IFV + Name: Reaper IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Scrin - Description: IFV loaded with a Disintegrator. Fires disintegration beams. + VariantGroup: Other Factions + Description: IFV loaded with a Cyborg Reaper. Fires anti-personnel missiles. TooltipExtras: - Strengths: Strong vs Vehicles, Buildings - Attributes: Passenger: Disintegrator (Scrin) + Strengths: • Strong vs Infantry, Light Armor, Buildings + Weaknesses: • Weak vs Heavy Armor + Attributes: • Passenger: Reaper -encyclopedia.ifv.conf: +encyclopedia.ifv.shad: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-conf + Sequence: turret-mg Tooltip: - Name: Confessor IFV + Name: Shadow IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Nod - Description: IFV loaded with a Confessor. Fires hallucinogenic rounds. + VariantGroup: Other Factions + Description: IFV loaded with a Shadow Operative. Armed with a heavy machinegun and able to cloak. TooltipExtras: - Strengths: Strong vs Infantry - Attributes: Passenger: Confessor (Nod) + Strengths: • Strong vs Infantry, Light Armor + Weaknesses: • Weak vs Heavy Armor, Defenses\n• Cannot attack Aircraft + Attributes: • Passenger: Shadow Operative -encyclopedia.ifv.reap: +encyclopedia.ifv.hack: Inherits: ^IFVVariantPreview - WithSpriteTurret: - Sequence: turret-reap Tooltip: - Name: Reaper IFV + Name: Hacker IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Nod - Description: IFV loaded with a Reaper. Fires Tiberium-infused rounds. + VariantGroup: Other Factions + Description: IFV loaded with a Hacker. Can hack enemy defenses and buildings. Control is lost if passenger exits. TooltipExtras: - Strengths: Strong vs Infantry - Attributes: Passenger: Reaper (Nod) + Strengths: • Strong vs Defenses, Buildings + Weaknesses: • Cannot deal damage\n• Control lost if passenger exits + Attributes: • Passenger: Hacker + -WithSpriteTurret: + WithIdleOverlay@HACK: + Sequence: hack -encyclopedia.ifv.med: +encyclopedia.ifv.ztrp: Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-ztrp Tooltip: - Name: Medical IFV + Name: Zone Trooper IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Utility - Description: IFV loaded with a Medic. Heals nearby friendly infantry. + VariantGroup: Other Factions + Description: IFV loaded with a Zone Trooper. Fires powerful railgun shots. TooltipExtras: - Strengths: Heals infantry - Weaknesses: Unarmed - Attributes: Passenger: Medic + Strengths: • Strong vs Heavy Armor, Light Armor, Defenses + Weaknesses: • Weak vs Infantry, Buildings\n• Cannot attack Aircraft + Attributes: • Passenger: Zone Trooper -encyclopedia.ifv.ivan: +encyclopedia.ifv.zdef: Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-zdef Tooltip: - Name: Explosive IFV + Name: Zone Defender IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Soviet - Description: IFV loaded with a Crazy Ivan. Self-destructs on contact with the target, dealing massive damage. + VariantGroup: Other Factions + Description: IFV loaded with a Zone Defender. Fires ion blasts. TooltipExtras: - Strengths: Strong vs Everything - Weaknesses: Self-destructs on attack - Attributes: Passenger: Crazy Ivan + Strengths: • Strong vs Heavy Armor, Light Armor, Defenses + Weaknesses: • Weak vs Infantry, Buildings\n• Cannot attack Aircraft + Attributes: • Passenger: Zone Defender -encyclopedia.ifv.spy: +encyclopedia.ifv.zrai: Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-zrai Tooltip: - Name: Spy IFV + Name: Zone Raider IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Allies - Description: IFV loaded with a Spy. Can infiltrate enemy structures and detects cloaked units. + VariantGroup: Other Factions + Description: IFV loaded with a Zone Raider. Fires sonic grenades. TooltipExtras: - Strengths: Infiltrates structures, detects cloaked - Weaknesses: Cannot deal damage - Attributes: Passenger: Spy + Strengths: • Strong vs Light Armor, Buildings + Weaknesses: • Cannot attack Aircraft + Attributes: • Passenger: Zone Raider -encyclopedia.ifv.tes: +encyclopedia.ifv.disin: Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-disin Tooltip: - Name: Tesla IFV + Name: Disintegrator IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Soviet - Description: IFV loaded with a Shock Trooper or Tesla Trooper. Fires a powerful tesla bolt. + VariantGroup: Other Factions + Description: IFV loaded with a Disintegrator. Fires disintegration beams. TooltipExtras: - Strengths: Strong vs Infantry, Vehicles - Weaknesses: Cannot attack Aircraft - Attributes: Passenger: Shock Trooper, Tesla Trooper + Strengths: • Strong vs Aircraft, Heavy Armor + Weaknesses: • Weak vs Infantry + Attributes: • Passenger: Disintegrator (Scrin) -encyclopedia.ifv.psy: +encyclopedia.ifv.shard: Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-shard Tooltip: - Name: Psychic IFV + Name: Shard IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Special - Description: IFV loaded with a Yuri Prime (Yuri) or Mastermind (Scrin). Can mind control enemy units. Control is lost if passenger exits. + VariantGroup: Other Factions + Description: IFV loaded with a Ravager or Eviscerator. Fires Tiberium shards. TooltipExtras: - Strengths: Mind controls enemies - Weaknesses: Cannot deal damage, control lost on exit - Attributes: Passenger: Yuri Prime (Yuri), Mastermind (Scrin) + Strengths: • Strong vs Infantry, Light Armor + Weaknesses: • Weak vs Heavy Armor, Defenses, Buildings\n• Cannot attack Aircraft + Attributes: • Passenger: Ravager, Eviscerator -encyclopedia.ifv.cmsr: +encyclopedia.ifv.pdisc: Inherits: ^IFVVariantPreview + WithSpriteTurret: + Sequence: turret-pdisc Tooltip: - Name: Commissar IFV + Name: Plasma Disc IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Soviet - Description: IFV loaded with a Commissar. Inspires nearby friendly units, boosting their combat effectiveness. + VariantGroup: Other Factions + Description: Fires plasma discs. TooltipExtras: - Strengths: Inspires nearby allies - Weaknesses: Unarmed - Attributes: Passenger: Commissar + Strengths: • Strong vs Buildings, Defenses, Heavy Armor, Light Armor + Weaknesses: • Weak vs Infantry\n• Cannot attack Aircraft + Attributes: • Passenger: Intruder, Marauder -encyclopedia.ifv.shad: +encyclopedia.ifv.impl: Inherits: ^IFVVariantPreview WithSpriteTurret: - Sequence: turret-cmdo + Sequence: turret-impl Tooltip: - Name: Shadow IFV + Name: Impaler IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Nod - Description: IFV loaded with a Shadow. Fires a sniper rifle effective against infantry. + VariantGroup: Other Factions + Description: IFV loaded with an Impaler. Fires long range impaling projectiles. TooltipExtras: - Strengths: Strong vs Infantry - Weaknesses: Weak vs Vehicles - Attributes: Passenger: Shadow + Strengths: • Strong vs Light Armor, Infantry + Weaknesses: • Weak vs Heavy Armor, Defenses\n• Cannot attack Aircraft + Attributes: • Passenger: Impaler -encyclopedia.ifv.hack: +encyclopedia.ifv.stlk: Inherits: ^IFVVariantPreview + RenderSprites: + Image: ifv + WithSpriteTurret: + Sequence: turret-stlk + Palette: playerscrin Tooltip: - Name: Hacker IFV + Name: Stalker IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Nod - Description: IFV loaded with a Hacker. Can mind control enemy defenses and buildings. Control is lost if passenger exits. + VariantGroup: Other Factions + Description: IFV loaded with a Stalker. Fires anti-infantry plasma darts and can cloak. TooltipExtras: - Strengths: Strong vs Defenses, Buildings - Weaknesses: Cannot deal direct damage, control lost on exit - Attributes: Passenger: Hacker + Strengths: • Strong vs Light Armor, Infantry + Weaknesses: • Weak vs Heavy Armor, Defenses\n• Cannot attack Aircraft + Attributes: • Passenger: Stalker encyclopedia.ifv.mortchem: Inherits: ^IFVVariantPreview @@ -1444,12 +1585,12 @@ encyclopedia.ifv.mortchem: Name: Chemical Mortar IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Nod + VariantGroup: Other Factions Description: IFV loaded with a Chemical Mortar Infantry. Fires chemical mortar rounds that leave toxic residue. TooltipExtras: - Strengths: Strong vs Infantry, area denial - Weaknesses: Minimum range - Attributes: Passenger: Chemical Mortar + Strengths: • Strong vs Infantry, Buildings + Weaknesses: • Weak vs Heavy Armor\n• Cannot attack Aircraft\n• Has difficulty hitting moving targets + Attributes: • Passenger: Chemical Mortar encyclopedia.ifv.mortcryo: Inherits: ^IFVVariantPreview @@ -1459,12 +1600,12 @@ encyclopedia.ifv.mortcryo: Name: Cryo Mortar IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: Allies + VariantGroup: Other Factions Description: IFV loaded with a Cryo Mortar Infantry. Fires cryo mortar rounds that slow and freeze enemies. TooltipExtras: - Strengths: Slows and freezes enemies - Weaknesses: Minimum range - Attributes: Passenger: Cryo Mortar + Strengths: • Strong vs Infantry, Buildings + Weaknesses: • Weak vs Heavy Armor\n• Cannot attack Aircraft\n• Has difficulty hitting moving targets + Attributes: • Passenger: Cryo Mortar encyclopedia.ifv.mortsonic: Inherits: ^IFVVariantPreview @@ -1474,12 +1615,12 @@ encyclopedia.ifv.mortsonic: Name: Sonic Mortar IFV EncyclopediaExtras: VariantOf: IFV - VariantGroup: GDI - Description: IFV loaded with a Sonic Mortar Infantry. Fires sonic mortar rounds that deal splash damage. + VariantGroup: Other Factions + Description: IFV loaded with a Sonic Mortar Infantry. Fires sonic mortar rounds that cause concussion. TooltipExtras: - Strengths: Strong vs Infantry, Buildings - Weaknesses: Minimum range - Attributes: Passenger: Sonic Mortar + Strengths: • Strong vs Infantry, Buildings + Weaknesses: • Weak vs Heavy Armor\n• Cannot attack Aircraft\n• Has difficulty hitting moving targets + Attributes: • Passenger: Sonic Mortar # Reckoner Variants ^ReckonerVariantPreview: @@ -1491,7 +1632,7 @@ encyclopedia.ifv.mortsonic: Sequence: turret Turreted: Encyclopedia: - Scale: 1.5 + Scale: 2 encyclopedia.reck.missile: Inherits: ^ReckonerVariantPreview @@ -1504,9 +1645,9 @@ encyclopedia.reck.missile: VariantGroup: Allies Description: Reckoner loaded with a Rocket Soldier. Fires anti-air/anti-armor missiles. TooltipExtras: - Strengths: Strong vs Aircraft, Heavy Armor - Weaknesses: Weak vs Infantry - Attributes: Passenger: Rocket Soldier + Strengths: • Strong vs Aircraft, Heavy Armor + Weaknesses: • Weak vs Infantry + Attributes: • Passenger: Rocket Soldier encyclopedia.reck.mg: Inherits: ^ReckonerVariantPreview @@ -1519,128 +1660,70 @@ encyclopedia.reck.mg: VariantGroup: Allies Description: Reckoner loaded with a Rifle Infantry. Fires a rapid machine gun. TooltipExtras: - Strengths: Strong vs Infantry, Light Vehicles - Weaknesses: Weak vs Heavy Armor - Attributes: Passenger: Rifle Infantry + Strengths: • Strong vs Infantry, Light Armor + Weaknesses: • Weak vs Heavy Armor, Defenses\n• Cannot attack Aircraft + Attributes: • Passenger: Rifle Infantry -encyclopedia.reck.grenade: +encyclopedia.reck.engi: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-frag - Tooltip: - Name: Grenade Reckoner - EncyclopediaExtras: - VariantOf: RECK - VariantGroup: Soviet - Description: Reckoner loaded with a Grenadier. Launches fragmentation grenades. - TooltipExtras: - Strengths: Strong vs Infantry, Buildings - Weaknesses: Weak vs Vehicles - Attributes: Passenger: Grenadier - -encyclopedia.reck.flame: - Inherits: ^ReckonerVariantPreview - WithSpriteTurret: - Sequence: turret-flame - Tooltip: - Name: Flamethrower Reckoner - EncyclopediaExtras: - VariantOf: RECK - VariantGroup: Soviet - Description: Reckoner loaded with a Flamethrower. Projects devastating flames. - TooltipExtras: - Strengths: Strong vs Infantry, Buildings - Weaknesses: Weak vs Vehicles - Attributes: Passenger: Flamethrower - -encyclopedia.reck.chem: - Inherits: ^ReckonerVariantPreview - WithSpriteTurret: - Sequence: turret-chem - Tooltip: - Name: Chemical Reckoner - EncyclopediaExtras: - VariantOf: RECK - VariantGroup: Nod - Description: Reckoner loaded with a Toxin Soldier. Sprays toxic chemicals. - TooltipExtras: - Strengths: Strong vs Infantry - Weaknesses: Weak vs Vehicles, Buildings - Attributes: Passenger: Toxin Soldier - -encyclopedia.reck.laser: - Inherits: ^ReckonerVariantPreview - WithSpriteTurret: - Sequence: turret-laser - Tooltip: - Name: Laser Reckoner - EncyclopediaExtras: - VariantOf: RECK - VariantGroup: Nod - Description: Reckoner loaded with an Acolyte. Fires a powerful laser beam. - TooltipExtras: - Strengths: Strong vs Infantry, Light Vehicles - Weaknesses: Moderate vs Heavy Armor - Attributes: Passenger: Acolyte - -encyclopedia.reck.rad: - Inherits: ^ReckonerVariantPreview - WithSpriteTurret: - Sequence: turret-rad + Sequence: turret-engi Tooltip: - Name: Desolator Reckoner + Name: Engineer Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Soviet - Description: Reckoner loaded with a Desolator. Sprays deadly radiation. + VariantGroup: Allies + Description: Reckoner loaded with an Engineer. Can capture enemy structures. TooltipExtras: - Strengths: Strong vs Infantry, Vehicles - Weaknesses: Damages friendly units in range - Attributes: Passenger: Desolator + Weaknesses: • Unarmed + Attributes: • Captures structures\n• Passenger: Engineer -encyclopedia.reck.cmdo: +encyclopedia.reck.med: Inherits: ^ReckonerVariantPreview - WithSpriteTurret: - Sequence: turret-cmdo Tooltip: - Name: Commando Reckoner + Name: Medical Reckoner EncyclopediaExtras: VariantOf: RECK VariantGroup: Allies - Description: Reckoner loaded with a Commando (Tanya/Boris). Fires powerful sniper bursts with high damage per shot. + Description: Reckoner loaded with a Medic. Heals nearby friendly infantry. TooltipExtras: - Strengths: Strong vs Infantry - Weaknesses: Weak vs Vehicles - Attributes: Passenger: Tanya, Boris + Weaknesses: • Unarmed + Attributes: • Heals friendly infantry\n• Passenger: Medic + -WithSpriteTurret: + WithIdleOverlay@MEDIC: + Sequence: medic + Offset: 0,0,40 + IsDecoration: True -encyclopedia.reck.seal: +encyclopedia.reck.mech: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-cmdo + Sequence: turret-mech Tooltip: - Name: SEAL Reckoner + Name: Repair Reckoner EncyclopediaExtras: VariantOf: RECK VariantGroup: Allies - Description: Reckoner loaded with a Navy SEAL. Fires a rapid-fire MP5 submachine gun. + Description: Reckoner loaded with a Mechanic. Repairs nearby friendly vehicles. TooltipExtras: - Strengths: Strong vs Infantry - Weaknesses: Weak vs Vehicles - Attributes: Passenger: Navy SEAL + Weaknesses: • Unarmed + Attributes: • Repairs friendly vehicles\n• Passenger: Mechanic -encyclopedia.reck.mech: +encyclopedia.reck.spy: Inherits: ^ReckonerVariantPreview - WithSpriteTurret: - Sequence: turret-mech Tooltip: - Name: Repair Reckoner + Name: Spy Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Utility - Description: Reckoner loaded with a Mechanic. Repairs nearby friendly vehicles. + VariantGroup: Allies + Description: Reckoner loaded with a Spy. Can sabotage enemy structures and detect cloaked units. TooltipExtras: - Strengths: Repairs vehicles - Attributes: Passenger: Mechanic + Weaknesses: • Cannot deal damage + Attributes: • Sabotages enemy structures:\n • Power Plants: Power outage\n • Barracks/Factory: Infantry/vehicles produced as veteran\n • Superweapons: Reset timer\n • Radar: Reset shroud\n • Helipad: Single-use paratroopers\n • Airfield/Gravity Stabilizer: Single-use airstrike\n• Passenger: Spy + -WithSpriteTurret: + WithIdleOverlay@SPINNER: + Sequence: spinner + Offset: 0,0,200 encyclopedia.reck.snip: Inherits: ^ReckonerVariantPreview @@ -1653,180 +1736,174 @@ encyclopedia.reck.snip: VariantGroup: Allies Description: Reckoner loaded with a Sniper. Fires long-range sniper rounds. TooltipExtras: - Strengths: Strong vs Infantry - Weaknesses: Weak vs Vehicles - Attributes: Passenger: Sniper + Strengths: • Strong vs Infantry, Light Armor, Heavy Armor + Weaknesses: • Weak vs Defenses, Buildings\n• Cannot attack Aircraft + Attributes: • Passenger: Sniper -encyclopedia.reck.enli: +encyclopedia.reck.enfo: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-enli + Sequence: turret-enfo Tooltip: - Name: Enlightened Reckoner + Name: Enforcer Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Nod - Description: Reckoner loaded with an Enlightened. Fires a powerful laser. + VariantGroup: Allies + Description: Reckoner loaded with an Enforcer. Fires double shotgun blasts. TooltipExtras: - Strengths: Strong vs Infantry, Vehicles - Attributes: Passenger: Enlightened + Strengths: • Strong vs Infantry, Light Armor + Weaknesses: • Weak vs Heavy Armor, Defenses\n• Cannot attack Aircraft + Attributes: • Passenger: Enforcer -encyclopedia.reck.rmbc: +encyclopedia.reck.ggi: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-rmbc + Sequence: turret-ggi Tooltip: - Name: Cyborg Elite Reckoner + Name: GGI Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Nod - Description: Reckoner loaded with a Cyborg Elite. Fires plasma bolts. + VariantGroup: Allies + Description: Reckoner loaded with a Guardian GI. Fires powerful anti-armor rounds. TooltipExtras: - Strengths: Strong vs Infantry, Vehicles - Attributes: Passenger: Cyborg Elite + Strengths: • Strong vs Aircraft, Heavy Armor + Weaknesses: • Weak vs Infantry + Attributes: • Passenger: Guardian GI -encyclopedia.reck.bh: +encyclopedia.reck.seal: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-bh + Sequence: turret-cmdo Tooltip: - Name: Black Hand Reckoner + Name: SEAL Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Nod - Description: Reckoner loaded with a Black Hand. Projects devastating flames. + VariantGroup: Allies + Description: Reckoner loaded with a Navy SEAL. Fires heavy anti-infantry rounds. TooltipExtras: - Strengths: Strong vs Infantry, Buildings - Weaknesses: Weak vs Vehicles - Attributes: Passenger: Black Hand + Strengths: • Strong vs Infantry + Weaknesses: • Cannot attack Aircraft, Vehicles, Buildings + Attributes: • Passenger: Navy SEAL -encyclopedia.reck.pdisc: +encyclopedia.reck.cryo: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-pdisc + Sequence: turret-cryo Tooltip: - Name: Plasma Disc Reckoner + Name: Cryo Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Scrin - Description: Reckoner loaded with a Disc Thrower. Fires plasma discs. + VariantGroup: Allies + Description: Reckoner loaded with a Cryo Trooper. Fires freezing blasts. TooltipExtras: - Strengths: Strong vs Infantry, Light Vehicles - Attributes: Passenger: Disc Thrower + Strengths: • Strong vs Infantry, Light Armor + Weaknesses: • Weak vs Buildings, Defenses\n• Cannot attack Aircraft + Attributes: • Slows enemy units and makes them take increased damage\n• Passenger: Cryo Trooper -encyclopedia.reck.shard: +encyclopedia.reck.tigr: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-shard + Sequence: turret-tigr Tooltip: - Name: Shard Reckoner + Name: Tiger Guard Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Scrin - Description: Reckoner loaded with a Shard Trooper. Fires Tiberium shards. + VariantGroup: Allies + Description: Reckoner loaded with a Tiger Guard. Fires long range missiles. TooltipExtras: - Strengths: Strong vs Infantry, Light Vehicles - Attributes: Passenger: Shard Trooper + Strengths: • Strong vs Aircraft, Heavy Armor + Weaknesses: • Weak vs Infantry + Attributes: • Passenger: Tiger Guard -encyclopedia.reck.enfo: +encyclopedia.reck.hopl: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-enfo + Sequence: turret-hopl Tooltip: - Name: Enforcer Reckoner + Name: Hoplite Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Soviet - Description: Reckoner loaded with an Enforcer. Fires shotgun blasts. + VariantGroup: Allies + Description: Reckoner loaded with a Hoplite. Fires a prism beam. TooltipExtras: - Strengths: Strong vs Infantry at close range - Attributes: Passenger: Enforcer + Strengths: • Strong vs Infantry, Light Armor, Buildings + Weaknesses: • Weak vs Heavy Armor\n• Cannot attack Aircraft + Attributes: • Passenger: Hoplite -encyclopedia.reck.engi: +encyclopedia.reck.cmdo: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-engi + Sequence: turret-cmdo Tooltip: - Name: Engineer Reckoner + Name: Commando Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Utility - Description: Reckoner loaded with an Engineer. Can capture enemy structures. + VariantGroup: Allies + Description: Reckoner loaded with a Commando (Tanya/Boris/Commando). Fires rapid and powerful sniper bursts with high damage per shot. TooltipExtras: - Strengths: Captures structures - Attributes: Passenger: Engineer + Strengths: • Strong vs Infantry + Weaknesses: • Cannot attack Aircraft, Vehicles, Buildings + Attributes: • Passenger: Tanya, Boris, Commando -encyclopedia.reck.conf: +encyclopedia.reck.grenade: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-conf + Sequence: turret-frag Tooltip: - Name: Confessor Reckoner + Name: Grenade Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Nod - Description: Reckoner loaded with a Confessor. Fires hallucinogenic rounds. + VariantGroup: Other Factions + Description: Reckoner loaded with a Grenadier. Launches fragmentation grenades. TooltipExtras: - Strengths: Strong vs Infantry - Attributes: Passenger: Confessor + Strengths: • Strong vs Buildings, Defenses, Light Armor + Weaknesses: • Weak vs Heavy Armor\n• Cannot attack Aircraft + Attributes: • Passenger: Grenadier -encyclopedia.reck.reap: +encyclopedia.reck.flame: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-reap + Sequence: turret-flame Tooltip: - Name: Reaper Reckoner + Name: Flamethrower Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Nod - Description: Reckoner loaded with a Reaper. Fires Tiberium-infused rounds. + VariantGroup: Other Factions + Description: Reckoner loaded with a Flamethrower. Projects devastating flames. TooltipExtras: - Strengths: Strong vs Infantry - Attributes: Passenger: Reaper + Strengths: • Strong vs Infantry, Light Armor, Buildings + Weaknesses: • Weak vs Heavy Armor\n• Cannot attack Aircraft + Attributes: • Passenger: Flamethrower -encyclopedia.reck.stlk: +encyclopedia.reck.chem: Inherits: ^ReckonerVariantPreview - RenderSprites: - Image: reck - Palette: playerscrin WithSpriteTurret: - Sequence: turret-stlk + Sequence: turret-chem Tooltip: - Name: Stalker Reckoner + Name: Chemical Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Scrin - Description: Reckoner loaded with a Stalker. Fires plasma bolts. + VariantGroup: Other Factions + Description: Reckoner loaded with a Toxin Soldier. Sprays toxic chemicals. TooltipExtras: - Strengths: Strong vs Infantry, Light Vehicles - Attributes: Passenger: Stalker (Scrin) + Strengths: • Strong vs Infantry, Light Armor, Buildings + Weaknesses: • Weak vs Heavy Armor\n• Cannot attack Aircraft + Attributes: • Passenger: Chem Warrior -encyclopedia.reck.disin: +encyclopedia.reck.rad: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-disin - Tooltip: - Name: Disintegrator Reckoner - EncyclopediaExtras: - VariantOf: RECK - VariantGroup: Scrin - Description: Reckoner loaded with a Disintegrator. Fires disintegration beams. - TooltipExtras: - Strengths: Strong vs Vehicles, Buildings - Attributes: Passenger: Disintegrator (Scrin) - -encyclopedia.reck.med: - Inherits: ^ReckonerVariantPreview + Sequence: turret-rad Tooltip: - Name: Medical Reckoner + Name: Desolator Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Utility - Description: Reckoner loaded with a Medic. Heals nearby friendly infantry. + VariantGroup: Other Factions + Description: Reckoner loaded with a Desolator. Fires a radiation beam. TooltipExtras: - Strengths: Heals infantry - Weaknesses: Unarmed - Attributes: Passenger: Medic + Strengths: • Strong vs Infantry, Light Armor + Weaknesses: • Weak vs Heavy Armor, Defenses, Buildings\n• Cannot attack Aircraft + Attributes: • Passenger: Desolator, Rad Trooper encyclopedia.reck.ivan: Inherits: ^ReckonerVariantPreview @@ -1834,25 +1911,16 @@ encyclopedia.reck.ivan: Name: Explosive Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Soviet + VariantGroup: Other Factions Description: Reckoner loaded with a Crazy Ivan. Self-destructs on contact with the target, dealing massive damage. TooltipExtras: - Strengths: Strong vs Everything - Weaknesses: Self-destructs on attack - Attributes: Passenger: Crazy Ivan - -encyclopedia.reck.spy: - Inherits: ^ReckonerVariantPreview - Tooltip: - Name: Spy Reckoner - EncyclopediaExtras: - VariantOf: RECK - VariantGroup: Allies - Description: Reckoner loaded with a Spy. Can infiltrate enemy structures and detects cloaked units. - TooltipExtras: - Strengths: Infiltrates structures, detects cloaked - Weaknesses: Cannot deal damage - Attributes: Passenger: Spy + Strengths: • Strong vs Everything + Weaknesses: • Self-destructs + Attributes: • Passengers: Crazy Ivan, Burster, Terror Dog + -WithSpriteTurret: + WithIdleOverlay@IVAN: + Sequence: nuke + Offset: 0,0,40 encyclopedia.reck.tes: Inherits: ^ReckonerVariantPreview @@ -1860,12 +1928,15 @@ encyclopedia.reck.tes: Name: Tesla Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Soviet + VariantGroup: Other Factions Description: Reckoner loaded with a Shock Trooper or Tesla Trooper. Fires a powerful tesla bolt. TooltipExtras: - Strengths: Strong vs Infantry, Vehicles - Weaknesses: Cannot attack Aircraft - Attributes: Passenger: Shock Trooper, Tesla Trooper + Strengths: • Strong vs Infantry, Heavy Armor, Light Armor + Weaknesses: • Cannot attack Aircraft + Attributes: • Passenger: Shock Trooper, Tesla Trooper + -WithSpriteTurret: + WithIdleOverlay@TESLA: + Sequence: tesla encyclopedia.reck.psy: Inherits: ^ReckonerVariantPreview @@ -1873,113 +1944,155 @@ encyclopedia.reck.psy: Name: Psychic Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Special - Description: Reckoner loaded with a Yuri Prime (Yuri) or Mastermind (Scrin). Can mind control enemy units. Control is lost if passenger exits. + VariantGroup: Other Factions + Description: Reckoner loaded with Yuri or a Mastermind. Can mind control enemy units. Control is lost if passenger exits. TooltipExtras: - Strengths: Mind controls enemies - Weaknesses: Cannot deal damage, control lost on exit - Attributes: Passenger: Yuri Prime (Yuri), Mastermind (Scrin) + Strengths: • Strong vs Infantry, Vehicles + Weaknesses: • Cannot deal damage\n• Cannot attack Aircraft + Attributes: • Can mind control enemy units\n• Control lost if passenger exits\n• Passenger: Yuri, Mastermind + -WithSpriteTurret: + WithIdleOverlay@PSYCHIC: + Sequence: psy + Palette: scrin encyclopedia.reck.cmsr: Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-mg Tooltip: Name: Commissar Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Soviet - Description: Reckoner loaded with a Commissar. Inspires nearby friendly units, boosting their combat effectiveness. + VariantGroup: Other Factions + Description: Reckoner loaded with a Commissar. Inspires nearby friendly infantry, boosting their combat effectiveness. TooltipExtras: Strengths: Inspires nearby allies Weaknesses: Unarmed - Attributes: Passenger: Commissar + Attributes: • Passenger: Commissar + WithPreviewDecoration@CMSR: + Image: pips + Sequence: pip-cmsr + Position: TopLeft -encyclopedia.reck.shad: +encyclopedia.reck.laser: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-cmdo + Sequence: turret-laser Tooltip: - Name: Shadow Reckoner + Name: Laser Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Nod - Description: Reckoner loaded with a Shadow. Fires a sniper rifle effective against infantry. + VariantGroup: Other Factions + Description: Reckoner loaded with an Acolyte or Templar. Fires a powerful laser beam. TooltipExtras: - Strengths: Strong vs Infantry - Weaknesses: Weak vs Vehicles - Attributes: Passenger: Shadow + Strengths: • Strong vs Infantry, Light Armor + Weaknesses: • Weak vs Heavy Armor, Defenses, Buildings\n• Cannot attack Aircraft + Attributes: • Passenger: Acolyte, Templar -encyclopedia.reck.hack: +encyclopedia.reck.enli: Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-enli Tooltip: - Name: Hacker Reckoner + Name: Enlightened Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Nod - Description: Reckoner loaded with a Hacker. Can mind control enemy defenses and buildings. Control is lost if passenger exits. + VariantGroup: Other Factions + Description: Reckoner loaded with an Enlightened. Fires a powerful particle beam. TooltipExtras: - Strengths: Strong vs Defenses, Buildings - Weaknesses: Cannot deal direct damage, control lost on exit - Attributes: Passenger: Hacker + Strengths: • Strong vs Heavy Armor, Light Armor + Weaknesses: • Weak vs Infantry\n• Cannot attack Aircraft + Attributes: • Passenger: Enlightened -encyclopedia.reck.mortchem: +encyclopedia.reck.rmbc: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-mort + Sequence: turret-rmbc Tooltip: - Name: Chemical Mortar Reckoner + Name: Cyborg Elite Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Nod - Description: Reckoner loaded with a Chemical Mortar Infantry. Fires chemical mortar rounds that leave toxic residue. + VariantGroup: Other Factions + Description: Reckoner loaded with a Cyborg Elite. Fires a plasma cannon. TooltipExtras: - Strengths: Strong vs Infantry, area denial - Weaknesses: Minimum range - Attributes: Passenger: Chemical Mortar + Strengths: • Strong vs Heavy Armor, Light Armor, Infantry + Weaknesses: • Cannot attack Aircraft -encyclopedia.reck.mortcryo: +encyclopedia.reck.bh: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-mort + Sequence: turret-bh Tooltip: - Name: Cryo Mortar Reckoner + Name: Black Hand Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Allies - Description: Reckoner loaded with a Cryo Mortar Infantry. Fires cryo mortar rounds that slow and freeze enemies. + VariantGroup: Other Factions + Description: Reckoner loaded with a Black Hand. Projects devastating anti-vehicle flames. TooltipExtras: - Strengths: Slows and freezes enemies - Weaknesses: Minimum range - Attributes: Passenger: Cryo Mortar + Strengths: • Strong vs Heavy Armor, Light Armor + Weaknesses: • Weak vs Buildings, Defenses\n• Cannot attack Aircraft + Attributes: • Passenger: Black Hand -encyclopedia.reck.mortsonic: +encyclopedia.reck.conf: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-mort + Sequence: turret-conf Tooltip: - Name: Sonic Mortar Reckoner + Name: Confessor Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: GDI - Description: Reckoner loaded with a Sonic Mortar Infantry. Fires sonic mortar rounds that deal splash damage. + VariantGroup: Other Factions + Description: Reckoner loaded with a Confessor. Fires hallucinogenic rounds. TooltipExtras: - Strengths: Strong vs Infantry, Buildings - Weaknesses: Minimum range - Attributes: Passenger: Sonic Mortar + Strengths: • Strong vs Infantry, Light Armor + Weaknesses: • Weak vs Heavy Armor, Defenses, Buildings\n• Cannot attack Aircraft + Attributes: • Explosion causes units to attack indiscriminately\n• Passenger: Confessor -encyclopedia.reck.ggi: +encyclopedia.reck.reap: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-ggi + Sequence: turret-reap Tooltip: - Name: GGI Reckoner + Name: Reaper Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Allies - Description: Reckoner loaded with a Guardian GI. Fires powerful anti-armor rounds. + VariantGroup: Other Factions + Description: Reckoner loaded with a Cyborg Reaper. Fires anti-personnel missiles. TooltipExtras: - Strengths: Strong vs Vehicles, Heavy Armor - Weaknesses: Weak vs Infantry - Attributes: Passenger: Guardian GI + Strengths: • Strong vs Infantry, Light Armor, Buildings + Weaknesses: • Weak vs Heavy Armor + Attributes: • Passenger: Reaper + +encyclopedia.reck.shad: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-mg + Tooltip: + Name: Shadow Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Other Factions + Description: Reckoner loaded with a Shadow Operative. Armed with a heavy machinegun and able to cloak. + TooltipExtras: + Strengths: • Strong vs Infantry, Light Armor + Weaknesses: • Weak vs Heavy Armor, Defenses\n• Cannot attack Aircraft + Attributes: • Passenger: Shadow Operative + +encyclopedia.reck.hack: + Inherits: ^ReckonerVariantPreview + Tooltip: + Name: Hacker Reckoner + EncyclopediaExtras: + VariantOf: RECK + VariantGroup: Other Factions + Description: Reckoner loaded with a Hacker. Can hack enemy defenses and buildings. Control is lost if passenger exits. + TooltipExtras: + Strengths: • Strong vs Defenses, Buildings + Weaknesses: • Cannot deal damage\n• Control lost if passenger exits + Attributes: • Passenger: Hacker + -WithSpriteTurret: + WithIdleOverlay@HACK: + Sequence: hack encyclopedia.reck.ztrp: Inherits: ^ReckonerVariantPreview @@ -1989,11 +2102,12 @@ encyclopedia.reck.ztrp: Name: Zone Trooper Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: GDI + VariantGroup: Other Factions Description: Reckoner loaded with a Zone Trooper. Fires powerful railgun shots. TooltipExtras: - Strengths: Strong vs Vehicles, Heavy Armor - Attributes: Passenger: Zone Trooper + Strengths: • Strong vs Heavy Armor, Light Armor, Defenses + Weaknesses: • Weak vs Infantry, Buildings\n• Cannot attack Aircraft + Attributes: • Passenger: Zone Trooper encyclopedia.reck.zdef: Inherits: ^ReckonerVariantPreview @@ -2003,11 +2117,12 @@ encyclopedia.reck.zdef: Name: Zone Defender Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: GDI - Description: Reckoner loaded with a Zone Defender. Fires anti-air missiles. + VariantGroup: Other Factions + Description: Reckoner loaded with a Zone Defender. Fires ion blasts. TooltipExtras: - Strengths: Strong vs Aircraft - Attributes: Passenger: Zone Defender + Strengths: • Strong vs Heavy Armor, Light Armor, Defenses + Weaknesses: • Weak vs Infantry, Buildings\n• Cannot attack Aircraft + Attributes: • Passenger: Zone Defender encyclopedia.reck.zrai: Inherits: ^ReckonerVariantPreview @@ -2017,159 +2132,135 @@ encyclopedia.reck.zrai: Name: Zone Raider Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: GDI - Description: Reckoner loaded with a Zone Raider. Fires sonic blasts. + VariantGroup: Other Factions + Description: Reckoner loaded with a Zone Raider. Fires sonic grenades. TooltipExtras: - Strengths: Strong vs Infantry, Structures - Attributes: Passenger: Zone Raider + Strengths: • Strong vs Light Armor, Buildings + Weaknesses: • Cannot attack Aircraft + Attributes: • Passenger: Zone Raider -encyclopedia.reck.tigr: +encyclopedia.reck.disin: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-tigr + Sequence: turret-disin Tooltip: - Name: Tiger Guard Reckoner + Name: Disintegrator Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Soviet - Description: Reckoner loaded with a Tiger Guard. Fires dual machine guns. + VariantGroup: Other Factions + Description: Reckoner loaded with a Disintegrator. Fires disintegration beams. TooltipExtras: - Strengths: Strong vs Infantry - Attributes: Passenger: Tiger Guard + Strengths: • Strong vs Aircraft, Heavy Armor + Weaknesses: • Weak vs Infantry + Attributes: • Passenger: Disintegrator (Scrin) -encyclopedia.reck.hopl: +encyclopedia.reck.shard: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-hopl + Sequence: turret-shard Tooltip: - Name: Hoplite Reckoner + Name: Shard Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: GDI - Description: Reckoner loaded with a Hoplite. Fires EMP rounds. + VariantGroup: Other Factions + Description: Reckoner loaded with a Ravager or Eviscerator. Fires Tiberium shards. TooltipExtras: - Strengths: Disables vehicles - Attributes: Passenger: Hoplite + Strengths: • Strong vs Infantry, Light Armor + Weaknesses: • Weak vs Heavy Armor, Defenses, Buildings\n• Cannot attack Aircraft + Attributes: • Passenger: Ravager, Eviscerator -encyclopedia.reck.impl: +encyclopedia.reck.pdisc: Inherits: ^ReckonerVariantPreview WithSpriteTurret: - Sequence: turret-impl + Sequence: turret-pdisc Tooltip: - Name: Impaler Reckoner + Name: Plasma Disc Reckoner EncyclopediaExtras: VariantOf: RECK - VariantGroup: Scrin - Description: Reckoner loaded with an Impaler. Fires spikes. - TooltipExtras: - Strengths: Strong vs Infantry - Attributes: Passenger: Impaler - -# Battle Fortress Variants -^BattleFortressVariantPreview: - Inherits: ^EffectPreview - RenderSprites: - Image: batf - WithFacingSpriteBody: - Encyclopedia: - Scale: 1.5 - -encyclopedia.batf.spy: - Inherits: ^BattleFortressVariantPreview - Tooltip: - Name: Spy Battle Fortress - EncyclopediaExtras: - VariantOf: BATF - VariantGroup: Utility - Description: Battle Fortress loaded with Spies, Thieves, or Saboteurs. Gains the ability to detect cloaked and disguised units within a 6 cell radius. - TooltipExtras: - Strengths: Detects cloaked/disguised units - Attributes: Passengers: Spy, Thief, Saboteur - -encyclopedia.batf.medic: - Inherits: ^BattleFortressVariantPreview - Tooltip: - Name: Medic Battle Fortress - EncyclopediaExtras: - VariantOf: BATF - VariantGroup: Utility - Description: Battle Fortress loaded with Medics. Provides a healing aura that restores health to nearby allied infantry. - TooltipExtras: - Strengths: Heals nearby infantry - Attributes: Passengers: Medic, Soviet Medic - -encyclopedia.batf.repair: - Inherits: ^BattleFortressVariantPreview - Tooltip: - Name: Repair Battle Fortress - EncyclopediaExtras: - VariantOf: BATF - VariantGroup: Utility - Description: Battle Fortress loaded with Engineers or Mechanics. Slowly repairs itself when damaged. + VariantGroup: Other Factions + Description: Fires plasma discs. TooltipExtras: - Strengths: Self-repairs when damaged - Attributes: Passengers: Engineer, Mechanic, Artificer + Strengths: • Strong vs Buildings, Defenses, Heavy Armor, Light Armor + Weaknesses: • Weak vs Infantry\n• Cannot attack Aircraft + Attributes: • Passenger: Intruder, Marauder -encyclopedia.batf.aa: - Inherits: ^BattleFortressVariantPreview +encyclopedia.reck.impl: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-impl Tooltip: - Name: Anti-Air Battle Fortress + Name: Impaler Reckoner EncyclopediaExtras: - VariantOf: BATF - VariantGroup: Combat - Description: Battle Fortress loaded with GGI or Rocket Soldiers. Gains the ability to attack aircraft using their anti-air missiles. + VariantOf: RECK + VariantGroup: Other Factions + Description: Reckoner loaded with an Impaler. Fires long range impaling projectiles. TooltipExtras: - Strengths: Can attack Aircraft - Attributes: Passengers: GGI, Rocket Soldier, Flak Trooper + Strengths: • Strong vs Light Armor, Infantry + Weaknesses: • Weak vs Heavy Armor, Defenses\n• Cannot attack Aircraft + Attributes: • Passenger: Impaler -encyclopedia.batf.flame: - Inherits: ^BattleFortressVariantPreview +encyclopedia.reck.stlk: + Inherits: ^ReckonerVariantPreview + RenderSprites: + Image: ifv + WithSpriteTurret: + Sequence: turret-stlk + Palette: playerscrin Tooltip: - Name: Flame Battle Fortress + Name: Stalker Reckoner EncyclopediaExtras: - VariantOf: BATF - VariantGroup: Combat - Description: Battle Fortress loaded with Black Hand troopers. Infantry fire their flamethrowers from the ports, devastating infantry and light vehicles. + VariantOf: RECK + VariantGroup: Other Factions + Description: Reckoner loaded with a Stalker. Fires anti-infantry plasma darts and can cloak. TooltipExtras: - Strengths: Strong vs Infantry, Light Vehicles, Buildings - Attributes: Passengers: Black Hand + Strengths: • Strong vs Light Armor, Infantry + Weaknesses: • Weak vs Heavy Armor, Defenses\n• Cannot attack Aircraft + Attributes: • Passenger: Stalker -encyclopedia.batf.cryo: - Inherits: ^BattleFortressVariantPreview +encyclopedia.reck.mortchem: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-mort Tooltip: - Name: Cryo Battle Fortress + Name: Chemical Mortar Reckoner EncyclopediaExtras: - VariantOf: BATF - VariantGroup: Combat - Description: Battle Fortress loaded with Cryo Troopers. Infantry fire their freeze rays from the ports, slowing and freezing enemies. + VariantOf: RECK + VariantGroup: Other Factions + Description: Reckoner loaded with a Chemical Mortar Infantry. Fires chemical mortar rounds that leave toxic residue. TooltipExtras: - Strengths: Slows and freezes enemies - Attributes: Passengers: Cryo Trooper + Strengths: • Strong vs Infantry, Buildings + Weaknesses: • Weak vs Heavy Armor\n• Cannot attack Aircraft\n• Has difficulty hitting moving targets + Attributes: • Passenger: Chemical Mortar -encyclopedia.batf.cmdo: - Inherits: ^BattleFortressVariantPreview +encyclopedia.reck.mortcryo: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-mort Tooltip: - Name: Commando Battle Fortress + Name: Cryo Mortar Reckoner EncyclopediaExtras: - VariantOf: BATF - VariantGroup: Special - Description: Battle Fortress loaded with Commandos (Tanya, Boris, Commando, Yuri Prime, or Mastermind). Becomes immune to mind control. + VariantOf: RECK + VariantGroup: Other Factions + Description: Reckoner loaded with a Cryo Mortar Infantry. Fires cryo mortar rounds that slow and freeze enemies. TooltipExtras: - Strengths: Immune to mind control - Attributes: Passengers: Tanya, Boris, Commando, Yuri Prime, Mastermind + Strengths: • Strong vs Infantry, Buildings + Weaknesses: • Weak vs Heavy Armor\n• Cannot attack Aircraft\n• Has difficulty hitting moving targets + Attributes: • Passenger: Cryo Mortar -encyclopedia.batf.seal: - Inherits: ^BattleFortressVariantPreview +encyclopedia.reck.mortsonic: + Inherits: ^ReckonerVariantPreview + WithSpriteTurret: + Sequence: turret-mort Tooltip: - Name: SEAL Battle Fortress + Name: Sonic Mortar Reckoner EncyclopediaExtras: - VariantOf: BATF - VariantGroup: Combat - Description: Battle Fortress loaded with Navy SEALs. SEALs fire their weapons from the ports, but unlike Commandos, do not provide mind control immunity. + VariantOf: RECK + VariantGroup: Other Factions + Description: Reckoner loaded with a Sonic Mortar Infantry. Fires sonic mortar rounds that cause concussion. TooltipExtras: - Strengths: Strong vs Infantry - Weaknesses: No mind control immunity - Attributes: Passengers: Navy SEAL + Strengths: • Strong vs Infantry, Buildings + Weaknesses: • Weak vs Heavy Armor\n• Cannot attack Aircraft\n• Has difficulty hitting moving targets + Attributes: • Passenger: Sonic Mortar encyclopedia.buffs.anathema: Inherits: ^VehicleEffectPreview diff --git a/mods/ca/rules/scrin.yaml b/mods/ca/rules/scrin.yaml index f009d50052..0489c1ec57 100644 --- a/mods/ca/rules/scrin.yaml +++ b/mods/ca/rules/scrin.yaml @@ -5978,8 +5978,6 @@ VSPK: TooltipExtras: Attributes: • Corrupts resources\n• Damages nearby enemies over time\n• Direct damage is reflected back to the attacker Description: Gradually transforms nearby resources into Black Tiberium, which is devoid of most of its useful properties. - Encyclopedia: - Category: Scrin/Support Powers RevealsShroud: MinRange: 6c0 Range: 8c0 @@ -6037,8 +6035,6 @@ ISPK: TooltipExtras: Attributes: • Enriches Tiberium trees\n• Empowers units with Resource Conversion upgrade Description: Enriches nearby Tiberium trees causing them to seed Tiberium more quickly. - Encyclopedia: - Category: Scrin/Support Powers RevealsShroud: MinRange: 6c0 Range: 8c0 diff --git a/mods/ca/sequences/powers.yaml b/mods/ca/sequences/powers.yaml deleted file mode 100644 index 06655bfa1a..0000000000 --- a/mods/ca/sequences/powers.yaml +++ /dev/null @@ -1,275 +0,0 @@ -# Support Power Icons for Encyclopedia -# Uses 'icon' sequence name (like upgrades) - -# Allies -chrono.power: - icon: - Filename: warpicon.shp - -forceshield.power: - icon: - Filename: forceshieldicon.shp - -gps.power: - icon: - Filename: gpssicon.shp - -timewarp.power: - icon: - Filename: timewarpicon.shp - -tempinc.power: - icon: - Filename: tempincicon.shp - -veilofwar.power: - icon: - Filename: veilofwaricnh.shp - -cmines.power: - icon: - Filename: cmineicon.shp - -strafe.power: - icon: - Filename: strafeicon.shp - -cryostorm.power: - icon: - Filename: cryostormicon.shp - -heliosbomb.power: - icon: - Filename: heliosicon.shp - -bsky.power: - icon: - Filename: bskyicon.shp - -# Soviets -invuln.power: - icon: - Filename: infxicon.shp - -storm.power: - icon: - Filename: stormicon.shp - -abomb.power: - icon: - Filename: atomicon.shp - -spyplane.power: - icon: - Filename: smigicon.shp - -paratroopers.power: - icon: - Filename: pinficon.shp - -stormtroopers.power: - icon: - Filename: stroopicon.shp - -parabombs.power: - icon: - Filename: pbmbicon.shp - -carpetbomb.power: - icon: - Filename: cbombicon.shp - -abombair.power: - icon: - Filename: abombicon.shp - -mutabomb.power: - icon: - Filename: gmutationicon.shp - -chaosbombs.power: - icon: - Filename: chaosbombicon.shp - -atomicammo.power: - icon: - Filename: atomicammoicon.shp - -heroes.power: - icon: - Filename: heroesicon.shp - -tankdrop.power: - icon: - Filename: tankdropicon.shp - -killzone.power: - icon: - Filename: killzoneicon.shp - -# GDI -ioncannon.power: - icon: - Filename: ionicon.shp - -uavicon.power: - icon: - Filename: uavicon.shp - -xodrop.power: - icon: - Filename: xodropicon.shp - -droppods.power: - icon: - Filename: droppodicnh.shp - -orcaca.power: - icon: - Filename: orcacarein.shp - -airsupport.power: - icon: - Filename: airsupicon.shp - -nrepair.power: - icon: - Filename: nrepairicon.shp - -fstorm.power: - icon: - Filename: fstormicnh.shp - -arscan.power: - icon: - Filename: arscanicnh.shp - -nshield.power: - icon: - Filename: nshieldicon.shp - -empmissile.power: - icon: - Filename: empicon.shp - -surgicalstrike.power: - icon: - Filename: surgicalsicnh.shp - -# Nod -clustermissile.power: - icon: - Filename: clustericnh.shp - -chemmissile.power: - icon: - Filename: cmissicnh.shp - -invis.power: - icon: - Filename: invisicnh.shp - -airdrop.power: - icon: - Filename: airdropicon.shp - -hacksat.power: - icon: - Filename: hacksaticon.shp - -infbomb.power: - icon: - Filename: b2bicnh.shp - -chack.power: - icon: - Filename: chackicon.shp - -shadteam.power: - icon: - Filename: shadteamicon.shp - -frenzy.power: - icon: - Filename: frenzyicon.shp - -techhack.power: - icon: - Filename: techhackicon.shp - -assassinsquad.power: - icon: - Filename: assaicnh.shp - -hackercell.power: - icon: - Filename: hackercellicon.shp - -confessorcabal.power: - icon: - Filename: confcabalicnh.shp - -substrike.power: - icon: - Filename: substrikeicon.shp - -# Scrin -riftpower.power: - icon: - Filename: riftpowericon.shp - -spresspower.power: - icon: - Filename: spresspowericon.shp - -gateway.power: - icon: - Filename: gatewayicon.shp - -anathema.power: - icon: - Filename: anathemaicon.shp - -owrath.power: - icon: - Filename: owrathicon.shp - -ionsurgepower.power: - icon: - Filename: ionsurgeicon.shp - -stormspikepower.power: - icon: - Filename: stormspikeicon.shp - -buzzpower.power: - icon: - Filename: buzzicon.shp - -ichorpower.power: - icon: - Filename: ichorpowericon.shp - -grclpower.power: - icon: - Filename: grclpowericon.shp - -fleetrecall.power: - icon: - Filename: fleetrecallicon.shp - -rescanpower.power: - icon: - Filename: rescanpowericon.shp - -ichorspike.power: - icon: - Filename: ispkicon.shp - -colonyspike.power: - icon: - Filename: cspkicon.shp - -voidspike.power: - icon: - Filename: vspkicon.shp From 528291ac6a9068250e16545357df3b7295679939 Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Thu, 1 Jan 2026 17:34:59 +0000 Subject: [PATCH 19/28] Multipolarity fixes. --- .../ca45-multipolarity/multipolarity.lua | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/mods/ca/missions/main-campaign/ca45-multipolarity/multipolarity.lua b/mods/ca/missions/main-campaign/ca45-multipolarity/multipolarity.lua index c28de04aab..abbfee094c 100644 --- a/mods/ca/missions/main-campaign/ca45-multipolarity/multipolarity.lua +++ b/mods/ca/missions/main-campaign/ca45-multipolarity/multipolarity.lua @@ -274,6 +274,22 @@ WorldLoaded = function() DoCommandoDrop() end + Trigger.OnAllKilledOrCaptured({ SovietFactory, SovietBarracks }, function() + SovietProductionDestroyed = true + if HawthorneClaimedSovietBase then + Squads.Main.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 20, Max = 40 }) + Squads.Secondary.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 20, Max = 40 }) + end + end) + + Trigger.OnAllKilledOrCaptured({ NodAirstrip, NodHand }, function() + NodProductionDestroyed = true + if HawthorneClaimedNodBase then + Squads.Main.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 20, Max = 40 }) + Squads.Secondary.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 20, Max = 40 }) + end + end) + AfterWorldLoaded() end @@ -528,13 +544,10 @@ HawthorneClaimSovietBase = function() AutoRepairBuilding(sam, GDI) AutoRebuildBuilding(sam, GDI, 10) - Squads.Main.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 15, Max = 30 }) - Squads.Secondary.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 15, Max = 30 }) - - Trigger.OnAllKilledOrCaptured({ SovietFactory, SovietBarracks }, function() - Squads.Main.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 20, Max = 40 }) - Squads.Secondary.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 20, Max = 40 }) - end) + if not SovietProductionDestroyed then + Squads.Main.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 15, Max = 30 }) + Squads.Secondary.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 15, Max = 30 }) + end end HawthorneClaimNodBase = function() @@ -586,13 +599,10 @@ HawthorneClaimNodBase = function() AutoRepairBuilding(nsam, GDI) AutoRebuildBuilding(nsam, GDI, 10) - Squads.Main.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 15, Max = 30 }) - Squads.Secondary.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 15, Max = 30 }) - - Trigger.OnAllKilledOrCaptured({ NodAirstrip, NodHand }, function() - Squads.Main.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 20, Max = 40 }) - Squads.Secondary.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 20, Max = 40 }) - end) + if not NodProductionDestroyed then + Squads.Main.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 15, Max = 30 }) + Squads.Secondary.AttackValuePerSecond = AdjustAttackValuesForDifficulty({ Min = 15, Max = 30 }) + end end DoDisruptorDrop = function() From 2eaf4f6407d3dc8664f841a7bdd045a4772c1921 Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Thu, 1 Jan 2026 20:03:25 +0000 Subject: [PATCH 20/28] Encyclopedia updates. --- OpenRA.Mods.CA/Traits/EncyclopediaExtras.cs | 4 + .../Widgets/Logic/EncyclopediaLogicCA.cs | 545 +++++++++--------- mods/ca/rules/scrin.yaml | 1 + mods/ca/rules/structures.yaml | 3 + mods/ca/rules/vehicles.yaml | 33 ++ 5 files changed, 299 insertions(+), 287 deletions(-) diff --git a/OpenRA.Mods.CA/Traits/EncyclopediaExtras.cs b/OpenRA.Mods.CA/Traits/EncyclopediaExtras.cs index 44a02ea73e..e537b9a7d4 100644 --- a/OpenRA.Mods.CA/Traits/EncyclopediaExtras.cs +++ b/OpenRA.Mods.CA/Traits/EncyclopediaExtras.cs @@ -15,6 +15,10 @@ namespace OpenRA.Mods.CA.Traits [Desc("To override encyclopedia preview.")] public class EncyclopediaExtrasInfo : TraitInfo { + [FluentReference] + [Desc("If set, will be used instead of the tooltip name in encyclopedia.")] + public readonly string Name = null; + [Desc("If set will override the preview with this actor.")] public readonly string RenderPreviewActor; diff --git a/OpenRA.Mods.CA/Widgets/Logic/EncyclopediaLogicCA.cs b/OpenRA.Mods.CA/Widgets/Logic/EncyclopediaLogicCA.cs index fdf5e99c52..ca3dc7d6be 100644 --- a/OpenRA.Mods.CA/Widgets/Logic/EncyclopediaLogicCA.cs +++ b/OpenRA.Mods.CA/Widgets/Logic/EncyclopediaLogicCA.cs @@ -639,128 +639,7 @@ void SelectActor(ActorInfo actor, string categoryPath = null) } } - var currentY = 0; - - if (productionContainer != null) - { - var currentX = 0; - var productionContainerHeight = 0; - const int IconWidth = 16; - const int LabelSpacing = 4; - const int GroupSpacing = 20; - - var costIcon = productionContainer.GetOrNull("COST_ICON"); - var timeIcon = productionContainer.GetOrNull("TIME_ICON"); - var notProducibleIcon = productionContainer.GetOrNull("NOT_PRODUCIBLE_ICON"); - var notProducibleLabel = productionContainer.GetOrNull("NOT_PRODUCIBLE"); - - if (costIcon != null) costIcon.Visible = false; - if (timeIcon != null) timeIcon.Visible = false; - if (productionCost != null) productionCost.Visible = false; - if (productionTime != null) productionTime.Visible = false; - if (armorTypeIcon != null) armorTypeIcon.Visible = false; - if (armorTypeLabel != null) armorTypeLabel.Visible = false; - if (productionPowerIcon != null) productionPowerIcon.Visible = false; - if (productionPower != null) productionPower.Visible = false; - if (notProducibleIcon != null) notProducibleIcon.Visible = false; - if (notProducibleLabel != null) notProducibleLabel.Visible = false; - - if (bi != null && !selectedInfo.HideBuildable) - { - var cost = actor.TraitInfoOrDefault()?.Cost ?? 0; - if (cost > 0 && productionCost != null && costIcon != null) - { - var costText = cost.ToString(NumberFormatInfo.CurrentInfo); - productionCost.Text = costText; - costIcon.Bounds.X = currentX; - productionCost.Bounds.X = currentX + IconWidth + LabelSpacing; - var costWidth = Game.Renderer.Fonts[productionCost.Font].Measure(costText).X; - currentX += IconWidth + LabelSpacing + costWidth + GroupSpacing; - - costIcon.Visible = true; - productionCost.Visible = true; - productionContainerHeight = descriptionFont.Measure(costText).Y; - } - - var time = BuildTime(selectedActor, selectedInfo.BuildableQueue); - if (time > 0 && productionTime != null && timeIcon != null) - { - var timeText = WidgetUtils.FormatTime(time, world.Timestep); - productionTime.Text = timeText; - timeIcon.Bounds.X = currentX; - productionTime.Bounds.X = currentX + IconWidth + LabelSpacing; - var timeWidth = Game.Renderer.Fonts[productionTime.Font].Measure(timeText).X; - currentX += IconWidth + LabelSpacing + timeWidth + GroupSpacing; - - timeIcon.Visible = true; - productionTime.Visible = true; - productionContainerHeight = Math.Max(productionContainerHeight, descriptionFont.Measure(timeText).Y); - } - } - else - { - if (encyclopediaExtrasInfo != null && encyclopediaExtrasInfo.HideNotProducible) - { - productionContainer.Visible = false; - } - else - { - notProducibleIcon.Visible = true; - notProducibleIcon.Bounds.X = currentX; - notProducibleLabel.Visible = true; - notProducibleLabel.Bounds.X = currentX + IconWidth + LabelSpacing; - var notProducibleLabelWidth = Game.Renderer.Fonts[notProducibleLabel.Font].Measure(notProducibleLabel.Text).X; - currentX += IconWidth + LabelSpacing + notProducibleLabelWidth + GroupSpacing; - productionContainerHeight = descriptionFont.Measure(notProducibleLabel.Text).Y; - } - } - - if (armorTypeLabel != null && armorTypeIcon != null) - { - var armor = actor.TraitInfos().FirstOrDefault(); - if (armor != null && !string.IsNullOrEmpty(armor.Type)) - { - SelectionTooltipLogic.GetArmorTypeLabel(armorTypeLabel, actor); - var hasArmorType = !string.IsNullOrEmpty(armorTypeLabel.Text); - if (hasArmorType) - { - armorTypeIcon.Bounds.X = currentX; - armorTypeLabel.Bounds.X = currentX + IconWidth + LabelSpacing; - var armorWidth = Game.Renderer.Fonts[armorTypeLabel.Font].Measure(armorTypeLabel.Text).X; - currentX += IconWidth + LabelSpacing + armorWidth + GroupSpacing; - - armorTypeIcon.Visible = true; - armorTypeLabel.Visible = true; - productionContainerHeight = Math.Max(productionContainerHeight, descriptionFont.Measure(armorTypeLabel.Text).Y); - } - } - } - - var power = actor.TraitInfos().Where(i => i.EnabledByDefault).Sum(i => i.Amount); - if (power != 0 && productionPower != null && productionPowerIcon != null) - { - var powerText = power.ToString(NumberFormatInfo.CurrentInfo); - productionPower.Text = powerText; - productionPowerIcon.Bounds.X = currentX; - productionPower.Bounds.X = currentX + IconWidth + LabelSpacing; - var powerWidth = Game.Renderer.Fonts[productionPower.Font].Measure(powerText).X; - currentX += IconWidth + LabelSpacing + powerWidth + GroupSpacing; - - productionPowerIcon.Visible = true; - productionPower.Visible = true; - productionContainerHeight = Math.Max(productionContainerHeight, descriptionFont.Measure(powerText).Y); - } - - // Only show the production container if it has any visible content - var hasVisibleContent = (costIcon?.Visible == true) || - (timeIcon?.Visible == true) || - (armorTypeIcon?.Visible == true) || - (productionPowerIcon?.Visible == true) || - (notProducibleIcon?.Visible == true); - - productionContainer.Visible = hasVisibleContent; - currentY = productionContainerHeight + 10; - } + var currentY = SetupProductionContainer(actor); FactionInfo subfaction = null; var subfactionText = ""; @@ -841,125 +720,7 @@ void SelectActor(ActorInfo actor, string categoryPath = null) currentY += additionalInfoHeight + 8; } - var prerequisitesText = ""; - var descriptionText = ""; - - if (bi != null) - { - var prereqs = bi.Prerequisites - .Select(a => ActorName(modData.DefaultRules, a)) - .Where(s => !s.StartsWith('~') && !s.StartsWith('!')) - .ToList(); - - if (prereqs.Count != 0) - { - prerequisitesText = WidgetUtilsCA.WrapTextWithIndent( - FluentProvider.GetMessage(Requires, "prerequisites", prereqs.JoinWith(", ")), - descriptionLabel.Bounds.Width, - descriptionFont); - } - - if (!string.IsNullOrEmpty(bi.Description)) - { - descriptionText = WidgetUtilsCA.WrapTextWithIndent( - FluentProvider.GetMessage(bi.Description.Replace("\\n", "\n")), - descriptionLabel.Bounds.Width, - descriptionFont); - } - } - - var tooltipExtras = actor.TraitInfos().FirstOrDefault(info => info.IsStandard); - - if (string.IsNullOrEmpty(descriptionText)) - { - if (tooltipExtras != null && !string.IsNullOrEmpty(tooltipExtras.Description)) - { - descriptionText = WidgetUtilsCA.WrapTextWithIndent( - FluentProvider.GetMessage(tooltipExtras.Description.Replace("\\n", "\n")), - descriptionLabel.Bounds.Width, - descriptionFont); - } - else if (encyclopediaExtrasInfo != null && !string.IsNullOrEmpty(encyclopediaExtrasInfo.Description)) - { - descriptionText = WidgetUtilsCA.WrapTextWithIndent( - FluentProvider.GetMessage(encyclopediaExtrasInfo.Description.Replace("\\n", "\n")), - descriptionLabel.Bounds.Width, - descriptionFont); - } - } - - var prerequisitesHeight = string.IsNullOrEmpty(prerequisitesText) ? 0 : descriptionFont.Measure(prerequisitesText).Y; - prerequisitesLabel.GetText = () => prerequisitesText; - prerequisitesLabel.Bounds.Height = prerequisitesHeight; - prerequisitesLabel.Visible = !string.IsNullOrEmpty(prerequisitesText); - - var descriptionHeight = string.IsNullOrEmpty(descriptionText) ? 0 : descriptionFont.Measure(descriptionText).Y; - descriptionLabel.GetText = () => descriptionText; - descriptionLabel.Bounds.Height = descriptionHeight; - descriptionLabel.Visible = !string.IsNullOrEmpty(descriptionText); - - if (!string.IsNullOrEmpty(prerequisitesText)) - { - prerequisitesLabel.Bounds.Y = currentY; - currentY += prerequisitesHeight + 8; - } - - if (!string.IsNullOrEmpty(descriptionText)) - { - descriptionLabel.Bounds.Y = currentY; - currentY += descriptionHeight + 8; - } - - var strengthsText = ""; - var weaknessesText = ""; - var attributesText = ""; - - if (tooltipExtras != null) - { - strengthsText = WidgetUtilsCA.WrapTextWithIndent(tooltipExtras.Strengths.Replace("\\n", "\n"), strengthsLabel.Bounds.Width, descriptionFont, 6); - weaknessesText = WidgetUtilsCA.WrapTextWithIndent(tooltipExtras.Weaknesses.Replace("\\n", "\n"), weaknessesLabel.Bounds.Width, descriptionFont, 6); - attributesText = WidgetUtilsCA.WrapTextWithIndent(tooltipExtras.Attributes.Replace("\\n", "\n"), attributesLabel.Bounds.Width, descriptionFont, 6); - } - - if (!string.IsNullOrEmpty(strengthsText) && strengthsLabel != null) - { - SetupTextLabel(strengthsLabel, strengthsText, ref currentY, 0); - } - else if (strengthsLabel != null) - { - strengthsLabel.Visible = false; - } - - if (!string.IsNullOrEmpty(weaknessesText) && weaknessesLabel != null) - { - SetupTextLabel(weaknessesLabel, weaknessesText, ref currentY, 0); - } - else if (weaknessesLabel != null) - { - weaknessesLabel.Visible = false; - } - - if (!string.IsNullOrEmpty(attributesText) && attributesLabel != null) - { - SetupTextLabel(attributesLabel, attributesText, ref currentY, 8); - } - else if (attributesLabel != null) - { - attributesLabel.Visible = false; - } - - var encyclopediaText = ""; - if (selectedInfo != null && !string.IsNullOrEmpty(selectedInfo.Description)) - encyclopediaText = WidgetUtils.WrapText(FluentProvider.GetMessage(selectedInfo.Description), descriptionLabel.Bounds.Width, descriptionFont); - - if (!string.IsNullOrEmpty(encyclopediaText) && encyclopediaDescriptionLabel != null) - { - SetupTextLabel(encyclopediaDescriptionLabel, encyclopediaText, ref currentY, 0); - } - else if (encyclopediaDescriptionLabel != null) - { - encyclopediaDescriptionLabel.Visible = false; - } + currentY = SetupDescriptionSection(actor, currentY, showEncyclopediaDescription: true); actorDetailsContainer.Bounds.Height = currentY; @@ -988,8 +749,12 @@ void SetupVariantDropdown(ActorInfo actor) variantDropdown.OnMouseDown = _ => { + // Include the base actor along with variants + var allVariants = new List { actor }; + allVariants.AddRange(variants); + // Separate variants into grouped and ungrouped - var variantsWithGroups = variants + var variantsWithGroups = allVariants .Select(v => new { Actor = v, @@ -1012,12 +777,11 @@ ScrollItemWidget SetupItem(ActorInfo variantActor, ScrollItemWidget template) if (!hasAnyGroups) { - // No groups - use simple flat dropdown without headers - var flatList = variants.OrderBy(v => GetActorDisplayName(v)).ToList(); + // No groups - use simple flat dropdown without headers (preserve YAML order) var itemHeight = 25; - var totalHeight = Math.Min(flatList.Count * itemHeight, 300); + var totalHeight = Math.Min(allVariants.Count * itemHeight, 300); - variantDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", totalHeight, flatList, SetupItem); + variantDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", totalHeight, allVariants, SetupItem); } else { @@ -1028,7 +792,7 @@ ScrollItemWidget SetupItem(ActorInfo variantActor, ScrollItemWidget template) .OrderBy(g => GetVariantGroupSortOrder(g.Key)) .ToDictionary( g => g.Key, - g => g.OrderBy(v => GetActorDisplayName(v.Actor)).Select(v => v.Actor).AsEnumerable() + g => g.Select(v => v.Actor).AsEnumerable() ); // Add ungrouped variants first (with empty key, handled specially) @@ -1037,7 +801,7 @@ ScrollItemWidget SetupItem(ActorInfo variantActor, ScrollItemWidget template) { var orderedGrouped = new Dictionary> { - { "", ungrouped.OrderBy(v => GetActorDisplayName(v)) } + { "", ungrouped } }; foreach (var kvp in groupedVariants) orderedGrouped[kvp.Key] = kvp.Value; @@ -1088,66 +852,249 @@ void SelectVariant(ActorInfo variant) void UpdateVariantDescription(ActorInfo variant) { - var currentY = 0; + var currentY = SetupProductionContainer(variant); + + // Hide subfaction info for variants + if (subfactionLabel != null) + subfactionLabel.Visible = false; + if (subfactionFlagImage != null) + subfactionFlagImage.Visible = false; + if (additionalInfoLabel != null) + additionalInfoLabel.Visible = false; + + currentY = SetupDescriptionSection(variant, currentY, showEncyclopediaDescription: false); + + actorDetailsContainer.Bounds.Height = currentY; + descriptionPanel.Layout.AdjustChildren(); + descriptionPanel.ScrollToTop(); + } + + int SetupProductionContainer(ActorInfo actor) + { + if (productionContainer == null) + return 0; + + var currentX = 0; + var productionContainerHeight = 0; + const int IconWidth = 16; + const int LabelSpacing = 4; + const int GroupSpacing = 20; + + var costIcon = productionContainer.GetOrNull("COST_ICON"); + var timeIcon = productionContainer.GetOrNull("TIME_ICON"); + var notProducibleIcon = productionContainer.GetOrNull("NOT_PRODUCIBLE_ICON"); + var notProducibleLabel = productionContainer.GetOrNull("NOT_PRODUCIBLE"); + + if (costIcon != null) costIcon.Visible = false; + if (timeIcon != null) timeIcon.Visible = false; + if (productionCost != null) productionCost.Visible = false; + if (productionTime != null) productionTime.Visible = false; + if (armorTypeIcon != null) armorTypeIcon.Visible = false; + if (armorTypeLabel != null) armorTypeLabel.Visible = false; + if (productionPowerIcon != null) productionPowerIcon.Visible = false; + if (productionPower != null) productionPower.Visible = false; + if (notProducibleIcon != null) notProducibleIcon.Visible = false; + if (notProducibleLabel != null) notProducibleLabel.Visible = false; + + // For variants without BuildableInfo/ValuedInfo, fall back to base actor + var bi = actor.TraitInfoOrDefault(); + var valued = actor.TraitInfoOrDefault(); + var actorForProduction = actor; + + if ((bi == null || valued == null) && actor != selectedActor && selectedActor != null) + { + // Variant doesn't have production info, use base actor + if (bi == null) + bi = selectedActor.TraitInfoOrDefault(); + if (valued == null) + { + valued = selectedActor.TraitInfoOrDefault(); + actorForProduction = selectedActor; + } + } + + var selectedInfo = info.ContainsKey(actor) ? info[actor] : info[selectedActor]; + + if (bi != null && !selectedInfo.HideBuildable) + { + var cost = valued?.Cost ?? 0; + if (cost > 0 && productionCost != null && costIcon != null) + { + var costText = cost.ToString(NumberFormatInfo.CurrentInfo); + productionCost.Text = costText; + costIcon.Bounds.X = currentX; + productionCost.Bounds.X = currentX + IconWidth + LabelSpacing; + var costWidth = Game.Renderer.Fonts[productionCost.Font].Measure(costText).X; + currentX += IconWidth + LabelSpacing + costWidth + GroupSpacing; + + costIcon.Visible = true; + productionCost.Visible = true; + productionContainerHeight = descriptionFont.Measure(costText).Y; + } + + var time = BuildTime(actorForProduction, selectedInfo.BuildableQueue); + if (time > 0 && productionTime != null && timeIcon != null) + { + var timeText = WidgetUtils.FormatTime(time, world.Timestep); + productionTime.Text = timeText; + timeIcon.Bounds.X = currentX; + productionTime.Bounds.X = currentX + IconWidth + LabelSpacing; + var timeWidth = Game.Renderer.Fonts[productionTime.Font].Measure(timeText).X; + currentX += IconWidth + LabelSpacing + timeWidth + GroupSpacing; + + timeIcon.Visible = true; + productionTime.Visible = true; + productionContainerHeight = Math.Max(productionContainerHeight, descriptionFont.Measure(timeText).Y); + } + } + else + { + if (encyclopediaExtrasInfo != null && encyclopediaExtrasInfo.HideNotProducible) + { + productionContainer.Visible = false; + } + else + { + notProducibleIcon.Visible = true; + notProducibleIcon.Bounds.X = currentX; + notProducibleLabel.Visible = true; + notProducibleLabel.Bounds.X = currentX + IconWidth + LabelSpacing; + var notProducibleLabelWidth = Game.Renderer.Fonts[notProducibleLabel.Font].Measure(notProducibleLabel.Text).X; + currentX += IconWidth + LabelSpacing + notProducibleLabelWidth + GroupSpacing; + productionContainerHeight = descriptionFont.Measure(notProducibleLabel.Text).Y; + } + } - // Hide production container for variants - if (productionContainer != null) - productionContainer.Visible = false; + if (armorTypeLabel != null && armorTypeIcon != null) + { + var armor = actor.TraitInfos().FirstOrDefault(); + if (armor != null && !string.IsNullOrEmpty(armor.Type)) + { + SelectionTooltipLogic.GetArmorTypeLabel(armorTypeLabel, actor); + var hasArmorType = !string.IsNullOrEmpty(armorTypeLabel.Text); + if (hasArmorType) + { + armorTypeIcon.Bounds.X = currentX; + armorTypeLabel.Bounds.X = currentX + IconWidth + LabelSpacing; + var armorWidth = Game.Renderer.Fonts[armorTypeLabel.Font].Measure(armorTypeLabel.Text).X; + currentX += IconWidth + LabelSpacing + armorWidth + GroupSpacing; + + armorTypeIcon.Visible = true; + armorTypeLabel.Visible = true; + productionContainerHeight = Math.Max(productionContainerHeight, descriptionFont.Measure(armorTypeLabel.Text).Y); + } + } + } + + var power = actor.TraitInfos().Where(i => i.EnabledByDefault).Sum(i => i.Amount); + if (power != 0 && productionPower != null && productionPowerIcon != null) + { + var powerText = power.ToString(NumberFormatInfo.CurrentInfo); + productionPower.Text = powerText; + productionPowerIcon.Bounds.X = currentX; + productionPower.Bounds.X = currentX + IconWidth + LabelSpacing; + var powerWidth = Game.Renderer.Fonts[productionPower.Font].Measure(powerText).X; + currentX += IconWidth + LabelSpacing + powerWidth + GroupSpacing; - currentY = 10; + productionPowerIcon.Visible = true; + productionPower.Visible = true; + productionContainerHeight = Math.Max(productionContainerHeight, descriptionFont.Measure(powerText).Y); + } - // Get description from variant's EncyclopediaExtras or TooltipExtras - var variantExtras = variant.TraitInfoOrDefault(); - var variantTooltipExtras = variant.TraitInfos().FirstOrDefault(t => t.IsStandard); + // Only show the production container if it has any visible content + var hasVisibleContent = (costIcon?.Visible == true) || + (timeIcon?.Visible == true) || + (armorTypeIcon?.Visible == true) || + (productionPowerIcon?.Visible == true) || + (notProducibleIcon?.Visible == true); + productionContainer.Visible = hasVisibleContent; + return productionContainerHeight + 10; + } + + int SetupDescriptionSection(ActorInfo actor, int currentY, bool showEncyclopediaDescription) + { + // Get prerequisites and description + var bi = actor.TraitInfoOrDefault(); + var prerequisitesText = ""; var descriptionText = ""; - if (variantExtras != null && !string.IsNullOrEmpty(variantExtras.Description)) + + if (bi != null) { - descriptionText = WidgetUtilsCA.WrapTextWithIndent( - FluentProvider.GetMessage(variantExtras.Description.Replace("\\n", "\n")), - descriptionLabel.Bounds.Width, - descriptionFont); + var prereqs = bi.Prerequisites + .Select(a => ActorName(modData.DefaultRules, a)) + .Where(s => !s.StartsWith('~') && !s.StartsWith('!')) + .ToList(); + + if (prereqs.Count != 0) + { + prerequisitesText = WidgetUtilsCA.WrapTextWithIndent( + FluentProvider.GetMessage(Requires, "prerequisites", prereqs.JoinWith(", ")), + descriptionLabel.Bounds.Width, + descriptionFont); + } + + if (!string.IsNullOrEmpty(bi.Description)) + { + descriptionText = WidgetUtilsCA.WrapTextWithIndent( + FluentProvider.GetMessage(bi.Description.Replace("\\n", "\n")), + descriptionLabel.Bounds.Width, + descriptionFont); + } } - else if (variantTooltipExtras != null && !string.IsNullOrEmpty(variantTooltipExtras.Description)) + + var tooltipExtras = actor.TraitInfos().FirstOrDefault(info => info.IsStandard); + + if (string.IsNullOrEmpty(descriptionText)) { - descriptionText = WidgetUtilsCA.WrapTextWithIndent( - FluentProvider.GetMessage(variantTooltipExtras.Description.Replace("\\n", "\n")), - descriptionLabel.Bounds.Width, - descriptionFont); + if (tooltipExtras != null && !string.IsNullOrEmpty(tooltipExtras.Description)) + { + descriptionText = WidgetUtilsCA.WrapTextWithIndent( + FluentProvider.GetMessage(tooltipExtras.Description.Replace("\\n", "\n")), + descriptionLabel.Bounds.Width, + descriptionFont); + } + else if (encyclopediaExtrasInfo != null && !string.IsNullOrEmpty(encyclopediaExtrasInfo.Description)) + { + descriptionText = WidgetUtilsCA.WrapTextWithIndent( + FluentProvider.GetMessage(encyclopediaExtrasInfo.Description.Replace("\\n", "\n")), + descriptionLabel.Bounds.Width, + descriptionFont); + } } + var prerequisitesHeight = string.IsNullOrEmpty(prerequisitesText) ? 0 : descriptionFont.Measure(prerequisitesText).Y; + prerequisitesLabel.GetText = () => prerequisitesText; + prerequisitesLabel.Bounds.Height = prerequisitesHeight; + prerequisitesLabel.Visible = !string.IsNullOrEmpty(prerequisitesText); + var descriptionHeight = string.IsNullOrEmpty(descriptionText) ? 0 : descriptionFont.Measure(descriptionText).Y; descriptionLabel.GetText = () => descriptionText; descriptionLabel.Bounds.Height = descriptionHeight; descriptionLabel.Visible = !string.IsNullOrEmpty(descriptionText); + if (!string.IsNullOrEmpty(prerequisitesText)) + { + prerequisitesLabel.Bounds.Y = currentY; + currentY += prerequisitesHeight + 8; + } + if (!string.IsNullOrEmpty(descriptionText)) { descriptionLabel.Bounds.Y = currentY; currentY += descriptionHeight + 8; } - // Hide prerequisites for variants - prerequisitesLabel.Visible = false; - - // Hide subfaction info - if (subfactionLabel != null) - subfactionLabel.Visible = false; - if (subfactionFlagImage != null) - subfactionFlagImage.Visible = false; - if (additionalInfoLabel != null) - additionalInfoLabel.Visible = false; - - // Get strengths/weaknesses/attributes from variant + // Get strengths/weaknesses/attributes var strengthsText = ""; var weaknessesText = ""; var attributesText = ""; - if (variantTooltipExtras != null) + if (tooltipExtras != null) { - strengthsText = WidgetUtilsCA.WrapTextWithIndent(variantTooltipExtras.Strengths.Replace("\\n", "\n"), strengthsLabel.Bounds.Width, descriptionFont, 6); - weaknessesText = WidgetUtilsCA.WrapTextWithIndent(variantTooltipExtras.Weaknesses.Replace("\\n", "\n"), weaknessesLabel.Bounds.Width, descriptionFont, 6); - attributesText = WidgetUtilsCA.WrapTextWithIndent(variantTooltipExtras.Attributes.Replace("\\n", "\n"), attributesLabel.Bounds.Width, descriptionFont, 6); + strengthsText = WidgetUtilsCA.WrapTextWithIndent(tooltipExtras.Strengths.Replace("\\n", "\n"), strengthsLabel.Bounds.Width, descriptionFont, 6); + weaknessesText = WidgetUtilsCA.WrapTextWithIndent(tooltipExtras.Weaknesses.Replace("\\n", "\n"), weaknessesLabel.Bounds.Width, descriptionFont, 6); + attributesText = WidgetUtilsCA.WrapTextWithIndent(tooltipExtras.Attributes.Replace("\\n", "\n"), attributesLabel.Bounds.Width, descriptionFont, 6); } if (!string.IsNullOrEmpty(strengthsText) && strengthsLabel != null) @@ -1177,13 +1124,29 @@ void UpdateVariantDescription(ActorInfo variant) attributesLabel.Visible = false; } - // Hide encyclopedia description for variants - if (encyclopediaDescriptionLabel != null) + // Show encyclopedia description only for base actors + if (showEncyclopediaDescription) + { + var selectedInfo = info.ContainsKey(actor) ? info[actor] : null; + var encyclopediaText = ""; + if (selectedInfo != null && !string.IsNullOrEmpty(selectedInfo.Description)) + encyclopediaText = WidgetUtils.WrapText(FluentProvider.GetMessage(selectedInfo.Description), descriptionLabel.Bounds.Width, descriptionFont); + + if (!string.IsNullOrEmpty(encyclopediaText) && encyclopediaDescriptionLabel != null) + { + SetupTextLabel(encyclopediaDescriptionLabel, encyclopediaText, ref currentY, 0); + } + else if (encyclopediaDescriptionLabel != null) + { + encyclopediaDescriptionLabel.Visible = false; + } + } + else if (encyclopediaDescriptionLabel != null) + { encyclopediaDescriptionLabel.Visible = false; + } - actorDetailsContainer.Bounds.Height = currentY; - descriptionPanel.Layout.AdjustChildren(); - descriptionPanel.ScrollToTop(); + return currentY; } int GetVariantGroupSortOrder(string groupName) @@ -1464,6 +1427,14 @@ void LoadExtras(ActorInfo actor) static string GetActorDisplayName(ActorInfo actor) { + // Check for EncyclopediaExtrasInfo.Name first + var extras = actor.TraitInfoOrDefault(); + if (extras != null && !string.IsNullOrEmpty(extras.Name)) + { + return FluentProvider.GetMessage(extras.Name); + } + + // Fall back to TooltipInfo var name = actor.TraitInfos().FirstOrDefault(info => info.EnabledByDefault)?.Name; if (!string.IsNullOrEmpty(name)) { diff --git a/mods/ca/rules/scrin.yaml b/mods/ca/rules/scrin.yaml index 0489c1ec57..5969a2bcd4 100644 --- a/mods/ca/rules/scrin.yaml +++ b/mods/ca/rules/scrin.yaml @@ -5505,6 +5505,7 @@ MANI: Amount: -200 MustBeDestroyed: RequiredForShortGame: false + ProvidesPrerequisite@buildingname: InfiltrateForSupportPowerReset: Types: ResetSupportPowerInfiltrate InfiltrationNotification: BuildingInfiltrated diff --git a/mods/ca/rules/structures.yaml b/mods/ca/rules/structures.yaml index 88870438ac..e0035ac13e 100644 --- a/mods/ca/rules/structures.yaml +++ b/mods/ca/rules/structures.yaml @@ -375,6 +375,7 @@ IRON: Amount: -200 MustBeDestroyed: RequiredForShortGame: false + ProvidesPrerequisite@buildingname: InfiltrateForSupportPowerReset: Types: ResetSupportPowerInfiltrate InfiltrationNotification: BuildingInfiltrated @@ -5219,6 +5220,7 @@ SGEN: Type: Rectangle TopLeft: -1536, -1024 BottomRight: 1536, 1024 + ProvidesPrerequisite@buildingname: InfiltrateForSupportPowerReset: Types: ResetSupportPowerInfiltrate InfiltrationNotification: BuildingInfiltrated @@ -5515,6 +5517,7 @@ PATR: RejectsOrders@LOADED: Except: Sell, PowerDown RequiresCondition: loaded-rocket + ProvidesPrerequisite@buildingname: InfiltrateForSupportPowerReset: Types: ResetSupportPowerInfiltrate InfiltrationNotification: BuildingInfiltrated diff --git a/mods/ca/rules/vehicles.yaml b/mods/ca/rules/vehicles.yaml index 934585568e..978c4de695 100644 --- a/mods/ca/rules/vehicles.yaml +++ b/mods/ca/rules/vehicles.yaml @@ -936,6 +936,8 @@ GTNK.squad: Upgradeable@RHINO: Actor: 3tnk.rhino.atomic -Upgradeable@ATOMIC: + EncyclopediaExtras: + VariantOf: 3TNK 3TNK.YURI: Inherits: 3TNK @@ -1068,6 +1070,7 @@ GTNK.squad: -Upgradeable@LASHER: EncyclopediaExtras: Subfaction: yuri + VariantOf: 3TNK.YURI 3TNK.RHINO: Inherits: ^Tank @@ -1177,6 +1180,7 @@ GTNK.squad: Encyclopedia: Category: Soviets/Vehicles EncyclopediaExtras: + Name: Rhino Tank AdditionalInfo: Requires Armor Doctrine. 3TNK.RHINO.ATOMIC: @@ -1207,6 +1211,9 @@ GTNK.squad: Upgradeable@LASHER: Actor: 3tnk.rhino.atomicyuri -Upgradeable@ATOMIC: + EncyclopediaExtras: + Name: Atomic Rhino Tank + VariantOf: 3TNK.RHINO 3TNK.RHINO.YURI: Inherits: 3TNK.RHINO @@ -1245,6 +1252,7 @@ GTNK.squad: -SpeedMultiplier@CRUSHSLOW: -Upgradeable@LASHER: EncyclopediaExtras: + Name: Thrasher Tank Subfaction: yuri 3TNK.RHINO.ATOMICYURI: @@ -1282,7 +1290,9 @@ GTNK.squad: -Upgradeable@LASHER: -WithDecoration@UpgradeOverlay: EncyclopediaExtras: + Name: Atomic Thrasher Tank Subfaction: yuri + VariantOf: 3TNK.RHINO.YURI 4TNK: Inherits: ^Tank @@ -1450,6 +1460,8 @@ GTNK.squad: Upgradeable@APOC: Actor: apoc.atomic -Upgradeable@ATOMIC: + EncyclopediaExtras: + VariantOf: 4TNK 4TNK.ERAD: Inherits: 4TNK @@ -1538,6 +1550,8 @@ GTNK.squad: Upgradeable@OVLD: Actor: ovld.erad.atomic -Upgradeable@ERAD: + EncyclopediaExtras: + VariantOf: 4TNK.ERAD ARTY: Inherits: ^Vehicle @@ -2374,6 +2388,8 @@ APC: RequiresCondition: !mindcontrolled Encyclopedia: Category: Allies/Vehicles + EncyclopediaExtras: + Name: APC AAPC: Inherits: APC @@ -2408,6 +2424,8 @@ AAPC: -ReplacedInQueue: -Upgradeable@Archer: -WithDecoration@UpgradeOverlay: + EncyclopediaExtras: + Name: Archer APC APC.AI: Inherits: APC @@ -3786,6 +3804,8 @@ APC2: RequiresCondition: !mindcontrolled Encyclopedia: Category: GDI/Vehicles; Nod/Vehicles + EncyclopediaExtras: + Name: APC APC2.NODAI: Inherits: APC2 @@ -3997,6 +4017,8 @@ VULC: RequiresCondition: loaded-cmdo Encyclopedia: Category: GDI/Vehicles + EncyclopediaExtras: + Name: Vulcan VULC.AI: Inherits: VULC @@ -5804,6 +5826,8 @@ BTR: VoiceSet: BtrVoice Encyclopedia: Category: Soviets/Vehicles + EncyclopediaExtras: + Name: BTR BTR.AI: Inherits: BTR @@ -5946,6 +5970,7 @@ BTR.YURI: -Upgradeable@GATTLING: -WithDecoration@UpgradeOverlay: EncyclopediaExtras: + Name: Gattling BTR Subfaction: yuri BTR.YURI.AI: @@ -7113,6 +7138,8 @@ IFV: CasingTargetOffset: 0, -600, 0 Encyclopedia: Category: Allies/Vehicles + EncyclopediaExtras: + Name: IFV IFV.AI: Inherits: ^Tank @@ -9851,6 +9878,8 @@ APOC.ATOMIC: Upgradeable@ERAD: Actor: apoc.erad.atomic -Upgradeable@ATOMIC: + EncyclopediaExtras: + VariantOf: APOC APOC.ERAD: Inherits: APOC @@ -9934,6 +9963,7 @@ APOC.ERAD.ATOMIC: VoiceSet: EradVoice EncyclopediaExtras: Subfaction: iraq + VariantOf: APOC.ERAD OVLD: Inherits: ^Tank @@ -10090,6 +10120,8 @@ OVLD.ATOMIC: SpeedMultiplier: Modifier: 125 -SpawnActorOnDeath: + EncyclopediaExtras: + VariantOf: OVLD OVLD.ERAD: Inherits: OVLD @@ -10166,6 +10198,7 @@ OVLD.ERAD.ATOMIC: VoiceSet: EradVoice EncyclopediaExtras: Subfaction: iraq + VariantOf: OVLD.ERAD THWK: Inherits: ^TankTD From 9e91d30809591528a6147ceb87e0b82f1703fa7c Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Fri, 2 Jan 2026 07:11:14 +0000 Subject: [PATCH 21/28] Campaign prerequisites correction. --- mods/ca/rules/custom/disable-coalitions.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mods/ca/rules/custom/disable-coalitions.yaml b/mods/ca/rules/custom/disable-coalitions.yaml index a62ab5c846..a3d56366c2 100644 --- a/mods/ca/rules/custom/disable-coalitions.yaml +++ b/mods/ca/rules/custom/disable-coalitions.yaml @@ -37,3 +37,7 @@ PMAK: ENFO: Buildable: Prerequisites: ~tent, atek, ~techlevel.high + +OREP: + Buildable: + Prerequisites: ~structures.allies, atek From 24220a71de12e4113db85946f3f772dd38e655e1 Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Fri, 2 Jan 2026 07:15:36 +0000 Subject: [PATCH 22/28] Campaign progress update. --- .../Traits/Player/CampaignProgressTracker.cs | 15 +++++++++++---- .../Widgets/Logic/MissionBrowserLogicCA.cs | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/OpenRA.Mods.CA/Traits/Player/CampaignProgressTracker.cs b/OpenRA.Mods.CA/Traits/Player/CampaignProgressTracker.cs index 7e6ecdbe6d..87e14379e2 100644 --- a/OpenRA.Mods.CA/Traits/Player/CampaignProgressTracker.cs +++ b/OpenRA.Mods.CA/Traits/Player/CampaignProgressTracker.cs @@ -48,7 +48,7 @@ void INotifyWinStateChanged.OnPlayerWon(Player player) if (!player.World.Map.Categories.Contains("Campaign")) return; - var missionTitle = GetMapTileWithoutNumber(player.World.Map.Title); + var missionTitle = GetMapTitleWithoutNumber(player.World.Map.Title); var worldActor = player.World.WorldActor; var difficulty = worldActor.TraitsImplementing() .FirstOrDefault(sld => sld.Info.ID == "difficulty"); @@ -124,16 +124,23 @@ void IResolveOrder.ResolveOrder(Actor self, Order order) developerCommandUsed = true; } - public static string GetMapTileWithoutNumber(string mapTitle) + public static string GetMapTitleWithoutNumber(string mapTitle) { var firstColonIndex = mapTitle.IndexOf(": "); var firstPeriodIndex = mapTitle.IndexOf(". "); var splitIndex = firstColonIndex >= 0 ? firstColonIndex : firstPeriodIndex; + string result; if (splitIndex >= 0) - return mapTitle.Substring(splitIndex + 2); + result = mapTitle.Substring(splitIndex + 2); + else + result = mapTitle; - return mapTitle; + var bracketIndex = result.IndexOf('('); + if (bracketIndex >= 0) + result = result.Substring(0, bracketIndex); + + return result.TrimEnd(); } public static Dictionary GetCampaignProgress() diff --git a/OpenRA.Mods.CA/Widgets/Logic/MissionBrowserLogicCA.cs b/OpenRA.Mods.CA/Widgets/Logic/MissionBrowserLogicCA.cs index 9a2f043117..c8a817a425 100644 --- a/OpenRA.Mods.CA/Widgets/Logic/MissionBrowserLogicCA.cs +++ b/OpenRA.Mods.CA/Widgets/Logic/MissionBrowserLogicCA.cs @@ -256,7 +256,7 @@ void CreateMissionGroup(string title, IEnumerable previews, Action o () => StartMissionClicked(onExit)); var label = item.Get("TITLE"); - var missionTitle = CampaignProgressTracker.GetMapTileWithoutNumber(preview.Title); + var missionTitle = CampaignProgressTracker.GetMapTitleWithoutNumber(preview.Title); if (campaignProgress.ContainsKey(missionTitle)) { From 23f4e611b6cbced7c2a91aabb3b121ffab258db0 Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Fri, 2 Jan 2026 22:21:40 +0000 Subject: [PATCH 23/28] Incapacitation respawn fix. --- .../ca21-incapacitation/incapacitation.lua | 93 ++++++++++++------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/mods/ca/missions/main-campaign/ca21-incapacitation/incapacitation.lua b/mods/ca/missions/main-campaign/ca21-incapacitation/incapacitation.lua index a2e5b4f461..7d6248866d 100644 --- a/mods/ca/missions/main-campaign/ca21-incapacitation/incapacitation.lua +++ b/mods/ca/missions/main-campaign/ca21-incapacitation/incapacitation.lua @@ -159,7 +159,7 @@ WorldLoaded = function() end) Trigger.AfterDelay(DateTime.Seconds(5), function() - SpawnLeechers() + SpawnInitialLeechers() Trigger.AfterDelay(DateTime.Seconds(5), function() Tip("Leechers can be deployed using [" .. UtilsCA.Hotkey("Deploy") .. "] to temporarily transform into balls of bio-matter which heal nearby allies.") @@ -274,6 +274,7 @@ end OncePerFiveSecondChecks = function() if DateTime.GameTime > 1 and DateTime.GameTime % 125 == 0 then UpdatePlayerBaseLocations() + LeecherRespawnCheck() end end @@ -395,7 +396,7 @@ UpdateObjective = function() UserInterface.SetMissionText(#activeAA .. " active anti-aircraft defenses remaining. " .. #aircraftStructuresRemaining .. " aircraft structures remaining.", HSLColor.Yellow) end -SpawnLeechers = function() +SpawnInitialLeechers = function() local wormhole = Actor.Create("wormhole", true, { Owner = Scrin, Location = LeecherSpawn.Location }) Beacon.New(Scrin, LeecherSpawn.CenterPosition, DateTime.Seconds(20)) @@ -411,51 +412,71 @@ SpawnLeechers = function() Utils.Do(leechers, function(leecher) leecher.GrantCondition("difficulty-" .. Difficulty) leecher.Scatter() - LeecherDeathTrigger(leecher) end) + SetupLeecherRespawning() + Trigger.AfterDelay(DateTime.Seconds(5), function() wormhole.Kill() end) end) end -LeecherDeathTrigger = function(a) - if RespawnEnabled then - Trigger.OnKilled(a, function(self, killer) - Trigger.AfterDelay(1, function() - local orbs = Utils.Where(self.Owner.GetActorsByType("lchr.orb"), function(a) - return not OrbsRespawning[tostring(a)] - end) +SetupLeecherRespawning = function() + if not RespawnEnabled then + return + end - Utils.Do(orbs, function(orb) - OrbsRespawning[tostring(orb)] = true - - Trigger.OnKilled(orb, function(self, killer) - local spawnCell = CPos.New(LeecherSpawn.Location.X + Utils.RandomInteger(-1, 1), LeecherSpawn.Location.Y + Utils.RandomInteger(-1, 1)) - Notification("Leecher arriving in 20 seconds.") - - Trigger.AfterDelay(DateTime.Seconds(20), function() - local wormhole = Actor.Create("wormhole", true, { Owner = Scrin, Location = spawnCell }) - - Trigger.AfterDelay(DateTime.Seconds(1), function() - local leecher = Reinforcements.Reinforce(self.Owner, { "lchr" }, { spawnCell }, 1)[1] - leecher.Scatter() - Beacon.New(self.Owner, leecher.CenterPosition) - Media.PlaySpeechNotification(self.Owner, "ReinforcementsArrived") - leecher.GrantCondition("difficulty-" .. Difficulty) - LeecherDeathTrigger(leecher) - end) - - Trigger.AfterDelay(DateTime.Seconds(5), function() - wormhole.Kill() - end) - end) - end) - end) - end) + LeecherStatuses = {} + + Trigger.AfterDelay(10, function() + local leechers = GetMissionPlayersActorsByType("lchr") + Utils.Do(leechers, function(leecher) + if not LeecherStatuses[leecher.Owner.InternalName] then + LeecherStatuses[leecher.Owner.InternalName] = { Owner = leecher.Owner, IsRespawning = false, Count = 0 } + end + LeecherStatuses[leecher.Owner.InternalName].Count = LeecherStatuses[leecher.Owner.InternalName].Count + 1 end) + end) +end + +LeecherRespawnCheck = function() + if not RespawnEnabled or not LeecherStatuses then + return end + Utils.Do(LeecherStatuses, function(status) + if not status.IsRespawning then + local leecherCount = #status.Owner.GetActorsByTypes({ "lchr", "lchr.orb" }) + if leecherCount < status.Count then + status.IsRespawning = true + RespawnLeecher(status) + end + end + end) +end + +RespawnLeecher = function(status) + Notification("Leecher arriving in 20 seconds.") + + local player = status.Owner + local spawnCell = CPos.New(LeecherSpawn.Location.X + Utils.RandomInteger(-1, 1), LeecherSpawn.Location.Y + Utils.RandomInteger(-1, 1)) + + Trigger.AfterDelay(DateTime.Seconds(20), function() + local wormhole = Actor.Create("wormhole", true, { Owner = player, Location = spawnCell }) + + Trigger.AfterDelay(DateTime.Seconds(1), function() + local leecher = Reinforcements.Reinforce(player, { "lchr" }, { spawnCell }, 1)[1] + leecher.Scatter() + Beacon.New(player, leecher.CenterPosition) + Media.PlaySpeechNotification(player, "ReinforcementsArrived") + leecher.GrantCondition("difficulty-" .. Difficulty) + status.IsRespawning = false + end) + + Trigger.AfterDelay(DateTime.Seconds(5), function() + wormhole.Kill() + end) + end) end IntruderDeathTrigger = function(a) From 025b114b747030adaa15df914e3a1899cc121dc1 Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Sat, 3 Jan 2026 10:06:53 +0000 Subject: [PATCH 24/28] Fix for in-game encyclopedia preview position. --- .../Widgets/ActorPreviewCAWidget.cs | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/OpenRA.Mods.CA/Widgets/ActorPreviewCAWidget.cs b/OpenRA.Mods.CA/Widgets/ActorPreviewCAWidget.cs index 79a4b6c0aa..2f95d000c6 100644 --- a/OpenRA.Mods.CA/Widgets/ActorPreviewCAWidget.cs +++ b/OpenRA.Mods.CA/Widgets/ActorPreviewCAWidget.cs @@ -16,7 +16,6 @@ using OpenRA.Mods.Common.Graphics; using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; -using OpenRA.Traits; using OpenRA.Widgets; namespace OpenRA.Mods.CA.Widgets @@ -113,17 +112,29 @@ public override void PrepareRenderables() { var origin = RenderOrigin + PreviewOffset + new int2(RenderBounds.Size.Width / 2, RenderBounds.Size.Height / 2); - // Calculate a world position that will project to our desired screen location - // We need to reverse the viewport projection - var centerPos = worldRenderer.Viewport.ViewToWorldPx(origin); - var worldPos = worldRenderer.ProjectedPosition(centerPos); + // Calculate where WPos.Zero would render on screen in viewport coordinates + var zeroScreenPos = worldRenderer.ScreenPxPosition(WPos.Zero); + var viewportZeroPos = worldRenderer.Viewport.WorldToViewPx(zeroScreenPos); - // Use Render() instead of RenderUI() to get SpriteRenderables which already implement IModifyableRenderable + // Calculate offset from that position to our desired screen position + // This offset in screen pixels needs to be converted to world units for OffsetBy + var screenOffset = origin - viewportZeroPos; + + // Convert screen pixel offset to world vector + // The tile size and scale determine the conversion factor + var worldOffsetX = screenOffset.X * worldRenderer.TileScale / worldRenderer.TileSize.Width; + var worldOffsetY = screenOffset.Y * worldRenderer.TileScale / worldRenderer.TileSize.Height; + var worldOffset = new WVec(worldOffsetX, worldOffsetY, 0); + + // Use Render() at WPos.Zero to get SpriteRenderables which support IModifyableRenderable // This allows WithColoredOverlayCA and alpha modifications to work automatically var baseRenderables = preview - .SelectMany(p => p.Render(worldRenderer, worldPos)) + .SelectMany(p => p.Render(worldRenderer, WPos.Zero)) .Select(r => { + // Offset each renderable to the correct screen position + r = r.OffsetBy(worldOffset); + // Apply encyclopedia scale to SpriteRenderables if (r is SpriteRenderable sr && GetScale() != 1f) { From a0e9ebdffcf007b22b4b4bd626c346ef99b7b7ff Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Sat, 3 Jan 2026 11:22:15 +0000 Subject: [PATCH 25/28] Speculative desync fix. --- OpenRA.Mods.CA/Traits/DelayedWeaponAttachable.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/OpenRA.Mods.CA/Traits/DelayedWeaponAttachable.cs b/OpenRA.Mods.CA/Traits/DelayedWeaponAttachable.cs index 4b2a0adc84..cb872bac34 100644 --- a/OpenRA.Mods.CA/Traits/DelayedWeaponAttachable.cs +++ b/OpenRA.Mods.CA/Traits/DelayedWeaponAttachable.cs @@ -40,7 +40,7 @@ public class DelayedWeaponAttachableInfo : ConditionalTraitInfo public class DelayedWeaponAttachable : ConditionalTrait, ITick, INotifyKilled, ISelectionBar, INotifyTransform, INotifyRemovedFromWorld { - public HashSet Container { get; private set; } + public List Container { get; private set; } readonly Actor self; readonly HashSet detectors = new HashSet(); @@ -52,7 +52,7 @@ public DelayedWeaponAttachable(Actor self, DelayedWeaponAttachableInfo info) : base(info) { this.self = self; - Container = new HashSet(); + Container = new List(); } void ITick.Tick(Actor self) @@ -62,7 +62,7 @@ void ITick.Tick(Actor self) foreach (var trigger in Container) trigger.Tick(self); - Container.RemoveWhere(p => !p.IsValid); + Container.RemoveAll(p => !p.IsValid); while (tokens.Count > Container.Count) self.RevokeCondition(tokens.Pop()); @@ -81,7 +81,7 @@ void INotifyKilled.Killed(Actor self, AttackInfo e) trigger.Activate(self); } - Container.RemoveWhere(p => !p.IsValid); + Container.RemoveAll(p => !p.IsValid); } } @@ -150,7 +150,7 @@ void INotifyTransform.BeforeTransform(Actor self) foreach (var trigger in Container) trigger.Activate(self); - Container.RemoveWhere(p => !p.IsValid); + Container.RemoveAll(p => !p.IsValid); while (tokens.Count > 0 && Container.Count == 0) self.RevokeCondition(tokens.Pop()); From 59cf9172ce339b72cf5f9ecb181fb829052233ed Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Sat, 3 Jan 2026 12:59:27 +0000 Subject: [PATCH 26/28] Encyclopedia previews fix and power reordering. --- .../Widgets/ActorPreviewCAWidget.cs | 2 +- .../Widgets/Logic/EncyclopediaLogicCA.cs | 16 +- mods/ca/rules/aircraft.yaml | 22 +- mods/ca/rules/encyclopedia.yaml | 437 +++++++++--------- mods/ca/rules/infantry.yaml | 2 +- mods/ca/rules/structures.yaml | 2 +- mods/ca/rules/vehicles.yaml | 41 +- 7 files changed, 291 insertions(+), 231 deletions(-) diff --git a/OpenRA.Mods.CA/Widgets/ActorPreviewCAWidget.cs b/OpenRA.Mods.CA/Widgets/ActorPreviewCAWidget.cs index 2f95d000c6..e4c31a8c0a 100644 --- a/OpenRA.Mods.CA/Widgets/ActorPreviewCAWidget.cs +++ b/OpenRA.Mods.CA/Widgets/ActorPreviewCAWidget.cs @@ -130,6 +130,7 @@ public override void PrepareRenderables() // This allows WithColoredOverlayCA and alpha modifications to work automatically var baseRenderables = preview .SelectMany(p => p.Render(worldRenderer, WPos.Zero)) + .OrderBy(WorldRenderer.RenderableZPositionComparisonKey) .Select(r => { // Offset each renderable to the correct screen position @@ -169,7 +170,6 @@ public override void PrepareRenderables() // Swap player palettes to encyclopedia palettes for proper coloring renderables = baseRenderables .Select(r => SwapPlayerPalette(r)) - .OrderBy(WorldRenderer.RenderableZPositionComparisonKey) .Select(r => r.PrepareRender(worldRenderer)) .ToArray(); } diff --git a/OpenRA.Mods.CA/Widgets/Logic/EncyclopediaLogicCA.cs b/OpenRA.Mods.CA/Widgets/Logic/EncyclopediaLogicCA.cs index ca3dc7d6be..f43d95b1d2 100644 --- a/OpenRA.Mods.CA/Widgets/Logic/EncyclopediaLogicCA.cs +++ b/OpenRA.Mods.CA/Widgets/Logic/EncyclopediaLogicCA.cs @@ -779,7 +779,7 @@ ScrollItemWidget SetupItem(ActorInfo variantActor, ScrollItemWidget template) { // No groups - use simple flat dropdown without headers (preserve YAML order) var itemHeight = 25; - var totalHeight = Math.Min(allVariants.Count * itemHeight, 300); + var totalHeight = Math.Min(allVariants.Count * itemHeight, 300) + 5; variantDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", totalHeight, allVariants, SetupItem); } @@ -811,7 +811,7 @@ ScrollItemWidget SetupItem(ActorInfo variantActor, ScrollItemWidget template) // Calculate dropdown height var itemHeight = 25; var headerHeight = 13; - var totalHeight = groupedVariants.Sum(g => (string.IsNullOrEmpty(g.Key) ? 0 : headerHeight) + g.Value.Count() * itemHeight); + var totalHeight = groupedVariants.Sum(g => (string.IsNullOrEmpty(g.Key) ? 0 : headerHeight) + g.Value.Count() * itemHeight) + 5; totalHeight = Math.Min(totalHeight, 300); // Cap at 300px variantDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", totalHeight, groupedVariants, SetupItem); @@ -1266,6 +1266,10 @@ string InferPreviewOwnerFromCategory(string categoryPath) return "Nod"; } + if (selectedActor.Name == "sbag" || selectedActor.Name == "fenc") { + return "GDI"; + } + return topLevelCategory switch { "Allies" => "Greece", @@ -1299,7 +1303,11 @@ Color GetPreviewColorFromCategory(string categoryPath) return Color.FromArgb(230, 230, 255); // E6E6FF } - return Color.FromArgb(254, 17, 0); + return Color.FromArgb(254, 17, 0); // FE1100 + } + + if (selectedActor.Name == "sbag" || selectedActor.Name == "fenc") { + return Color.FromArgb(242, 207, 116); // F2CF74 } return topLevelCategory switch @@ -1308,7 +1316,7 @@ Color GetPreviewColorFromCategory(string categoryPath) "Soviets" => Color.FromArgb(254, 17, 0), // FE1100 "GDI" => Color.FromArgb(242, 207, 116), // F2CF74 "Scrin" => Color.FromArgb(128, 0, 200), // 7700FF - _ => Color.FromArgb(158, 166, 179) // 9ea6b3 + _ => Color.FromArgb(158, 166, 179) // 9EA6B3 }; } diff --git a/mods/ca/rules/aircraft.yaml b/mods/ca/rules/aircraft.yaml index 03ea3608fa..0727984284 100644 --- a/mods/ca/rules/aircraft.yaml +++ b/mods/ca/rules/aircraft.yaml @@ -482,7 +482,9 @@ SUK.UPG: Selectable: Class: suk -ReplacedInQueue: - -Encyclopedia: + EncyclopediaExtras: + Name: Seismic Sukhoi + VariantOf: SUK YAK: Inherits: ^Plane @@ -1706,7 +1708,9 @@ A10.SW: RequiresCondition: !ammo && ammo2 ValidTargets: Air, AirSmall InvalidTargets: NoAutoTarget - -Encyclopedia: + EncyclopediaExtras: + Name: Sidewinder Warthog + VariantOf: A10 A10.GAU: Inherits: A10 @@ -1783,7 +1787,9 @@ A10.GAU: AmmoPools: secondary FullSequence: pip-red WithMuzzleOverlay: - -Encyclopedia: + EncyclopediaExtras: + Name: Avenger Warthog + VariantOf: A10 A10.bomber: Inherits: ^NeutralPlane @@ -4321,7 +4327,7 @@ PHAN: EjectOnDeath: ChuteSound: gejecta.aud Encyclopedia: - Category: Nod/Aircraft + Category: Nod/Stolen Technology EncyclopediaExtras: AdditionalInfo: Stolen from Allied Helipad. @@ -4444,7 +4450,7 @@ KAMV: EjectOnDeath: ChuteSound: gejecta.aud Encyclopedia: - Category: Nod/Aircraft + Category: Nod/Stolen Technology EncyclopediaExtras: AdditionalInfo: Stolen from Soviet Airfield. @@ -4575,7 +4581,7 @@ SHDE: EjectOnDeath: ChuteSound: gejecta.aud Encyclopedia: - Category: Nod/Aircraft + Category: Nod/Stolen Technology EncyclopediaExtras: AdditionalInfo: Stolen from GDI Helipad. @@ -4728,7 +4734,7 @@ VERT: EjectOnDeath: ChuteSound: gejecta.aud Encyclopedia: - Category: Nod/Aircraft + Category: Nod/Stolen Technology EncyclopediaExtras: AdditionalInfo: Stolen from Nod Helipad. @@ -4810,7 +4816,7 @@ MCOR: EjectOnDeath: ChuteSound: gejecta.aud Encyclopedia: - Category: Nod/Aircraft + Category: Nod/Stolen Technology EncyclopediaExtras: AdditionalInfo: Stolen from Scrin Gravity Stabilizer. diff --git a/mods/ca/rules/encyclopedia.yaml b/mods/ca/rules/encyclopedia.yaml index 4b19d7df8e..183885aebf 100644 --- a/mods/ca/rules/encyclopedia.yaml +++ b/mods/ca/rules/encyclopedia.yaml @@ -38,7 +38,6 @@ encyclopedia.tips.general: QuantizeFacingsFromSequence: Sequence: stand -# Support Power Template - Uses build icon like upgrades (no 3D preview background) ^SupportPowerPreview: Inherits: ^InvisibleDummy Buildable: @@ -49,85 +48,7 @@ encyclopedia.tips.general: EncyclopediaExtras: HideNotProducible: true -encyclopedia.power.lightningstorm: - Inherits: ^SupportPowerPreview - Buildable: - Icon: storm - Prerequisites: weat - BuildDuration: 13500 - Tooltip: - Name: Lightning Storm - Encyclopedia: - Category: Allies/Support Powers - EncyclopediaExtras: - Description: Initiate a Lightning Storm which deals heavy damage over a large area. - -encyclopedia.power.chronoshift: - Inherits: ^SupportPowerPreview - Buildable: - Icon: chrono - Prerequisites: pdox - BuildDuration: 4500 - Tooltip: - Name: Chronoshift - Encyclopedia: - Category: Allies/Support Powers - EncyclopediaExtras: - Description: Teleports up to 9 selected vehicles to a targeted location, returning them to their original location after a short time.\n\n• If killed, units are returned with 20% health\n• Units with larger health pools take additional damage\n• Passengers cannot be unloaded\n• Enemy units are teleported for reduced duration - -encyclopedia.power.forceshield: - Inherits: ^SupportPowerPreview - Buildable: - Icon: forceshield - Prerequisites: techcenter.any - BuildDuration: 7500 - Tooltip: - Name: Force Shield - Encyclopedia: - Category: Allies/Support Powers; Soviets/Support Powers; GDI/Support Powers; Nod/Support Powers; Scrin/Support Powers - EncyclopediaExtras: - Description: Makes selected friendly structures temporarily invulnerable.\n\nWarning: Causes power failure. - -encyclopedia.power.spysatellite: - Inherits: ^SupportPowerPreview - Buildable: - Icon: gps - Prerequisites: atek - BuildDuration: 5250 - Tooltip: - Name: Spy Satellite - Encyclopedia: - Category: Allies/Support Powers - EncyclopediaExtras: - Description: Periodically reveals the entire map for a short time (activated automatically). - -encyclopedia.power.timewarp: - Inherits: ^SupportPowerPreview - Buildable: - Icon: timewarp - Prerequisites: pdox - BuildDuration: 4500 - Tooltip: - Name: Time Warp - Encyclopedia: - Category: Allies/Support Powers - EncyclopediaExtras: - Description: Disrupts time and space at the target location. Affected units & structures are frozen in time, unable to act, but immune to damage. - Subfaction: germany - -encyclopedia.power.tempinc: - Inherits: ^SupportPowerPreview - Buildable: - Icon: tempinc - Prerequisites: pdox - BuildDuration: 7500 - Tooltip: - Name: Temporal Incursion - Encyclopedia: - Category: Allies/Support Powers - EncyclopediaExtras: - Description: Summons reinforcements from the future. Units return to their origin time after a short time. - Subfaction: germany +# Allied Support Powers encyclopedia.power.veilofwar: Inherits: ^SupportPowerPreview @@ -215,32 +136,74 @@ encyclopedia.power.bsky: Description: Fires long range guided missiles at multiple ground targets prioritized by value. Tracking can be lost if targets move far enough from their initial location. AdditionalInfo: Requires Korea Coalition. -# Soviets Support Powers -encyclopedia.power.ironcurtain: +encyclopedia.power.spysatellite: Inherits: ^SupportPowerPreview Buildable: - Icon: invuln - Prerequisites: iron + Icon: gps + Prerequisites: atek + BuildDuration: 5250 + Tooltip: + Name: Spy Satellite + Encyclopedia: + Category: Allies/Support Powers + EncyclopediaExtras: + Description: Periodically reveals the entire map for a short time (activated automatically). + +encyclopedia.power.tempinc: + Inherits: ^SupportPowerPreview + Buildable: + Icon: tempinc + Prerequisites: pdox + BuildDuration: 7500 + Tooltip: + Name: Temporal Incursion + Encyclopedia: + Category: Allies/Support Powers + EncyclopediaExtras: + Description: Summons reinforcements from the future. Units return to their origin time after a short time. + Subfaction: germany + +encyclopedia.power.timewarp: + Inherits: ^SupportPowerPreview + Buildable: + Icon: timewarp + Prerequisites: pdox BuildDuration: 4500 Tooltip: - Name: Iron Curtain + Name: Time Warp Encyclopedia: - Category: Soviets/Support Powers + Category: Allies/Support Powers EncyclopediaExtras: - Description: Makes selected friendly vehicles temporarily invulnerable. + Description: Disrupts time and space at the target location. Affected units & structures are frozen in time, unable to act, but immune to damage. + Subfaction: germany -encyclopedia.power.atombomb: +encyclopedia.power.chronoshift: Inherits: ^SupportPowerPreview Buildable: - Icon: abomb - Prerequisites: mslo + Icon: chrono + Prerequisites: pdox + BuildDuration: 4500 + Tooltip: + Name: Chronoshift + Encyclopedia: + Category: Allies/Support Powers + EncyclopediaExtras: + Description: Teleports up to 9 selected vehicles to a targeted location, returning them to their original location after a short time.\n\n• If killed, units are returned with 20% health\n• Units with larger health pools take additional damage\n• Passengers cannot be unloaded\n• Enemy units are teleported for reduced duration + +encyclopedia.power.lightningstorm: + Inherits: ^SupportPowerPreview + Buildable: + Icon: storm + Prerequisites: weat BuildDuration: 13500 Tooltip: - Name: Atom Bomb + Name: Lightning Storm Encyclopedia: - Category: Soviets/Support Powers + Category: Allies/Support Powers EncyclopediaExtras: - Description: Launches a devastating atomic bomb at the target location, dealing heavy damage over a large area. + Description: Initiate a Lightning Storm which deals heavy damage over a large area. + +# Soviet Support Powers encyclopedia.power.spyplane: Inherits: ^SupportPowerPreview @@ -404,19 +367,33 @@ encyclopedia.power.killzone: Description: Calls in a Spy Plane which marks a target area. Any enemy units within it take increased damage. AdditionalInfo: Requires Artillery Doctrine. -# GDI Support Powers -encyclopedia.power.ioncannon: +encyclopedia.power.ironcurtain: Inherits: ^SupportPowerPreview Buildable: - Icon: ioncannon - Prerequisites: eye + Icon: invuln + Prerequisites: iron + BuildDuration: 4500 + Tooltip: + Name: Iron Curtain + Encyclopedia: + Category: Soviets/Support Powers + EncyclopediaExtras: + Description: Makes selected friendly vehicles temporarily invulnerable. + +encyclopedia.power.atombomb: + Inherits: ^SupportPowerPreview + Buildable: + Icon: abomb + Prerequisites: mslo BuildDuration: 13500 Tooltip: - Name: Ion Cannon + Name: Atom Bomb Encyclopedia: - Category: GDI/Support Powers + Category: Soviets/Support Powers EncyclopediaExtras: - Description: Fires a devastating ion cannon beam at the target location. + Description: Launches a devastating atomic bomb at the target location, dealing heavy damage over a large area. + +# GDI Support Powers encyclopedia.power.recondrone: Inherits: ^SupportPowerPreview @@ -502,6 +479,21 @@ encyclopedia.power.naniterepair: Description: Repairs selected damaged vehicles over time. Subfaction: arc +encyclopedia.power.surgicalstrike: + Inherits: ^SupportPowerPreview + Buildable: + Icon: surgicalstrike + IconPalette: chrometd + Prerequisites: eye + BuildDuration: 6000 + Tooltip: + Name: Surgical Strike + Encyclopedia: + Category: GDI/Support Powers + EncyclopediaExtras: + Description: Initiate an precision Ion Cannon strike which deals instant damage to a small area. + Subfaction: zocom + encyclopedia.power.firestorm: Inherits: ^SupportPowerPreview Buildable: @@ -559,63 +551,33 @@ encyclopedia.power.empmissile: EncyclopediaExtras: Description: Launches an EMP missile that disables vehicles and structures in the target area. -encyclopedia.power.surgicalstrike: +encyclopedia.power.ioncannon: Inherits: ^SupportPowerPreview Buildable: - Icon: surgicalstrike - IconPalette: chrometd + Icon: ioncannon Prerequisites: eye - BuildDuration: 6000 + BuildDuration: 13500 Tooltip: - Name: Surgical Strike + Name: Ion Cannon Encyclopedia: Category: GDI/Support Powers EncyclopediaExtras: - Description: Initiate an precision Ion Cannon strike which deals instant damage to a small area. - Subfaction: zocom + Description: Fires a devastating ion cannon beam at the target location. # Nod Support Powers -encyclopedia.power.clustermissile: - Inherits: ^SupportPowerPreview - Buildable: - Icon: clustermissile - IconPalette: chrometd - Prerequisites: tmpl - BuildDuration: 10500 - Tooltip: - Name: Cluster Missile - Encyclopedia: - Category: Nod/Support Powers - EncyclopediaExtras: - Description: Launches a Cluster Missile which deals heavy damage at the target location. -encyclopedia.power.chemmissile: - Inherits: ^SupportPowerPreview - Buildable: - Icon: chemmissile - IconPalette: chrometd - Prerequisites: mslo.nod - BuildDuration: 13500 - Tooltip: - Name: Chemical Missile - Encyclopedia: - Category: Nod/Support Powers - EncyclopediaExtras: - Description: Launches an deadly Chemical Missile. Deals heavy damage and creates toxic clouds which are extremely harmful to infantry.\n - -encyclopedia.power.tibstealth: +encyclopedia.power.hacksat: Inherits: ^SupportPowerPreview Buildable: - Icon: invis - IconPalette: chrometd - Prerequisites: sgen + Icon: hacksat + Prerequisites: anyradar BuildDuration: 4500 Tooltip: - Name: Tiberium Stealth + Name: Hack Satellite Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Makes selected vehicles and structures temporarily invisible.\n\nWarning: Harmful to non-cyborg infantry.\n\nHazmat Suits upgrade makes allies immune to damage and enemies take 50% damage. + Description: Reveals the targeted area for a short time. encyclopedia.power.airdrop: Inherits: ^SupportPowerPreview @@ -630,18 +592,19 @@ encyclopedia.power.airdrop: EncyclopediaExtras: Description: Dispatches cargo planes to air drop tanks at the target location. Units dependent on subfaction. -encyclopedia.power.hacksat: +encyclopedia.power.substrike: Inherits: ^SupportPowerPreview Buildable: - Icon: hacksat - Prerequisites: anyradar - BuildDuration: 4500 + Icon: substrike + Prerequisites: weap.td + BuildDuration: 12000 Tooltip: - Name: Hack Satellite + Name: Subterranean Strike Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Reveals the targeted area for a short time. + Description: Deploys a squad of infantry via Subterranean APC. + Subfaction: marked encyclopedia.power.infbomb: Inherits: ^SupportPowerPreview @@ -755,91 +718,105 @@ encyclopedia.power.confessorcabal: Description: Deploy a Confessor Cabal which is able to construct Idols of Kane that buffs allies and debuffs enemies.\n\nTarget infantry production structure to deploy. AdditionalInfo: Requires Zeal Covenant. -encyclopedia.power.substrike: +encyclopedia.power.clustermissile: Inherits: ^SupportPowerPreview Buildable: - Icon: substrike - Prerequisites: weap.td - BuildDuration: 12000 + Icon: clustermissile + IconPalette: chrometd + Prerequisites: tmpl + BuildDuration: 10500 Tooltip: - Name: Subterranean Strike + Name: Cluster Missile Encyclopedia: Category: Nod/Support Powers EncyclopediaExtras: - Description: Deploys a squad of infantry via Subterranean APC. - Subfaction: marked + Description: Launches a Cluster Missile which deals heavy damage at the target location. -# Scrin Support Powers -encyclopedia.power.rift: +encyclopedia.power.tibstealth: Inherits: ^SupportPowerPreview Buildable: - Icon: riftpower - Prerequisites: rfgn + Icon: invis + IconPalette: chrometd + Prerequisites: sgen + BuildDuration: 4500 + Tooltip: + Name: Tiberium Stealth + Encyclopedia: + Category: Nod/Support Powers + EncyclopediaExtras: + Description: Makes selected vehicles and structures temporarily invisible.\n\nWarning: Harmful to non-cyborg infantry.\n\nHazmat Suits upgrade makes allies immune to damage and enemies take 50% damage. + +encyclopedia.power.chemmissile: + Inherits: ^SupportPowerPreview + Buildable: + Icon: chemmissile + IconPalette: chrometd + Prerequisites: mslo.nod BuildDuration: 13500 Tooltip: - Name: Rift + Name: Chemical Missile Encyclopedia: - Category: Scrin/Support Powers + Category: Nod/Support Powers EncyclopediaExtras: - Description: Initiate a Rift which deals heavy damage over time to a large area. + Description: Launches an deadly Chemical Missile. Deals heavy damage and creates toxic clouds which are extremely harmful to infantry.\n -encyclopedia.power.suppression: +# Scrin Support Powers + + +encyclopedia.power.resourcescan: Inherits: ^SupportPowerPreview Buildable: - Icon: spresspower - Prerequisites: mani - BuildDuration: 4500 + Icon: rescanpower + Prerequisites: anyradar + BuildDuration: 7500 Tooltip: - Name: Suppression + Name: Resource Scan Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Applies a suppression field to the target area, slowing unit movement and rate of fire. + Description: Reveals the area surrounding all resources on the map. -encyclopedia.power.gateway: +encyclopedia.power.ichorspike: Inherits: ^SupportPowerPreview Buildable: - Icon: gateway + Icon: ichorspike IconPalette: chromes - Prerequisites: scrt BuildDuration: 6000 Tooltip: - Name: Gateway + Name: Ichor Spike Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Creates a Gateway at the target location. Acts as the exit for any\n targeted infantry or vehicle production structure.\n\nRequires target location to be within vision. - AdditionalInfo: Requires Rebel Allegiance. + Description: Creates an Ichor Spike at the target location. Resources within its area of influence grow and spread faster.\n\nAlso empowers units with Resource Conversion upgrade. + AdditionalInfo: Requires Loyalist Allegiance. -encyclopedia.power.anathemapower: +encyclopedia.power.colonyspike: Inherits: ^SupportPowerPreview Buildable: - Icon: anathema + Icon: colonyspike IconPalette: chromes - Prerequisites: scrt - BuildDuration: 3000 + BuildDuration: 7500 Tooltip: - Name: Anathema + Name: Colony Spike Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Targeted vehicle greatly increases in power over time. After 30 seconds the unit explodes. - AdditionalInfo: Requires Malefic Allegiance. + Description: Creates a Colony Spike at the target location. Enables production of non-defensive structures.\n\nWhen fully charged allows non-defensive structures to be built nearby.\n\nMust be built within range of a Colony Platform. + AdditionalInfo: Requires Rebel Allegiance. -encyclopedia.power.owrath: +encyclopedia.power.voidspike: Inherits: ^SupportPowerPreview Buildable: - Icon: owrath + Icon: voidspike IconPalette: chromes - Prerequisites: scrt - BuildDuration: 10500 + BuildDuration: 6000 Tooltip: - Name: Overlord's Wrath + Name: Voidspike Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: A Tiberium meteor strikes the target location dealing heavy damage and creating a patch of Tiberium. - AdditionalInfo: Requires Loyalist Allegiance. + Description: Creates a Voidspike at the target location. Gradually transforms nearby resources into Black Tiberium which is devoid of most of its useful properties.\n\nDamages nearby enemy units. + AdditionalInfo: Requires Malefic Allegiance. encyclopedia.power.ionsurge: Inherits: ^SupportPowerPreview @@ -913,47 +890,50 @@ encyclopedia.power.greatercoalescence: Description: Spawns a controllable biomass which heals nearby allies and sustains itself by feeding off enemies.\n\nFeeding from power plants will shut them down temporarily. Subfaction: collector -encyclopedia.power.ichorspike: +encyclopedia.power.gateway: Inherits: ^SupportPowerPreview Buildable: - Icon: ichorspike + Icon: gateway IconPalette: chromes + Prerequisites: scrt BuildDuration: 6000 Tooltip: - Name: Ichor Spike + Name: Gateway Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Creates an Ichor Spike at the target location. Resources within its area of influence grow and spread faster.\n\nAlso empowers units with Resource Conversion upgrade. - AdditionalInfo: Requires Loyalist Allegiance. + Description: Creates a Gateway at the target location. Acts as the exit for any\n targeted infantry or vehicle production structure.\n\nRequires target location to be within vision. + AdditionalInfo: Requires Rebel Allegiance. -encyclopedia.power.colonyspike: +encyclopedia.power.anathemapower: Inherits: ^SupportPowerPreview Buildable: - Icon: colonyspike + Icon: anathema IconPalette: chromes - BuildDuration: 7500 + Prerequisites: scrt + BuildDuration: 3000 Tooltip: - Name: Colony Spike + Name: Anathema Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Creates a Colony Spike at the target location. Enables production of non-defensive structures.\n\nWhen fully charged allows non-defensive structures to be built nearby.\n\nMust be built within range of a Colony Platform. - AdditionalInfo: Requires Rebel Allegiance. + Description: Targeted vehicle greatly increases in power over time. After 30 seconds the unit explodes. + AdditionalInfo: Requires Malefic Allegiance. -encyclopedia.power.voidspike: +encyclopedia.power.owrath: Inherits: ^SupportPowerPreview Buildable: - Icon: voidspike + Icon: owrath IconPalette: chromes - BuildDuration: 6000 + Prerequisites: scrt + BuildDuration: 10500 Tooltip: - Name: Voidspike + Name: Overlord's Wrath Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Creates a Voidspike at the target location. Gradually transforms nearby resources into Black Tiberium which is devoid of most of its useful properties.\n\nDamages nearby enemy units. - AdditionalInfo: Requires Malefic Allegiance. + Description: A Tiberium meteor strikes the target location dealing heavy damage and creating a patch of Tiberium. + AdditionalInfo: Requires Loyalist Allegiance. encyclopedia.power.fleetrecall: Inherits: ^SupportPowerPreview @@ -969,18 +949,57 @@ encyclopedia.power.fleetrecall: EncyclopediaExtras: Description: Recalls all selected Scrin fleet vessels back to the Signal Transmitter.\n\nApplies to: Mothership, Planetary Assault Carrier, Devastator -encyclopedia.power.resourcescan: +encyclopedia.power.suppression: Inherits: ^SupportPowerPreview Buildable: - Icon: rescanpower - Prerequisites: anyradar - BuildDuration: 7500 + Icon: spresspower + Prerequisites: mani + BuildDuration: 4500 Tooltip: - Name: Resource Scan + Name: Suppression Encyclopedia: Category: Scrin/Support Powers EncyclopediaExtras: - Description: Reveals the area surrounding all resources on the map. + Description: Applies a suppression field to the target area, slowing unit movement and rate of fire. + +encyclopedia.power.rift: + Inherits: ^SupportPowerPreview + Buildable: + Icon: riftpower + Prerequisites: rfgn + BuildDuration: 13500 + Tooltip: + Name: Rift + Encyclopedia: + Category: Scrin/Support Powers + EncyclopediaExtras: + Description: Initiate a Rift which deals heavy damage over time to a large area. + +encyclopedia.power.forceshield: + Inherits: ^SupportPowerPreview + Buildable: + Icon: forceshield + Prerequisites: techcenter.any + BuildDuration: 7500 + Tooltip: + Name: Force Shield + Encyclopedia: + Category: Other/Shared Support Powers + EncyclopediaExtras: + Description: Makes selected friendly structures temporarily invulnerable.\n\nWarning: Causes power failure. + +encyclopedia.power.sendcash: + Inherits: ^SupportPowerPreview + Buildable: + Icon: sendcash + Prerequisites: anyrefinery + BuildDuration: 0 + Tooltip: + Name: Send Cash + Encyclopedia: + Category: Other/Shared Support Powers + EncyclopediaExtras: + Description: Available in team games and co-op. Sends $1000 credits to targeted ally. # IFV Variants ^IFVVariantPreview: diff --git a/mods/ca/rules/infantry.yaml b/mods/ca/rules/infantry.yaml index ce976af162..f7fec0ffc5 100644 --- a/mods/ca/rules/infantry.yaml +++ b/mods/ca/rules/infantry.yaml @@ -4459,7 +4459,7 @@ ENLI: Types: Veterancy Prerequisites: barracks.upgraded Encyclopedia: - Category: Nod/Infantry + Category: Nod/Stolen Technology MORT.Cryo: Inherits: ^MortarInfantryBase diff --git a/mods/ca/rules/structures.yaml b/mods/ca/rules/structures.yaml index e0035ac13e..57a00927ad 100644 --- a/mods/ca/rules/structures.yaml +++ b/mods/ca/rules/structures.yaml @@ -2425,7 +2425,7 @@ SBAG: WithWallSpriteBody: Type: sandbag Encyclopedia: - Category: Allies/Defenses + Category: Allies/Defenses; GDI/Defenses FENC: Inherits: ^Wall diff --git a/mods/ca/rules/vehicles.yaml b/mods/ca/rules/vehicles.yaml index 978c4de695..9271621140 100644 --- a/mods/ca/rules/vehicles.yaml +++ b/mods/ca/rules/vehicles.yaml @@ -3803,10 +3803,21 @@ APC2: UpgradeAtActors: fix, rep, srep RequiresCondition: !mindcontrolled Encyclopedia: - Category: GDI/Vehicles; Nod/Vehicles + Category: GDI/Vehicles EncyclopediaExtras: Name: APC +APC2.Nod.Preview: + Inherits: APC2 + RenderSprites: + Image: apc2.nod + Buildable: + Queue: Encyclopedia + Encyclopedia: + Category: Nod/Vehicles + EncyclopediaExtras: + Subfaction: legion + APC2.NODAI: Inherits: APC2 Inherits@AIUNLOAD: ^AIUNLOAD @@ -4170,6 +4181,8 @@ LTNK.Laser: Selectable: Class: ltnk -ReplacedInQueue: + EncyclopediaExtras: + VariantOf: LTNK MTNK: Inherits: ^TankTD @@ -4316,7 +4329,7 @@ MTNK: BuildDuration: 100 RequiresCondition: !mindcontrolled Encyclopedia: - Category: GDI/Vehicles; Nod/Vehicles + Category: GDI/Vehicles MTNK.Drone: Inherits: MTNK @@ -4405,6 +4418,18 @@ MTNK.Drone: EncyclopediaExtras: Subfaction: arc +MTNK.Nod.Preview: + Inherits: MTNK + RenderSprites: + Image: mtnk.nod + Buildable: + Queue: Encyclopedia + BuildPaletteOrder: 101 + Encyclopedia: + Category: Nod/Vehicles + EncyclopediaExtras: + Subfaction: legion + MTNK.Laser: Inherits: MTNK RenderSprites: @@ -4442,6 +4467,7 @@ MTNK.Laser: Category: Nod/Vehicles EncyclopediaExtras: Subfaction: legion + VariantOf: MTNK.Nod.Preview MDRN: Inherits: ^Vehicle @@ -7386,9 +7412,10 @@ RECK: SpeedMultiplier@TigerGuard: Modifier: 90 Encyclopedia: - Category: Nod/Vehicles + Category: Nod/Stolen Technology EncyclopediaExtras: AdditionalInfo: Stolen from Allied War Factory. + -Name: TITN: Inherits: ^TankTD @@ -9302,7 +9329,7 @@ CYCP: Actor: CYCP.Husk RequiresCondition: !being-warped Encyclopedia: - Category: Nod/Vehicles + Category: Nod/Stolen Technology EncyclopediaExtras: AdditionalInfo: Stolen from Soviet War Factory. @@ -9384,7 +9411,7 @@ BASI: Condition: inherited-cloak RequiresCondition: hidden || invisibility Encyclopedia: - Category: Nod/Vehicles + Category: Nod/Stolen Technology EncyclopediaExtras: AdditionalInfo: Stolen from GDI Weapons Factory. @@ -9448,7 +9475,7 @@ MANT: GuardsSelection: ValidTargets: Infantry, Vehicle Encyclopedia: - Category: Nod/Vehicles + Category: Nod/Stolen Technology EncyclopediaExtras: AdditionalInfo: Stolen from Nod Airstrip. @@ -9519,7 +9546,7 @@ VIPR: TargetTypes: Water, Ship RequiresCondition: onwater Encyclopedia: - Category: Nod/Vehicles + Category: Nod/Stolen Technology EncyclopediaExtras: AdditionalInfo: Stolen from Scrin Warp Sphere. From d6b3b6372ba43d750dec82b70a42d463ae57209c Mon Sep 17 00:00:00 2001 From: darkademic <41052878+darkademic@users.noreply.github.com> Date: Mon, 5 Jan 2026 22:16:51 +0000 Subject: [PATCH 27/28] Emancipation & Duality fixes. --- .../ca27-emancipation-coop/emancipation-coop-rules.yaml | 4 ++++ .../ca27-emancipation-coop/emancipation-coop.lua | 2 +- .../ca/missions/main-campaign/ca28-duality/duality-rules.yaml | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mods/ca/missions/coop-campaign/ca27-emancipation-coop/emancipation-coop-rules.yaml b/mods/ca/missions/coop-campaign/ca27-emancipation-coop/emancipation-coop-rules.yaml index c8c1ad8fb0..2377c490a2 100644 --- a/mods/ca/missions/coop-campaign/ca27-emancipation-coop/emancipation-coop-rules.yaml +++ b/mods/ca/missions/coop-campaign/ca27-emancipation-coop/emancipation-coop-rules.yaml @@ -4,3 +4,7 @@ World: LobbyMissionInfo@InitialObjectives: Prefix: Initial Objectives Text: • Liberate the five enslaved GDI bases by killing Masterminds.\n• Eliminate all Scrin forces.\n• Avoid killing enslaved GDI units. + +MDRN: + Attachable: + OnDetachBehavior: None diff --git a/mods/ca/missions/coop-campaign/ca27-emancipation-coop/emancipation-coop.lua b/mods/ca/missions/coop-campaign/ca27-emancipation-coop/emancipation-coop.lua index 2333b9042e..8176b1ae95 100644 --- a/mods/ca/missions/coop-campaign/ca27-emancipation-coop/emancipation-coop.lua +++ b/mods/ca/missions/coop-campaign/ca27-emancipation-coop/emancipation-coop.lua @@ -31,7 +31,7 @@ AfterTick = function() end FreeSlaves = function(slaves) - local baseSlaves = Utils.Where(slaves, function(s) return IsBaseTransferActor(a) end) + local baseSlaves = Utils.Where(slaves, function(s) return IsBaseTransferActor(s) end) local otherSlaves = Utils.Where(slaves, function(s) return s.HasProperty("Move") and not IsHarvester(s) end) local firstActivePlayer = GetFirstActivePlayer() diff --git a/mods/ca/missions/main-campaign/ca28-duality/duality-rules.yaml b/mods/ca/missions/main-campaign/ca28-duality/duality-rules.yaml index d102606dcc..68d8c843d4 100644 --- a/mods/ca/missions/main-campaign/ca28-duality/duality-rules.yaml +++ b/mods/ca/missions/main-campaign/ca28-duality/duality-rules.yaml @@ -100,6 +100,7 @@ PDGY: Condition: activated Armament@PRIMARY: Weapon: ProdigyZap + -MindController: WORMHOLE: -Targetable: From df16cf90c36c7cfff15848814bd8ce3fab76c2ef Mon Sep 17 00:00:00 2001 From: Paul Schultz Date: Mon, 5 Jan 2026 14:03:41 -0600 Subject: [PATCH 28/28] feat(i18n): add fluent translation support for prologue missions Signed-off-by: Paul Schultz --- .../Widgets/Logic/MissionBrowserLogicCA.cs | 13 +- mods/ca/fluent/coop.ftl | 3 + mods/ca/fluent/lua.ftl | 18 + .../main-campaign/ca-prologue-01/en.ftl | 31 ++ .../main-campaign/ca-prologue-01/map.yaml | 2 + .../ca-prologue-01/relativity-rules.yaml | 2 +- .../ca-prologue-01/relativity.lua | 31 +- .../main-campaign/ca-prologue-02/en.ftl | 18 + .../main-campaign/ca-prologue-02/map.yaml | 2 + .../ca-prologue-02/reprisal-rules.yaml | 2 +- .../main-campaign/ca-prologue-02/reprisal.lua | 25 +- .../ca-prologue-03/detachment-rules.yaml | 2 +- .../ca-prologue-03/detachment.lua | 38 +- .../main-campaign/ca-prologue-03/en.ftl | 27 ++ .../main-campaign/ca-prologue-03/map.yaml | 2 + .../main-campaign/ca-prologue-04/en.ftl | 16 + .../ca-prologue-04/juncture-rules.yaml | 2 +- .../main-campaign/ca-prologue-04/juncture.lua | 8 +- .../main-campaign/ca-prologue-04/map.yaml | 2 + mods/ca/mod.yaml | 2 + mods/ca/scripts/campaign.lua | 410 +++++++++--------- mods/ca/scripts/coop.lua | 23 +- 22 files changed, 418 insertions(+), 261 deletions(-) create mode 100644 mods/ca/fluent/coop.ftl create mode 100644 mods/ca/fluent/lua.ftl create mode 100644 mods/ca/missions/main-campaign/ca-prologue-01/en.ftl create mode 100644 mods/ca/missions/main-campaign/ca-prologue-02/en.ftl create mode 100644 mods/ca/missions/main-campaign/ca-prologue-03/en.ftl create mode 100644 mods/ca/missions/main-campaign/ca-prologue-04/en.ftl diff --git a/OpenRA.Mods.CA/Widgets/Logic/MissionBrowserLogicCA.cs b/OpenRA.Mods.CA/Widgets/Logic/MissionBrowserLogicCA.cs index c8a817a425..c6b1c5895a 100644 --- a/OpenRA.Mods.CA/Widgets/Logic/MissionBrowserLogicCA.cs +++ b/OpenRA.Mods.CA/Widgets/Logic/MissionBrowserLogicCA.cs @@ -352,7 +352,14 @@ void SelectMap(MapPreview preview) infoVideo = missionData.BackgroundVideo; infoVideoVisible = infoVideo != null; - var briefingText = missionData.Briefing?.Replace("\\n", "\n"); + // Try to translate the briefing as a Fluent key, fall back to raw text + var rawBriefing = missionData.Briefing; + string briefingText; + if (!string.IsNullOrEmpty(rawBriefing) && preview.TryGetMessage(rawBriefing, out var translated)) + briefingText = translated; + else + briefingText = rawBriefing?.Replace("\\n", "\n"); + var briefing = WidgetUtils.WrapText(briefingText, description.Bounds.Width, descriptionFont); var height = descriptionFont.Measure(briefing).Y; Game.RunAfterTick(() => @@ -401,7 +408,7 @@ void RebuildOptions() var yOffset = 0; foreach (var option in allOptions.Where(o => o is LobbyBooleanOption)) { - if (!missionOptions.ContainsKey(option.Id) || !( new[] { "True", "False" }.Contains(missionOptions[option.Id]))) + if (!missionOptions.ContainsKey(option.Id) || !(new[] { "True", "False" }.Contains(missionOptions[option.Id]))) missionOptions[option.Id] = option.DefaultValue; if (checkboxColumns.Count == 0) @@ -603,7 +610,7 @@ void LoadSavedOptions() var savedOptionsFileContents = File.ReadAllText(savedOptionsFilePath); var savedOptions = JsonConvert.DeserializeObject>(savedOptionsFileContents); - foreach(KeyValuePair option in savedOptions) + foreach (KeyValuePair option in savedOptions) missionOptions[option.Key] = option.Value; } catch diff --git a/mods/ca/fluent/coop.ftl b/mods/ca/fluent/coop.ftl new file mode 100644 index 0000000000..30a0cd9321 --- /dev/null +++ b/mods/ca/fluent/coop.ftl @@ -0,0 +1,3 @@ +## coop.lua shared strings +high-command = High Command +resource-spawners-deleted = All resource spawners are deleted now. Good luck! diff --git a/mods/ca/fluent/lua.ftl b/mods/ca/fluent/lua.ftl new file mode 100644 index 0000000000..fccd86d79c --- /dev/null +++ b/mods/ca/fluent/lua.ftl @@ -0,0 +1,18 @@ +## shared strings +objective-failed = Objective failed +objective-completed = Objective completed + +primary = Primary +secondary = Secondary + +new-primary-objective = New primary objective +new-secondary-objective = New secondary objective + +## Common message categories +notification = Notification +tip = Tip +warning = Warning + +## Common notifications +reinforcements-arrived = Reinforcements have arrived. +signal-flare-detected = Signal flare detected. Press [{ $hotkey }] to view location. diff --git a/mods/ca/missions/main-campaign/ca-prologue-01/en.ftl b/mods/ca/missions/main-campaign/ca-prologue-01/en.ftl new file mode 100644 index 0000000000..819b280c94 --- /dev/null +++ b/mods/ca/missions/main-campaign/ca-prologue-01/en.ftl @@ -0,0 +1,31 @@ +## Briefing +briefing = The Soviets have captured one of our most important scientists, Professor Einstein. + + He is currently being held inside a Soviet base not far from the border. It is crucial that he be rescued before he can be transported deeper into Soviet territory. + + Using a small task force led by our special commando Tanya Adams, rescue Einstein and bring him to the extraction point. + + The Soviet base is protected by Tesla Coils. Cutting their power supply will render them inoperative. + +## Objectives +find-einstein = Find Einstein. +tanya-survive = Tanya must survive. +einstein-survive = Einstein must survive. +destroy-sub-pen = Destroy the Soviet Sub Pen. +extract-einstein = Bring Einstein to the extraction point and board + the transport helicopter. + +## Notifications +allied-cruisers-arrived = Allied cruisers have arrived. +unidentified-allied-units = Unidentified Allied units detected. + +## Dialogue +cruiser-captain = Cruiser Captain +encountering-soviets = Encountering Soviet naval presence! We're under heavy fire! +waters-cleared = This is impossible! These waters were cleared! + +unknown-speaker = Unknown +temporal-disturbance = Another temporal disturbance.. Well, we can work this out later. For now, we are at your disposal commander. + +## Tips +tooltip-tip = Information is displayed in the bottom right of the screen if any single unit or structure is selected, listing its strengths and weaknesses (as long as Selected Unit Tooltip is enabled in settings). diff --git a/mods/ca/missions/main-campaign/ca-prologue-01/map.yaml b/mods/ca/missions/main-campaign/ca-prologue-01/map.yaml index 279958ef8d..f05290ece8 100644 --- a/mods/ca/missions/main-campaign/ca-prologue-01/map.yaml +++ b/mods/ca/missions/main-campaign/ca-prologue-01/map.yaml @@ -522,3 +522,5 @@ Actors: Rules: ca|rules/custom/campaign-rules.yaml, ca|rules/custom/campaign-tooltips.yaml, ca|rules/custom/two-tone-nod.yaml, ca|rules/custom/disable-coalitions.yaml, relativity-rules.yaml Weapons: ca|weapons/custom/campaign.yaml, relativity-weapons.yaml + +FluentMessages: ca|fluent/lua.ftl, en.ftl diff --git a/mods/ca/missions/main-campaign/ca-prologue-01/relativity-rules.yaml b/mods/ca/missions/main-campaign/ca-prologue-01/relativity-rules.yaml index 7b0b022c80..97da9b7ee5 100644 --- a/mods/ca/missions/main-campaign/ca-prologue-01/relativity-rules.yaml +++ b/mods/ca/missions/main-campaign/ca-prologue-01/relativity-rules.yaml @@ -6,7 +6,7 @@ World: LuaScript: Scripts: campaign.lua, relativity.lua MissionData: - Briefing: The Soviets have captured one of our most important scientists, Professor Einstein.\n\nHe is currently being held inside a Soviet base not far from the border. It is crucial that he be rescued before he can be transported deeper into Soviet territory.\n\nUsing a small task force led by our special commando Tanya Adams, rescue Einstein and bring him to the extraction point.\n\nThe Soviet base is protected by Tesla Coils. Cutting their power supply will render them inoperative. + Briefing: briefing -ScriptLobbyDropdown@DIFFICULTY: ScriptLobbyDropdown@PROLOGUEDIFFICULTY: ID: prologuedifficulty diff --git a/mods/ca/missions/main-campaign/ca-prologue-01/relativity.lua b/mods/ca/missions/main-campaign/ca-prologue-01/relativity.lua index 187fe3b592..466ed27c34 100644 --- a/mods/ca/missions/main-campaign/ca-prologue-01/relativity.lua +++ b/mods/ca/missions/main-campaign/ca-prologue-01/relativity.lua @@ -27,9 +27,9 @@ WorldLoaded = function() SetupPlayers() InitObjectives(Greece) - FindEinsteinObjective = Greece.AddObjective("Find Einstein.") - TanyaSurviveObjective = Greece.AddObjective("Tanya must survive.") - EinsteinSurviveObjective = Greece.AddObjective("Einstein must survive.") + FindEinsteinObjective = Greece.AddObjective(UserInterface.GetFluentMessage("find-einstein")) + TanyaSurviveObjective = Greece.AddObjective(UserInterface.GetFluentMessage("tanya-survive")) + EinsteinSurviveObjective = Greece.AddObjective(UserInterface.GetFluentMessage("einstein-survive")) RunInitialActivities() @@ -39,7 +39,8 @@ WorldLoaded = function() Trigger.OnAllKilled(LabGuardsTeam, LabGuardsKilled) - Trigger.AfterDelay(DateTime.Seconds(5), function() Actor.Create("camera", true, { Owner = Greece, Location = BaseCameraPoint.Location }) end) + Trigger.AfterDelay(DateTime.Seconds(5), + function() Actor.Create("camera", true, { Owner = Greece, Location = BaseCameraPoint.Location }) end) Camera.Position = InsertionLZ.CenterPosition @@ -52,7 +53,7 @@ WorldLoaded = function() Trigger.OnKilled(SubPen, function(self, killer) if ObjectiveDestroySubPen == nil then - ObjectiveDestroySubPen = Greece.AddObjective("Destroy the Soviet Sub Pen.") + ObjectiveDestroySubPen = Greece.AddObjective(UserInterface.GetFluentMessage("destroy-sub-pen")) end if not Greece.IsObjectiveCompleted(ObjectiveDestroySubPen) then @@ -64,7 +65,7 @@ WorldLoaded = function() end) Trigger.AfterDelay(DateTime.Seconds(30), function() - Tip("Information is displayed in the bottom right of the screen if any single unit or structure is selected, listing its strengths and weaknesses (as long as Selected Unit Tooltip is enabled in settings).") + Tip("tooltip-tip") end) AfterWorldLoaded() @@ -164,7 +165,7 @@ end SendCruisers = function() CruisersArrived = true - Notification("Allied cruisers have arrived.") + Notification("allied-cruisers-arrived") MediaCA.PlaySound(MissionDir .. "/r_alliedcruisers.aud", 2); Actor.Create("camera", true, { Owner = Greece, Location = CruiserCameraPoint.Location }) Beacon.New(Greece, CruiserBeacon.CenterPosition) @@ -177,15 +178,18 @@ SendCruisers = function() i = i + 1 end) + local cruiserCaptain = UserInterface.GetFluentMessage("cruiser-captain") + Trigger.AfterDelay(DateTime.Seconds(4), function() - Media.DisplayMessage("Encountering Soviet naval presence! We're under heavy fire!", "Cruiser Captain", HSLColor.FromHex("99ACF2")) + Media.DisplayMessage(UserInterface.GetFluentMessage("encountering-soviets"), cruiserCaptain, + HSLColor.FromHex("99ACF2")) MediaCA.PlaySound(MissionDir .. "/encountering.aud", 2) Trigger.AfterDelay(AdjustTimeForGameSpeed(DateTime.Seconds(4)), function() - Media.DisplayMessage("This is impossible! These waters were cleared!", "Cruiser Captain", HSLColor.FromHex("99ACF2")) + Media.DisplayMessage(UserInterface.GetFluentMessage("waters-cleared"), cruiserCaptain, HSLColor.FromHex("99ACF2")) MediaCA.PlaySound(MissionDir .. "/impossible.aud", 2) Trigger.AfterDelay(DateTime.Seconds(2), function() if not SubPen.IsDead and ObjectiveDestroySubPen == nil then - ObjectiveDestroySubPen = Greece.AddObjective("Destroy the Soviet Sub Pen.") + ObjectiveDestroySubPen = Greece.AddObjective(UserInterface.GetFluentMessage("destroy-sub-pen")) Beacon.New(Greece, SubPen.CenterPosition) Trigger.AfterDelay(AdjustTimeForGameSpeed(DateTime.Seconds(4)), function() PrismsArrived = true @@ -198,11 +202,12 @@ SendCruisers = function() Actor.Create("ptnk", true, { Owner = England, Location = PrismSpawn2.Location, Facing = Angle.East }) Actor.Create("ptnk", true, { Owner = England, Location = PrismSpawn3.Location, Facing = Angle.East }) Trigger.AfterDelay(DateTime.Seconds(2), function() - Notification("Unidentified Allied units detected.") + Notification("unidentified-allied-units") MediaCA.PlaySound(MissionDir .. "/r_unidentified.aud", 2) Trigger.AfterDelay(AdjustTimeForGameSpeed(DateTime.Seconds(3)), function() - Media.DisplayMessage("Another temporal disturbance.. Well, we can work this out later. For now, we are at your disposal commander.", "Unknown", HSLColor.FromHex("99ACF2")) + Media.DisplayMessage(UserInterface.GetFluentMessage("temporal-disturbance"), + UserInterface.GetFluentMessage("unknown-speaker"), HSLColor.FromHex("99ACF2")) MediaCA.PlaySound(MissionDir .. "/disturbance.aud", 2) Trigger.AfterDelay(AdjustTimeForGameSpeed(DateTime.Seconds(5)), function() local prismTanks = England.GetActorsByType("ptnk") @@ -243,7 +248,7 @@ CreateEinstein = function() Einstein = Actor.Create(EinsteinType, true, { Location = EinsteinSpawnPoint.Location, Owner = Greece }) Einstein.Scatter() Trigger.OnKilled(Einstein, RescueFailed) - ExtractObjective = Greece.AddObjective("Bring Einstein to the extraction point and board\nthe transport helicopter.") + ExtractObjective = Greece.AddObjective(UserInterface.GetFluentMessage("extract-einstein")) Trigger.AfterDelay(DateTime.Seconds(1), function() PlaySpeechNotificationToMissionPlayers("TargetFreed") end) end diff --git a/mods/ca/missions/main-campaign/ca-prologue-02/en.ftl b/mods/ca/missions/main-campaign/ca-prologue-02/en.ftl new file mode 100644 index 0000000000..89f58c03e9 --- /dev/null +++ b/mods/ca/missions/main-campaign/ca-prologue-02/en.ftl @@ -0,0 +1,18 @@ +## Briefing +briefing = While we extracted all the information we needed from Einstein, Comrade Stalin was most displeased to learn of his escape. + + The traitor who informed the Allies of the scientist's whereabouts has been identified, and Stalin demands retribution. + + Go to the village the traitor calls home. Wipe it out. Leave no survivors. + + The Allies have set up a base nearby, to protect the village while they prepare to evacuate it. Once the village is destroyed, destroy the Allied base. + +## Objectives +wipe-out-village = Wipe out the village. +destroy-allied-base = Destroy the Allied base. + +## Dialogue +tesla-tank = Tesla Tank +greetings = Greetings Comrades! The Soviet Empire truly knows no boundaries! +unknown-speaker = Unknown +doubts = We understand that Comrade Stalin has his doubts about our agreement. We hope these gifts will put his mind at ease. diff --git a/mods/ca/missions/main-campaign/ca-prologue-02/map.yaml b/mods/ca/missions/main-campaign/ca-prologue-02/map.yaml index e28ac5ed09..ef83acae49 100644 --- a/mods/ca/missions/main-campaign/ca-prologue-02/map.yaml +++ b/mods/ca/missions/main-campaign/ca-prologue-02/map.yaml @@ -998,3 +998,5 @@ Actors: Rules: ca|rules/custom/campaign-rules.yaml, ca|rules/custom/campaign-tooltips.yaml, ca|rules/custom/two-tone-nod.yaml, ca|rules/custom/disable-doctrines.yaml, reprisal-rules.yaml Weapons: ca|weapons/custom/campaign.yaml + +FluentMessages: ca|fluent/lua.ftl, en.ftl diff --git a/mods/ca/missions/main-campaign/ca-prologue-02/reprisal-rules.yaml b/mods/ca/missions/main-campaign/ca-prologue-02/reprisal-rules.yaml index dc19922065..f88f3f80c1 100644 --- a/mods/ca/missions/main-campaign/ca-prologue-02/reprisal-rules.yaml +++ b/mods/ca/missions/main-campaign/ca-prologue-02/reprisal-rules.yaml @@ -6,7 +6,7 @@ World: LuaScript: Scripts: campaign.lua, reprisal.lua MissionData: - Briefing: While we extracted all the information we needed from Einstein, Comrade Stalin was most displeased to learn of his escape.\n\nThe traitor who informed the Allies of the scientist's whereabouts has been identified, and Stalin demands retribution.\n\nGo to the village the traitor calls home. Wipe it out. Leave no survivors.\n\nThe Allies have set up a base nearby, to protect the village while they prepare to evacuate it. Once the village is destroyed, destroy the Allied base. + Briefing: briefing -ScriptLobbyDropdown@DIFFICULTY: ScriptLobbyDropdown@PROLOGUEDIFFICULTY: ID: prologuedifficulty diff --git a/mods/ca/missions/main-campaign/ca-prologue-02/reprisal.lua b/mods/ca/missions/main-campaign/ca-prologue-02/reprisal.lua index 50843ba897..56ff281fa8 100644 --- a/mods/ca/missions/main-campaign/ca-prologue-02/reprisal.lua +++ b/mods/ca/missions/main-campaign/ca-prologue-02/reprisal.lua @@ -7,9 +7,12 @@ AlliedAttackPaths = { { EastAttackRally.Location, SovietBase.Location } } -TeslaTrigger = { TeslaTrigger1.Location, TeslaTrigger2.Location, TeslaTrigger3.Location, TeslaTrigger4.Location, TeslaTrigger5.Location, TeslaTrigger6.Location, TeslaTrigger7.Location } -TeslaTriggerWest = { TeslaTriggerWest1.Location, TeslaTriggerWest2.Location, TeslaTriggerWest3.Location, TeslaTriggerWest4.Location, TeslaTriggerWest5.Location, TeslaTriggerWest6.Location, TeslaTriggerWest7.Location, - TeslaTriggerWest8.Location, TeslaTriggerWest9.Location, TeslaTriggerWest10.Location, TeslaTriggerWest11.Location, TeslaTriggerWest12.Location, TeslaTriggerWest13.Location, TeslaTriggerWest14.Location, +TeslaTrigger = { TeslaTrigger1.Location, TeslaTrigger2.Location, TeslaTrigger3.Location, TeslaTrigger4.Location, + TeslaTrigger5.Location, TeslaTrigger6.Location, TeslaTrigger7.Location } +TeslaTriggerWest = { TeslaTriggerWest1.Location, TeslaTriggerWest2.Location, TeslaTriggerWest3.Location, + TeslaTriggerWest4.Location, TeslaTriggerWest5.Location, TeslaTriggerWest6.Location, TeslaTriggerWest7.Location, + TeslaTriggerWest8.Location, TeslaTriggerWest9.Location, TeslaTriggerWest10.Location, TeslaTriggerWest11.Location, + TeslaTriggerWest12.Location, TeslaTriggerWest13.Location, TeslaTriggerWest14.Location, TeslaTriggerWest15.Location, TeslaTriggerWest16.Location } Squads = { @@ -47,19 +50,21 @@ WorldLoaded = function() InitObjectives(USSR) InitGreece() - ObjectiveWipeOutVillage = USSR.AddObjective("Wipe out the village.") - ObjectiveDestroyBase = USSR.AddObjective("Destroy the Allied base.") + ObjectiveWipeOutVillage = USSR.AddObjective(UserInterface.GetFluentMessage("wipe-out-village")) + ObjectiveDestroyBase = USSR.AddObjective(UserInterface.GetFluentMessage("destroy-allied-base")) Trigger.AfterDelay(DateTime.Seconds(2), function() PlaySpeechNotificationToMissionPlayers("ReinforcementsArrived") - Notification("Reinforcements have arrived.") + Notification("reinforcements-arrived") DoMcvArrival() end) Trigger.AfterDelay(DateTime.Minutes(1), function() local villageFlare = Actor.Create("flare", true, { Owner = USSR, Location = VillageCenter.Location }) PlaySpeechNotificationToMissionPlayers("SignalFlare") - Notification("Signal flare detected. Press [" .. UtilsCA.Hotkey("ToLastEvent") .. "] to view location.") + Media.DisplayMessage( + UserInterface.GetFluentMessage("signal-flare-detected", { ["hotkey"] = UtilsCA.Hotkey("ToLastEvent") }), + UserInterface.GetFluentMessage("notification"), HSLColor.FromHex("1E90FF")) Beacon.New(USSR, VillageCenter.CenterPosition) Trigger.AfterDelay(DateTime.Minutes(5), villageFlare.Destroy) end) @@ -157,11 +162,13 @@ WarpInTeslaTanks = function(TankLocation1, TankLocation2, EffectLocation) Actor.Create("ttnk", true, { Owner = USSR, Location = TankLocation1, Facing = Angle.South }) Actor.Create("ttnk", true, { Owner = USSR, Location = TankLocation2, Facing = Angle.South }) Trigger.AfterDelay(DateTime.Seconds(2), function() - Media.DisplayMessage("Greetings Comrades! The Soviet Empire truly knows no boundaries!", "Tesla Tank", HSLColor.FromHex("FF0000")) + Media.DisplayMessage(UserInterface.GetFluentMessage("greetings"), UserInterface.GetFluentMessage("tesla-tank"), + HSLColor.FromHex("FF0000")) MediaCA.PlaySound(MissionDir .. "/greetings.aud", 2) Trigger.AfterDelay(AdjustTimeForGameSpeed(DateTime.Seconds(6)), function() - Media.DisplayMessage("We understand that Comrade Stalin has his doubts about our agreement. We hope these gifts will put his mind at ease.", "Unknown", HSLColor.FromHex("999999")) + Media.DisplayMessage(UserInterface.GetFluentMessage("doubts"), UserInterface.GetFluentMessage("unknown-speaker"), + HSLColor.FromHex("999999")) MediaCA.PlaySound(MissionDir .. "/doubts.aud", 2) end) end) diff --git a/mods/ca/missions/main-campaign/ca-prologue-03/detachment-rules.yaml b/mods/ca/missions/main-campaign/ca-prologue-03/detachment-rules.yaml index fdaa17d87d..2556eaceb8 100644 --- a/mods/ca/missions/main-campaign/ca-prologue-03/detachment-rules.yaml +++ b/mods/ca/missions/main-campaign/ca-prologue-03/detachment-rules.yaml @@ -6,7 +6,7 @@ World: LuaScript: Scripts: campaign.lua, detachment.lua MissionData: - Briefing: GDI forces have found themselves scattered and confused in unfamiliar surroundings, confronted by hostile soldiers.\n\nOther GDI units have been in radio contact after finding themselves in the same situation.\n\nGunfire can be heard echoing through the trees, and reports of casualties have already been heard over the radio. There is no time to lose.\n\nTake command, locate these other GDI troops, and find a route to safety. + Briefing: briefing -ScriptLobbyDropdown@DIFFICULTY: ScriptLobbyDropdown@PROLOGUEDIFFICULTY: ID: prologuedifficulty diff --git a/mods/ca/missions/main-campaign/ca-prologue-03/detachment.lua b/mods/ca/missions/main-campaign/ca-prologue-03/detachment.lua index 5bd935f20f..5b36025bbd 100644 --- a/mods/ca/missions/main-campaign/ca-prologue-03/detachment.lua +++ b/mods/ca/missions/main-campaign/ca-prologue-03/detachment.lua @@ -25,8 +25,8 @@ WorldLoaded = function() InitObjectives(GDI) InitUSSR() - ObjectiveLocateForces = GDI.AddObjective("Locate all GDI forces.") - ObjectiveExit = GDI.AddObjective("Find a safe exit route.") + ObjectiveLocateForces = GDI.AddObjective(UserInterface.GetFluentMessage("locate-forces")) + ObjectiveExit = GDI.AddObjective(UserInterface.GetFluentMessage("find-exit")) SetupReveals({ Reveal1, Reveal3, Reveal4 }) @@ -44,9 +44,9 @@ WorldLoaded = function() FirstRevealComplete = true local camera = Actor.Create("smallcamera", true, { Owner = GDI, Location = Reveal2.Location }) - if UtilsCA.FogEnabled() then - Tip("When an enemy structure is destroyed under the fog of war, it won't disappear until its location is revealed again. The explosion sound and screen shake can be used to verify its destruction.") - end + if UtilsCA.FogEnabled() then + Tip("fog-tip") + end Trigger.AfterDelay(DateTime.Seconds(4), function() camera.Destroy() @@ -59,15 +59,15 @@ WorldLoaded = function() if IsMissionPlayer(a.Owner) and not GroupsFound[g.Id] then Trigger.RemoveProximityTrigger(id) GroupsFound[g.Id] = true - Notification("GDI forces found.") - MediaCA.PlaySound(MissionDir .. "/gdifound.aud", 2) + Notification("gdi-found") + MediaCA.PlaySound(MissionDir .. "/gdifound.aud", 2) - if g.Id == 2 then - Trigger.AfterDelay(AdjustTimeForGameSpeed(DateTime.Seconds(2)), function() - Media.DisplayMessage("Thank god! You found us!.", "GDI Soldier", HSLColor.FromHex("F2CF74")) - MediaCA.PlaySound(MissionDir .. "/thankgod.aud", 1.5) - end) - end + if g.Id == 2 then + Trigger.AfterDelay(AdjustTimeForGameSpeed(DateTime.Seconds(2)), function() + Media.DisplayMessage(UserInterface.GetFluentMessage("thank-god"), UserInterface.GetFluentMessage("gdi-soldier"), HSLColor.FromHex("F2CF74")) + MediaCA.PlaySound(MissionDir .. "/thankgod.aud", 1.5) + end) + end local groupActors = Map.ActorsInCircle(g.Waypoint.CenterPosition, WDist.New(8 * 1024)); Utils.Do(groupActors, function(a) @@ -87,7 +87,7 @@ WorldLoaded = function() Trigger.AfterDelay(DateTime.Seconds(4), function() Actor.Create("flare", true, { Owner = GDI, Location = SignalFlare.Location }) PlaySpeechNotificationToMissionPlayers("SignalFlare") - Notification("Signal flare detected. Press [" .. UtilsCA.Hotkey("ToLastEvent") .. "] to view location.") + Media.DisplayMessage(UserInterface.GetFluentMessage("signal-flare-detected", { ["hotkey"] = UtilsCA.Hotkey("ToLastEvent") }), UserInterface.GetFluentMessage("notification"), HSLColor.FromHex("1E90FF")) Beacon.New(GDI, SignalFlare.CenterPosition) end) end @@ -96,11 +96,11 @@ WorldLoaded = function() end) Trigger.AfterDelay(DateTime.Seconds(4), function() - Media.DisplayMessage("Commander what's going on, where the hell are we?!", "GDI Soldier", HSLColor.FromHex("F2CF74")) + Media.DisplayMessage(UserInterface.GetFluentMessage("where-are-we"), UserInterface.GetFluentMessage("gdi-soldier"), HSLColor.FromHex("F2CF74")) Media.PlaySound(MissionDir .. "/wherearewe.aud") Trigger.AfterDelay(DateTime.Seconds(20), function() - Media.DisplayMessage("Come in, any GDI units, hostile troops have us pinned down.", "Radio", HSLColor.FromHex("F2CF74")) + Media.DisplayMessage(UserInterface.GetFluentMessage("pinned-down"), UserInterface.GetFluentMessage("radio"), HSLColor.FromHex("F2CF74")) MediaCA.PlaySoundAtPos(MissionDir .. "/pinned.aud", 2, Camera.Position + WVec.New(2560, 0, 0)) end) end) @@ -148,9 +148,9 @@ OncePerSecondChecks = function() Trigger.AfterDelay(DateTime.Seconds(2), function() Reinforcements.Reinforce(GDI, { "n1", "n2", "n1", "n2", "n1", "medi", "mtnk", "mtnk" }, { RescueSpawn.Location, RescueRally1.Location, RescueRally2.Location }) - Trigger.AfterDelay(AdjustTimeForGameSpeed(DateTime.Seconds(2)), function() - Media.DisplayMessage("Hold your fire, we're GDI! Damn, we thought we'd lost the whole company! We've got a base not far from here, we'll take you there.", "GDI Soldier", HSLColor.FromHex("F2CF74")) - MediaCA.PlaySound(MissionDir .. "/holdfire.aud", 2) + Trigger.AfterDelay(AdjustTimeForGameSpeed(DateTime.Seconds(2)), function() + Media.DisplayMessage(UserInterface.GetFluentMessage("hold-fire"), UserInterface.GetFluentMessage("gdi-soldier"), HSLColor.FromHex("F2CF74")) + MediaCA.PlaySound(MissionDir .. "/holdfire.aud", 2) Trigger.AfterDelay(DateTime.Seconds(12), function() GDI.MarkCompletedObjective(ObjectiveExit) diff --git a/mods/ca/missions/main-campaign/ca-prologue-03/en.ftl b/mods/ca/missions/main-campaign/ca-prologue-03/en.ftl new file mode 100644 index 0000000000..15cece9ccc --- /dev/null +++ b/mods/ca/missions/main-campaign/ca-prologue-03/en.ftl @@ -0,0 +1,27 @@ +## Briefing +briefing = GDI forces have found themselves scattered and confused in unfamiliar surroundings, confronted by hostile soldiers. + + Other GDI units have been in radio contact after finding themselves in the same situation. + + Gunfire can be heard echoing through the trees, and reports of casualties have already been heard over the radio. There is no time to lose. + + Take command, locate these other GDI troops, and find a route to safety. + +## Objectives +locate-forces = Locate all GDI forces. +find-exit = Find a safe exit route. + +## Notifications +gdi-found = GDI forces found. + +## Tips +fog-tip = When an enemy structure is destroyed under the fog of war, it won't disappear until its location is revealed again. The explosion sound and screen shake can be used to verify its destruction. + +## Dialogue +gdi-soldier = GDI Soldier +radio = Radio + +where-are-we = Commander what's going on, where the hell are we?! +pinned-down = Come in, any GDI units, hostile troops have us pinned down. +thank-god = Thank god! You found us! +hold-fire = Hold your fire, we're GDI! Damn, we thought we'd lost the whole company! We've got a base not far from here, we'll take you there. diff --git a/mods/ca/missions/main-campaign/ca-prologue-03/map.yaml b/mods/ca/missions/main-campaign/ca-prologue-03/map.yaml index 85745d79bc..b5b2beb63c 100644 --- a/mods/ca/missions/main-campaign/ca-prologue-03/map.yaml +++ b/mods/ca/missions/main-campaign/ca-prologue-03/map.yaml @@ -1181,3 +1181,5 @@ Actors: Rules: ca|rules/custom/campaign-rules.yaml, ca|rules/custom/campaign-tooltips.yaml, ca|rules/custom/two-tone-nod.yaml, detachment-rules.yaml Weapons: ca|weapons/custom/campaign.yaml + +FluentMessages: ca|fluent/lua.ftl, en.ftl diff --git a/mods/ca/missions/main-campaign/ca-prologue-04/en.ftl b/mods/ca/missions/main-campaign/ca-prologue-04/en.ftl new file mode 100644 index 0000000000..9d3dbb0df2 --- /dev/null +++ b/mods/ca/missions/main-campaign/ca-prologue-04/en.ftl @@ -0,0 +1,16 @@ +## Briefing +briefing = Critical components and supplies are being transported to a secret staging area. Few in the Brotherhood know the full details of this operation, but all will be revealed in time. + + GDI continue to threaten vital transportation routes, and you have been chosen to secure one such route in Egypt. + + GDI is blockading the Nile river with their fleet, and this is unacceptably delaying our progress. + + Establish a foothold, then your objective is to destroy the GDI anti-aircraft systems that line the river bank. Once those are out of the way, Kane has a surprise in store for GDI's navy. + +## Objectives +destroy-aa = Destroy GDI anti-aircraft defenses. +destroy-frigates = Destroy GDI naval blockade. + +## Dialogue +kane = Kane +things-to-come = You have done well commander! Now behold; a taste of things to come. Use them wisely. diff --git a/mods/ca/missions/main-campaign/ca-prologue-04/juncture-rules.yaml b/mods/ca/missions/main-campaign/ca-prologue-04/juncture-rules.yaml index 8785a1ded7..8aa7c5c6d3 100644 --- a/mods/ca/missions/main-campaign/ca-prologue-04/juncture-rules.yaml +++ b/mods/ca/missions/main-campaign/ca-prologue-04/juncture-rules.yaml @@ -6,7 +6,7 @@ World: LuaScript: Scripts: campaign.lua, juncture.lua MissionData: - Briefing: Critical components and supplies are being transported to a secret staging area. Few in the Brotherhood know the full details of this operation, but all will be revealed in time.\n\nGDI continue to threaten vital transportation routes, and you have been chosen to secure one such route in Egypt.\n\nGDI is blockading the Nile river with their fleet, and this is unacceptably delaying our progress.\n\nEstablish a foothold, then your objective is to destroy the GDI anti-aircraft systems that line the river bank. Once those are out of the way, Kane has a surprise in store for GDI's navy. + Briefing: briefing -ScriptLobbyDropdown@DIFFICULTY: ScriptLobbyDropdown@PROLOGUEDIFFICULTY: ID: prologuedifficulty diff --git a/mods/ca/missions/main-campaign/ca-prologue-04/juncture.lua b/mods/ca/missions/main-campaign/ca-prologue-04/juncture.lua index b8eda34c91..8879145c41 100644 --- a/mods/ca/missions/main-campaign/ca-prologue-04/juncture.lua +++ b/mods/ca/missions/main-campaign/ca-prologue-04/juncture.lua @@ -40,12 +40,12 @@ WorldLoaded = function() InitObjectives(Nod) InitGDI() - ObjectiveDestroyAA = Nod.AddObjective("Destroy GDI anti-aircraft defenses.") + ObjectiveDestroyAA = Nod.AddObjective(UserInterface.GetFluentMessage("destroy-aa")) local aaGuns = GDI.GetActorsByType("cram") Trigger.OnAllKilled(aaGuns, function() local frigates = GDI.GetActorsByType("dd2") - ObjectiveDestroyFrigates = Nod.AddObjective("Destroy GDI naval blockade.") + ObjectiveDestroyFrigates = Nod.AddObjective(UserInterface.GetFluentMessage("destroy-frigates")) Trigger.AfterDelay(DateTime.Seconds(6), function() InitReinforcements() @@ -123,10 +123,10 @@ InitReinforcements = function() Trigger.AfterDelay(DateTime.Seconds(2), function() PlaySpeechNotificationToMissionPlayers("ReinforcementsArrived") - Notification("Reinforcements have arrived.") + Notification("reinforcements-arrived") Trigger.AfterDelay(AdjustTimeForGameSpeed(DateTime.Seconds(3)), function() - Media.DisplayMessage("You have done well commander! Now behold; a taste of things to come. Use them wisely.", "Kane", HSLColor.FromHex("FF0000")) + Media.DisplayMessage(UserInterface.GetFluentMessage("things-to-come"), UserInterface.GetFluentMessage("kane"), HSLColor.FromHex("FF0000")) MediaCA.PlaySound(MissionDir .. "/thingstocome.aud", 2) end) end) diff --git a/mods/ca/missions/main-campaign/ca-prologue-04/map.yaml b/mods/ca/missions/main-campaign/ca-prologue-04/map.yaml index 7295671976..a8643db111 100644 --- a/mods/ca/missions/main-campaign/ca-prologue-04/map.yaml +++ b/mods/ca/missions/main-campaign/ca-prologue-04/map.yaml @@ -778,4 +778,6 @@ Rules: ca|rules/custom/campaign-rules.yaml, ca|rules/custom/campaign-tooltips.ya Weapons: ca|weapons/custom/campaign.yaml +FluentMessages: ca|fluent/lua.ftl, en.ftl + Notifications: juncture-notifications.yaml diff --git a/mods/ca/mod.yaml b/mods/ca/mod.yaml index a2011489f2..3db1e68617 100644 --- a/mods/ca/mod.yaml +++ b/mods/ca/mod.yaml @@ -100,9 +100,11 @@ FluentMessages: common|fluent/rules.ftl ca|fluent/ca.ftl ca|fluent/chrome.ftl + ca|fluent/coop.ftl ca|fluent/encyclopedia.ftl ca|fluent/factions.ftl ca|fluent/hotkeys.ftl + ca|fluent/lua.ftl ca|fluent/options.ftl ca|fluent/powers.ftl ca|fluent/rules.ftl diff --git a/mods/ca/scripts/campaign.lua b/mods/ca/scripts/campaign.lua index 7c3f0541a1..5fe01a59e8 100644 --- a/mods/ca/scripts/campaign.lua +++ b/mods/ca/scripts/campaign.lua @@ -18,7 +18,7 @@ StructureBuildTimeMultipliers = { } McvRebuildDelay = { - easy = DateTime.Minutes(25), -- not used by default + easy = DateTime.Minutes(25), -- not used by default normal = DateTime.Minutes(15), -- not used by default hard = DateTime.Minutes(8), vhard = DateTime.Minutes(5), @@ -26,11 +26,11 @@ McvRebuildDelay = { } UnitBuildTimeMultipliers = { - easy = 1.25, -- 2000 value/min per queue (~33/s) + easy = 1.25, -- 2000 value/min per queue (~33/s) normal = 0.81, -- 3086 value/min per queue (~51/s) - hard = 0.6, -- 4166 value/min per queue (~69/s) - vhard = 0.4, -- 6250 value/min per queue (~104/s) - brutal = 0.3 -- 8333 value/min per queue (~138/s) + hard = 0.6, -- 4166 value/min per queue (~69/s) + vhard = 0.4, -- 6250 value/min per queue (~104/s) + brutal = 0.3 -- 8333 value/min per queue (~138/s) -- 1.0 -- 2500 value/min per queue (~41.6/s) -- 0.52 -- 4807 value/min per queue (~80/s) -- 0.34 -- 7352 value/min per queue (~122/s) @@ -42,7 +42,7 @@ AttackValueMultipliers = { easy = 0.5, -- standard min 20/s, max 40/s normal = 1, -- standard min 40/s, max 80/s hard = 1.5, -- standard min 60/s, max 120/s - vhard = 2, -- standard min 80/s, max 160/s + vhard = 2, -- standard min 80/s, max 160/s brutal = 2.5 -- standard min 100/s, max 200/s } @@ -50,7 +50,7 @@ NormalRampDuration = DateTime.Minutes(17) RampDurationMultipliers = { easy = 1.06, -- 18 min - normal = 1, -- 17 min + normal = 1, -- 17 min hard = 0.94, -- 16 min vhard = 0.88, -- 15 min brutal = 0.82 -- 14 min @@ -136,7 +136,8 @@ CashRewardOnCaptureTypes = { "proc", "proc.td", "proc.scrin", "silo", "silo.td", WallTypes = { "sbag", "fenc", "brik", "cycl", "barb" } -KeyStructures = { "fact", "afac", "sfac", "proc", "proc.td", "proc.scrin", "weap", "weap.td", "airs", "wsph", "dome", "hq", "nerv", "atek", "stek", "gtek", "tmpl", "scrt", "mcv", "amcv", "smcv" } +KeyStructures = { "fact", "afac", "sfac", "proc", "proc.td", "proc.scrin", "weap", "weap.td", "airs", "wsph", "dome", + "hq", "nerv", "atek", "stek", "gtek", "tmpl", "scrt", "mcv", "amcv", "smcv" } DefaultQueueProducers = { Infantry = BarracksTypes, @@ -161,56 +162,56 @@ RebuildFunctions = { } -- should be populated with the human players in the mission -MissionPlayers = { } +MissionPlayers = {} -- should be populated with squads definitions needed by the mission -Squads = { } +Squads = {} -- -- begin automatically populated vars (do not assign values to these) -- -- stores the player base locations (recalculated at intervals) -PlayerBaseLocations = { } +PlayerBaseLocations = {} -- queued structures for AI -BuildingQueues = { } +BuildingQueues = {} -- stores active squad leaders -SquadLeaders = { } +SquadLeaders = {} -- stores actors which have called for help so they don't do so repeatedly -AlertedUnits = { } +AlertedUnits = {} -- per production structure, stores which squad to assign produced units to next -SquadAssignmentQueue = { } +SquadAssignmentQueue = {} -- stores which AI production structures have triggers assigned to prevent them being added multiple times -OnProductionTriggers = { } +OnProductionTriggers = {} -- when player kills an AI harvester it delays the production of the next attack wave -HarvesterDeathStacks = { } +HarvesterDeathStacks = {} -- minimum time until next special composition for each AI player -SpecialCompositionMinTimes = { } +SpecialCompositionMinTimes = {} -- caches unit costs for adjusting composition difficulty -UnitCosts = { } +UnitCosts = {} -- player characteristics used to enable AI behaviours -PlayerCharacteristics = { } +PlayerCharacteristics = {} -- stores original AI conyards for rebuilding purposes -AiConyards = { } +AiConyards = {} -- stores queue of conyards that need rebuilding -AiConyardRebuildQueue = { } +AiConyardRebuildQueue = {} -- stores actors that have a trigger assigned for rebuilding AI conyards -McvProductionTriggers = { } +McvProductionTriggers = {} -- stores buildings that should be sold on capture attempts -BuildingsToSellOnCaptureAttempt = { } +BuildingsToSellOnCaptureAttempt = {} -- -- end automatically populated vars @@ -225,10 +226,12 @@ InitObjectives = function(player) if p.IsLocalPlayer then Trigger.AfterDelay(1, function() local colour = HSLColor.Yellow + local messageKey = "new-primary-objective" if p.GetObjectiveType(id) ~= "Primary" then colour = HSLColor.Gray + messageKey = "new-secondary-objective" end - Media.DisplayMessage(p.GetObjectiveDescription(id), "New " .. string.lower(p.GetObjectiveType(id)) .. " objective", colour) + Media.DisplayMessage(p.GetObjectiveDescription(id), UserInterface.GetFluentMessage(messageKey), colour) end) end end) @@ -236,13 +239,15 @@ InitObjectives = function(player) Trigger.OnObjectiveCompleted(player, function(p, id) if p.IsLocalPlayer then Media.PlaySoundNotification(player, "AlertBleep") - Media.DisplayMessage(p.GetObjectiveDescription(id), "Objective completed", HSLColor.LimeGreen) + Media.DisplayMessage(p.GetObjectiveDescription(id), UserInterface.GetFluentMessage("objective-completed"), + HSLColor.LimeGreen) end end) Trigger.OnObjectiveFailed(player, function(p, id) if p.IsLocalPlayer then - Media.DisplayMessage(p.GetObjectiveDescription(id), "Objective failed", HSLColor.Red) + Media.DisplayMessage(p.GetObjectiveDescription(id), UserInterface.GetFluentMessage("objective-failed"), + HSLColor.Red) end end) @@ -264,11 +269,13 @@ InitObjectives = function(player) end Notification = function(text) - Media.DisplayMessage(text, "Notification", HSLColor.FromHex("1E90FF")) + Media.DisplayMessage(UserInterface.GetFluentMessage(text), UserInterface.GetFluentMessage("notification"), + HSLColor.FromHex("1E90FF")) end Tip = function(text) - Media.DisplayMessage(text, "Tip", HSLColor.FromHex("29F3CF")) + Media.DisplayMessage(UserInterface.GetFluentMessage(text), UserInterface.GetFluentMessage("tip"), + HSLColor.FromHex("29F3CF")) end IsNormalOrAbove = function() @@ -295,7 +302,7 @@ IsVeryHardOrBelow = function() return IsHardOrBelow() or Difficulty == "vhard" end -AttackAircraftTargets = { } +AttackAircraftTargets = {} InitAttackAircraft = function(aircraft, targetPlayers, targetList, targetType) if not aircraft.IsDead then local typeKey = string.gsub(aircraft.Type, "%.", "_") @@ -353,7 +360,8 @@ end ChooseRandomTarget = function(unit, targetPlayer) local target = nil local enemies = Utils.Where(targetPlayer.GetActors(), function(self) - return self.HasProperty("Health") and unit.CanTarget(self) and not Utils.Any(WallTypes, function(type) return self.Type == type end) + return self.HasProperty("Health") and unit.CanTarget(self) and + not Utils.Any(WallTypes, function(type) return self.Type == type end) end) if #enemies > 0 then target = Utils.Random(enemies) @@ -375,7 +383,8 @@ ChooseRandomTargetOfTypes = function(unit, targetPlayer, targetList, targetType) end local enemies = Utils.Where(enemies, function(e) - return e.HasProperty("Health") and (e.HasProperty("Move") or e.HasProperty("StartBuildingRepairs")) and unit.CanTarget(e) + return e.HasProperty("Health") and (e.HasProperty("Move") or e.HasProperty("StartBuildingRepairs")) and + unit.CanTarget(e) end) if #enemies > 0 then @@ -389,7 +398,8 @@ end ChooseRandomBuildingTarget = function(unit, targetPlayer) local target = nil local enemies = Utils.Where(targetPlayer.GetActors(), function(e) - return e.HasProperty("Health") and e.HasProperty("StartBuildingRepairs") and unit.CanTarget(e) and not Utils.Any(WallTypes, function(t) return e.Type == t end) + return e.HasProperty("Health") and e.HasProperty("StartBuildingRepairs") and unit.CanTarget(e) and + not Utils.Any(WallTypes, function(t) return e.Type == t end) end) if #enemies > 0 then target = Utils.Random(enemies) @@ -415,7 +425,6 @@ IdleHunt = function(actor) end AssaultPlayerBaseOrHunt = function(actor, targetPlayers, waypoints, fromIdle) - if actor.IsDead or not actor.HasProperty("AttackMove") or IsMissionPlayer(actor.Owner) then return end @@ -492,7 +501,8 @@ end PlayerBuildingsCount = function(player) local buildings = Utils.Where(player.GetActors(), function(a) - return a.HasProperty("StartBuildingRepairs") and not a.HasProperty("Attack") and a.Type ~= "silo" and a.Type ~= "silo.td" and a.Type ~= "silo.scrin" + return a.HasProperty("StartBuildingRepairs") and not a.HasProperty("Attack") and a.Type ~= "silo" and + a.Type ~= "silo.td" and a.Type ~= "silo.scrin" end) local mcvs = player.GetActorsByTypes(McvTypes) return #buildings + #mcvs @@ -505,7 +515,8 @@ end MissionPlayersHaveNavalPresence = function() local count = 0 for _, p in pairs(MissionPlayers) do - local navalUnits = p.GetActorsByTypes({ "isub", "msub", "ca", "cv", "dd", "pt", "ss", "seas", "ss2", "sb", "dd2", "pt2", "lst" }) + local navalUnits = p.GetActorsByTypes({ "isub", "msub", "ca", "cv", "dd", "pt", "ss", "seas", "ss2", "sb", "dd2", + "pt2", "lst" }) count = count + #navalUnits end return count > 4 @@ -550,7 +561,8 @@ RemoveActorsBasedOnDifficultyTags = function() local brutalOnlyActors = Map.ActorsWithTag("BrutalOnly") local actorsToRemoveAbove = { - easy = Utils.Concat(normalAndAboveActors, Utils.Concat(hardAndAboveActors, Utils.Concat(veryHardAndAboveActors, brutalOnlyActors))), + easy = Utils.Concat(normalAndAboveActors, + Utils.Concat(hardAndAboveActors, Utils.Concat(veryHardAndAboveActors, brutalOnlyActors))), normal = Utils.Concat(hardAndAboveActors, Utils.Concat(veryHardAndAboveActors, brutalOnlyActors)), hard = Utils.Concat(veryHardAndAboveActors, brutalOnlyActors), vhard = brutalOnlyActors, @@ -569,7 +581,8 @@ RemoveActorsBasedOnDifficultyTags = function() local veryHardAndBelowActors = Map.ActorsWithTag("VeryHardAndBelow") local actorsToRemoveBelow = { - brutal = Utils.Concat(veryHardAndBelowActors, Utils.Concat(hardAndBelowActors, Utils.Concat(normalAndBelowActors, easyOnlyActors))), + brutal = Utils.Concat(veryHardAndBelowActors, + Utils.Concat(hardAndBelowActors, Utils.Concat(normalAndBelowActors, easyOnlyActors))), vhard = Utils.Concat(hardAndBelowActors, Utils.Concat(normalAndBelowActors, easyOnlyActors)), hard = Utils.Concat(normalAndBelowActors, easyOnlyActors), normal = easyOnlyActors, @@ -584,14 +597,16 @@ RemoveActorsBasedOnDifficultyTags = function() end AutoRepairBuildings = function(player) - local buildings = Utils.Where(Map.ActorsInWorld, function(self) return self.Owner == player and self.HasProperty("StartBuildingRepairs") end) + local buildings = Utils.Where(Map.ActorsInWorld, + function(self) return self.Owner == player and self.HasProperty("StartBuildingRepairs") end) Utils.Do(buildings, function(a) AutoRepairBuilding(a, player) end) end AutoRepairAndRebuildBuildings = function(player, maxAttempts) - local buildings = Utils.Where(Map.ActorsInWorld, function(self) return self.Owner == player and self.HasProperty("StartBuildingRepairs") end) + local buildings = Utils.Where(Map.ActorsInWorld, + function(self) return self.Owner == player and self.HasProperty("StartBuildingRepairs") end) Utils.Do(buildings, function(a) local excludeFromRebuilding = false @@ -641,7 +656,7 @@ end AutoRebuildBuilding = function(building, player, maxAttempts) if BuildingQueues[player.InternalName] == nil then - BuildingQueues[player.InternalName] = { } + BuildingQueues[player.InternalName] = {} end if maxAttempts == nil then maxAttempts = 15 @@ -708,7 +723,7 @@ RebuildBuilding = function(queueItem) RebuildFunctions[queueItem.Player.InternalName](b) end - -- otherwise add to back of queue (if attempts remaining) + -- otherwise add to back of queue (if attempts remaining) elseif queueItem.AttemptsRemaining > 1 then queueItem.AttemptsRemaining = queueItem.AttemptsRemaining - 1 BuildingQueues[queueItem.Player.InternalName][#BuildingQueues[queueItem.Player.InternalName] + 1] = queueItem @@ -754,7 +769,8 @@ HasOwnedBuildingsNearby = function(player, pos, noEnemyBuildings) local bottomRight = WPos.New(pos.X + 8192, pos.Y + 8192, 0) local nearbyBuildings = Map.ActorsInBox(topLeft, bottomRight, function(a) - return not a.IsDead and a.Owner == player and a.HasProperty("StartBuildingRepairs") and not a.HasProperty("Attack") and a.Type ~= "silo" and a.Type ~= "silo.td" and a.Type ~= "silo.scrin" + return not a.IsDead and a.Owner == player and a.HasProperty("StartBuildingRepairs") and not a.HasProperty("Attack") and + a.Type ~= "silo" and a.Type ~= "silo.td" and a.Type ~= "silo.scrin" end) -- require an owned building nearby @@ -859,11 +875,10 @@ end QueueMcv = function(player, conyardEntry) if AiConyardRebuildQueue[player.InternalName] == nil then - AiConyardRebuildQueue[player.InternalName] = { } + AiConyardRebuildQueue[player.InternalName] = {} end Trigger.AfterDelay(McvRebuildDelay[Difficulty], function() - -- get a conyard without an assigned mcv that has friendly buildings nearby, where a producer can path to it local possibleConyards = GetRebuildableConyardEntries(player) @@ -896,7 +911,8 @@ QueueMcv = function(player, conyardEntry) Trigger.OnIdle(produced, function(self) -- if deploy failed, wait 10 seconds, move to a random location within 5 cells then return and try again produced.Wait(DateTime.Seconds(10)) - local randomCell = CPos.New(targetConyardEntry.DeployLocation.X + Utils.RandomInteger(-5, 5), targetConyardEntry.DeployLocation.Y + Utils.RandomInteger(-5, 5)) + local randomCell = CPos.New(targetConyardEntry.DeployLocation.X + Utils.RandomInteger(-5, 5), + targetConyardEntry.DeployLocation.Y + Utils.RandomInteger(-5, 5)) produced.Move(randomCell) produced.Move(targetConyardEntry.DeployLocation) produced.Deploy() @@ -906,7 +922,8 @@ QueueMcv = function(player, conyardEntry) Trigger.OnRemovedFromWorld(produced, function(self) if not self.IsDead then Trigger.AfterDelay(DateTime.Seconds(1), function() - local nearbyConyards = Map.ActorsInCircle(Map.CenterOfCell(targetConyardEntry.DeployLocation), WDist.FromCells(2), function(a) + local nearbyConyards = Map.ActorsInCircle(Map.CenterOfCell(targetConyardEntry.DeployLocation), + WDist.FromCells(2), function(a) return a.Owner == player and IsConyard(a) end) if #nearbyConyards > 0 then @@ -936,10 +953,10 @@ end GetRebuildableConyardEntries = function(player) return Utils.Where(AiConyards, function(c) return c.Actor.IsDead - and c.AssignedMcv == nil - and c.Player == player - and HasOwnedBuildingsNearby(player, Map.CenterOfCell(c.DeployLocation), true) - and Utils.Any(c.PossibleProducers, function(p) return not p.IsDead end) + and c.AssignedMcv == nil + and c.Player == player + and HasOwnedBuildingsNearby(player, Map.CenterOfCell(c.DeployLocation), true) + and Utils.Any(c.PossibleProducers, function(p) return not p.IsDead end) end) end @@ -948,9 +965,9 @@ RestoreSquadProduction = function(oldBuilding, newBuilding) return end - for squadName,squad in pairs(Squads) do + for squadName, squad in pairs(Squads) do if squad.ProducerActors ~= nil then - for queue,actors in pairs(squad.ProducerActors) do + for queue, actors in pairs(squad.ProducerActors) do if actors ~= nil then for idx, a in pairs(actors) do if a == oldBuilding then @@ -972,7 +989,7 @@ TargetSwapChance = function(unit, chance, isMissionPlayerFunc) if isMissionPlayerFunc(self.Owner) or not isMissionPlayerFunc(attacker.Owner) then return end - local rand = Utils.RandomInteger(1,100) + local rand = Utils.RandomInteger(1, 100) if rand > 100 - chance then if not unit.IsDead and not attacker.IsDead and unit.HasProperty("Attack") then unit.Stop() @@ -1068,11 +1085,11 @@ InitAttackSquad = function(squad, player, targetPlayers) end if squad.QueueProductionStatuses == nil then - squad.QueueProductionStatuses = { } + squad.QueueProductionStatuses = {} end if squad.IdleUnits == nil then - squad.IdleUnits = { } + squad.IdleUnits = {} end if squad.Delay ~= nil then @@ -1103,7 +1120,6 @@ InitNavalAttackSquad = function(squad, player, targetPlayers) end InitAttackWave = function(squad) - if IsSquadInProduction(squad) then return end @@ -1135,12 +1151,16 @@ InitAttackWave = function(squad) -- filter possible compositions based on game time and other requirements local validCompositions = Utils.Where(allCompositions, function(composition) return (composition.MinTime == nil or DateTime.GameTime >= composition.MinTime + squad.InitTime) -- after min time - and (composition.MaxTime == nil or DateTime.GameTime < composition.MaxTime + squad.InitTime) -- before max time - and (composition.RequiredTargetCharacteristics == nil or Utils.All(composition.RequiredTargetCharacteristics, function(characteristic) - return PlayerOrMissionPlayersHaveCharacteristic(squad.TargetPlayer, characteristic) - end)) -- target player has all required characteristics - and (composition.Prerequisites == nil or squad.Player.HasPrerequisites(composition.Prerequisites)) -- player has prerequisites - and (composition.EnabledFunc == nil or composition.EnabledFunc()) -- custom function for whether the composition is enabled + and (composition.MaxTime == nil or DateTime.GameTime < composition.MaxTime + squad.InitTime) -- before max time + and + (composition.RequiredTargetCharacteristics == nil or Utils.All(composition.RequiredTargetCharacteristics, function( + characteristic) + return PlayerOrMissionPlayersHaveCharacteristic(squad.TargetPlayer, characteristic) + end)) -- target player has all required characteristics + and + (composition.Prerequisites == nil or squad.Player.HasPrerequisites(composition.Prerequisites)) -- player has prerequisites + and + (composition.EnabledFunc == nil or composition.EnabledFunc()) -- custom function for whether the composition is enabled end) -- determine whether to choose a special composition (33% chance if enough time has elapsed since last used) @@ -1198,9 +1218,9 @@ InitAttackWave = function(squad) end GetQueuesForComposition = function(composition) - local queues = { } + local queues = {} - for k,v in pairs(composition) do + for k, v in pairs(composition) do if IsQueue(k) then table.insert(queues, k) end @@ -1233,11 +1253,15 @@ IsQueue = function(key) end PlayerHasCharacteristic = function(player, characteristic) - return PlayerCharacteristics[player.InternalName] ~= nil and PlayerCharacteristics[player.InternalName][characteristic] ~= nil and PlayerCharacteristics[player.InternalName][characteristic] + return PlayerCharacteristics[player.InternalName] ~= nil and + PlayerCharacteristics[player.InternalName][characteristic] ~= nil and + PlayerCharacteristics[player.InternalName][characteristic] end MissionPlayersHaveCharacteristic = function(characteristic) - return PlayerCharacteristics["MissionPlayers"] ~= nil and PlayerCharacteristics["MissionPlayers"][characteristic] ~= nil and PlayerCharacteristics["MissionPlayers"][characteristic] + return PlayerCharacteristics["MissionPlayers"] ~= nil and + PlayerCharacteristics["MissionPlayers"][characteristic] ~= nil and + PlayerCharacteristics["MissionPlayers"][characteristic] end PlayerOrMissionPlayersHaveCharacteristic = function(player, characteristic) @@ -1248,7 +1272,7 @@ ProduceNextAttackSquadUnit = function(squad, queue, unitIndex) local units = squad.QueuedUnits[queue] if units == nil then - units = { } + units = {} end -- if there are no more units to build for this queue, check if any other queues are producing -- if none are, send the attack and produce next attack squad after interval @@ -1293,7 +1317,7 @@ ProduceNextAttackSquadUnit = function(squad, queue, unitIndex) InitAttackSquad(squad, squad.Player, squad.TargetPlayers) end) end - -- if more units to build, set them to produce after delay equal to their build time (with difficulty multiplier applied) + -- if more units to build, set them to produce after delay equal to their build time (with difficulty multiplier applied) else squad.QueueProductionStatuses[queue] = true local nextUnit = units[unitIndex] @@ -1340,13 +1364,13 @@ ProduceNextAttackSquadUnit = function(squad, queue, unitIndex) local producerId = tostring(producer) local engineersNearby = Map.ActorsInCircle(producer.CenterPosition, WDist.New(2730), function(a) - return not a.IsDead and (a.Type == "e6" or a.Type == "n6" or a.Type == "s6" or a.Type == "mast") and a.Owner ~= producer.Owner + return not a.IsDead and (a.Type == "e6" or a.Type == "n6" or a.Type == "s6" or a.Type == "mast") and + a.Owner ~= producer.Owner end) -- prevents capturing player getting a free unit -- the capture blocks the production until the capture completes if #engineersNearby == 0 then - -- set that the next unit produced from this producer should be assigned to the specified squad AddToSquadAssignmentQueue(producerId, squad) @@ -1377,7 +1401,6 @@ ProduceNextAttackSquadUnit = function(squad, queue, unitIndex) end HandleProducedSquadUnit = function(produced, producerId, squad) - -- if the produced unit is not owned by the squad player, ignore it if produced.Owner ~= squad.Player then return @@ -1405,7 +1428,6 @@ HandleProducedSquadUnit = function(produced, producerId, squad) if assignedSquad.OnProducedAction ~= nil then assignedSquad.OnProducedAction(produced) end - elseif produced.HasProperty("Hunt") then produced.Hunt() end @@ -1417,15 +1439,16 @@ end -- also split by player to prevent these getting jumbled if producer owner changes AddToSquadAssignmentQueue = function(producerId, squad) InitSquadAssignmentQueueForProducer(producerId, squad.Player) - SquadAssignmentQueue[squad.Player.InternalName][producerId][#SquadAssignmentQueue[squad.Player.InternalName][producerId] + 1] = squad + SquadAssignmentQueue[squad.Player.InternalName][producerId][#SquadAssignmentQueue[squad.Player.InternalName][producerId] + 1] = + squad end InitSquadAssignmentQueueForProducer = function(producerId, player) if SquadAssignmentQueue[player.InternalName] == nil then - SquadAssignmentQueue[player.InternalName] = { } + SquadAssignmentQueue[player.InternalName] = {} end if SquadAssignmentQueue[player.InternalName][producerId] == nil then - SquadAssignmentQueue[player.InternalName][producerId] = { } + SquadAssignmentQueue[player.InternalName][producerId] = {} end end @@ -1478,10 +1501,10 @@ function CalculateValuePerSecond(currentTick, attackValues) rampDuration = NormalRampDuration * RampDurationMultipliers[Difficulty] end local growthFactor = 2.06 - local progress = currentTick / rampDuration - local scaledProgress = progress ^ growthFactor - local value = minValue + (maxValue - minValue) * scaledProgress - return math.min(math.floor(value), maxValue) + local progress = currentTick / rampDuration + local scaledProgress = progress ^ growthFactor + local value = minValue + (maxValue - minValue) * scaledProgress + return math.min(math.floor(value), maxValue) end IsSquadInProduction = function(squad) @@ -1556,7 +1579,7 @@ SendAttackSquad = function(squad) ClearSquadLeader(squadLeader) end) - -- if not squad leader, follow the leader + -- if not squad leader, follow the leader else SquadLeaders[actorId] = squadLeader FollowSquadLeader(a, squad) @@ -1569,11 +1592,11 @@ SendAttackSquad = function(squad) end end) end - squad.IdleUnits = { } + squad.IdleUnits = {} end ClearSquadLeader = function(squadLeader) - for k,v in pairs(SquadLeaders) do + for k, v in pairs(SquadLeaders) do if v == squadLeader then SquadLeaders[k] = nil end @@ -1590,7 +1613,7 @@ FollowSquadLeader = function(actor, squad) actor.Stop() actor.AttackMove(cell, 1) - Trigger.AfterDelay(Utils.RandomInteger(35,65), function() + Trigger.AfterDelay(Utils.RandomInteger(35, 65), function() FollowSquadLeader(actor, squad) end) else @@ -1620,7 +1643,7 @@ FollowActor = function(actor, actorToFollow) actor.Stop() actor.AttackMove(cell, 1) - Trigger.AfterDelay(Utils.RandomInteger(35,65), function() + Trigger.AfterDelay(Utils.RandomInteger(35, 65), function() FollowActor(actor, actorToFollow) end) else @@ -1719,7 +1742,8 @@ SellOnCaptureAttempt = function(buildings, sellAsGroup) local captureCells = Utils.ExpandFootprint(footprint, true) Trigger.OnEnteredFootprint(captureCells, function(a, id) - if IsMissionPlayer(a.Owner) and (a.Type == "e6" or a.Type == "n6" or a.Type == "s6" or a.Type == "mast" or (a.Type == "ifv" and a.HasPassengers and Utils.Any(a.Passengers, function(p) return p.Type == "e6" or p.Type == "n6" or p.Type == "s6" end))) then + if IsMissionPlayer(a.Owner) and (a.Type == "e6" or a.Type == "n6" or a.Type == "s6" or a.Type == "mast" or (a.Type == "ifv" and a.HasPassengers and Utils.Any(a.Passengers, function( + p) return p.Type == "e6" or p.Type == "n6" or p.Type == "s6" end))) then Trigger.RemoveFootprintTrigger(id) if sellAsGroup then Utils.Do(buildings, function(b2) @@ -1902,15 +1926,18 @@ IsGroundHunterUnit = function(actor) end IsGreeceGroundHunterUnit = function(actor) - return IsGroundHunterUnit(actor) and actor.Type ~= "arty" and actor.Type ~= "cryo" and actor.Type ~= "mgg" and actor.Type ~= "mrj" + return IsGroundHunterUnit(actor) and actor.Type ~= "arty" and actor.Type ~= "cryo" and actor.Type ~= "mgg" and + actor.Type ~= "mrj" end IsUSSRGroundHunterUnit = function(actor) - return IsGroundHunterUnit(actor) and actor.Type ~= "v2rl" and actor.Type ~= "v3rl" and actor.Type ~= "katy" and actor.Type ~= "grad" and actor.Type ~= "nukc" + return IsGroundHunterUnit(actor) and actor.Type ~= "v2rl" and actor.Type ~= "v3rl" and actor.Type ~= "katy" and + actor.Type ~= "grad" and actor.Type ~= "nukc" end IsGDIGroundHunterUnit = function(actor) - return (IsGroundHunterUnit(actor) or actor.Type == "jjet") and actor.Type ~= "msam" and actor.Type ~= "memp" and actor.Type ~= "thwk" + return (IsGroundHunterUnit(actor) or actor.Type == "jjet") and actor.Type ~= "msam" and actor.Type ~= "memp" and + actor.Type ~= "thwk" end IsNodGroundHunterUnit = function(actor) @@ -1945,13 +1972,10 @@ InitAiUpgrades = function(player, advancedDelay) end if (player.Faction == "allies") then - if IsHardOrAbove() then Actor.Create("cryw.upgrade", true, { Owner = Greece }) end - elseif (player.Faction == "soviet") then - if IsHardOrAbove() then Trigger.AfterDelay(advancedDelay, function() Actor.Create("tarc.upgrade", true, { Owner = player }) @@ -1961,9 +1985,7 @@ InitAiUpgrades = function(player, advancedDelay) Actor.Create(selectedDoctrineUpgrade, true, { Owner = player }) end) end - elseif (player.Faction == "nod") then - if IsHardOrAbove() then Trigger.AfterDelay(advancedDelay, function() Actor.Create("blacknapalm.upgrade", true, { Owner = player }) @@ -1973,9 +1995,7 @@ InitAiUpgrades = function(player, advancedDelay) Actor.Create("cyborgarmor.upgrade", true, { Owner = player }) end) end - elseif (player.Faction == "gdi") then - if IsHardOrAbove() then Actor.Create("sonic.upgrade", true, { Owner = player, }) Actor.Create("empgren.upgrade", true, { Owner = player, }) @@ -1983,8 +2003,8 @@ InitAiUpgrades = function(player, advancedDelay) Trigger.AfterDelay(advancedDelay, function() local strategyUpgrades = { { "bombard.strat", "bombard2.strat", "hailstorm.upgrade" }, - { "seek.strat", "seek2.strat", "hypersonic.upgrade" }, - { "hold.strat", "hold2.strat", "hammerhead.upgrade" }, + { "seek.strat", "seek2.strat", "hypersonic.upgrade" }, + { "hold.strat", "hold2.strat", "hammerhead.upgrade" }, } local selectedStrategyUpgrades = Utils.Random(strategyUpgrades) @@ -1993,9 +2013,7 @@ InitAiUpgrades = function(player, advancedDelay) end) end) end - elseif (player.Faction == "scrin") then - if IsHardOrAbove() then Actor.Create("ioncon.upgrade", true, { Owner = player }) @@ -2004,7 +2022,6 @@ InitAiUpgrades = function(player, advancedDelay) Actor.Create("shields.upgrade", true, { Owner = player }) end) end - end end @@ -2043,7 +2060,7 @@ AdjustAttackValuesForDifficulty = function(attackValues, difficulty) end AdjustCompositionsForDifficulty = function(compositions, difficulty) - local updatedCompositions = { } + local updatedCompositions = {} Utils.Do(compositions, function(comp) local updatedComposition = AdjustCompositionForDifficulty(comp, difficulty) @@ -2063,16 +2080,15 @@ AdjustCompositionForDifficulty = function(composition, difficulty) end -- total unadjusted cost for all units in each queue - local queueTotalUnitCost = { } + local queueTotalUnitCost = {} -- total adjusted cost for each queue - local queueAllocatedTotalUnitCost = { } + local queueAllocatedTotalUnitCost = {} -- units added to the adjusted composition - local updatedComposition = { } - - for k,v in pairs(composition) do + local updatedComposition = {} + for k, v in pairs(composition) do if IsQueue(k) then local queueName = k local queueUnits = v @@ -2080,7 +2096,7 @@ AdjustCompositionForDifficulty = function(composition, difficulty) queueAllocatedTotalUnitCost[queueName] = 0 -- for each unit in the queue - for i,unit in pairs(queueUnits) do + for i, unit in pairs(queueUnits) do local chosenUnit -- if the unit is a table of possible units, use the one with the highest cost for calculation purposes @@ -2097,11 +2113,12 @@ AdjustCompositionForDifficulty = function(composition, difficulty) queueTotalUnitCost[queueName] = queueTotalUnitCost[queueName] + UnitCosts[chosenUnit] end - local adjustedDesiredTotalUnitCostForQueue = queueTotalUnitCost[queueName] * CompositionValueMultipliers[difficulty] + local adjustedDesiredTotalUnitCostForQueue = queueTotalUnitCost[queueName] * + CompositionValueMultipliers[difficulty] -- allocate units until the adjusted cost is reached while queueAllocatedTotalUnitCost[queueName] < adjustedDesiredTotalUnitCostForQueue do - for i,unit in pairs(queueUnits) do + for i, unit in pairs(queueUnits) do if queueAllocatedTotalUnitCost[queueName] >= adjustedDesiredTotalUnitCostForQueue then break end @@ -2115,7 +2132,7 @@ AdjustCompositionForDifficulty = function(composition, difficulty) end if updatedComposition[queueName] == nil then - updatedComposition[queueName] = { } + updatedComposition[queueName] = {} end table.insert(updatedComposition[queueName], unit) @@ -2129,7 +2146,6 @@ AdjustCompositionForDifficulty = function(composition, difficulty) end else if k == "MinTime" or k == "MaxTime" then - if difficulty == "easy" then updatedComposition[k] = v * 1.4 elseif difficulty == "normal" then @@ -2137,7 +2153,6 @@ AdjustCompositionForDifficulty = function(composition, difficulty) elseif difficulty == "brutal" then updatedComposition[k] = v * 0.9 end - else updatedComposition[k] = v end @@ -2283,9 +2298,12 @@ CalculatePlayerCharacteristics = function() end) Utils.Do(MissionPlayers, function(p) - PlayerCharacteristics["MissionPlayers"].InfantryValue = PlayerCharacteristics["MissionPlayers"].InfantryValue + PlayerCharacteristics[p.InternalName].InfantryValue - PlayerCharacteristics["MissionPlayers"].HeavyValue = PlayerCharacteristics["MissionPlayers"].HeavyValue + PlayerCharacteristics[p.InternalName].HeavyValue - PlayerCharacteristics["MissionPlayers"].AirValue = PlayerCharacteristics["MissionPlayers"].AirValue + PlayerCharacteristics[p.InternalName].AirValue + PlayerCharacteristics["MissionPlayers"].InfantryValue = PlayerCharacteristics["MissionPlayers"].InfantryValue + + PlayerCharacteristics[p.InternalName].InfantryValue + PlayerCharacteristics["MissionPlayers"].HeavyValue = PlayerCharacteristics["MissionPlayers"].HeavyValue + + PlayerCharacteristics[p.InternalName].HeavyValue + PlayerCharacteristics["MissionPlayers"].AirValue = PlayerCharacteristics["MissionPlayers"].AirValue + + PlayerCharacteristics[p.InternalName].AirValue end) if PlayerCharacteristics["MissionPlayers"].InfantryValue > 20000 then @@ -2370,145 +2388,145 @@ PacOrDevastator = { "pac", "deva" } UnitCompositions = { Allied = { -- 0 to 10 minutes - { Infantry = {}, Vehicles = { "jeep", "jeep", "jeep", "jeep", "jeep" }, MaxTime = DateTime.Minutes(10) }, - { Infantry = {}, Vehicles = { "1tnk", "1tnk", "1tnk", "1tnk" }, MaxTime = DateTime.Minutes(10) }, - { Infantry = { "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1" }, Vehicles = { "2tnk", "apc.ai", "2tnk", "arty" }, MaxTime = DateTime.Minutes(10) }, - { Infantry = { "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", "e1", "e1" }, Vehicles = { "2tnk", "ifv.ai", "2tnk", "ifv.ai" }, MaxTime = DateTime.Minutes(10) }, + { Infantry = {}, Vehicles = { "jeep", "jeep", "jeep", "jeep", "jeep" }, MaxTime = DateTime.Minutes(10) }, + { Infantry = {}, Vehicles = { "1tnk", "1tnk", "1tnk", "1tnk" }, MaxTime = DateTime.Minutes(10) }, + { Infantry = { "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1" }, Vehicles = { "2tnk", "apc.ai", "2tnk", "arty" }, MaxTime = DateTime.Minutes(10) }, + { Infantry = { "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", "e1", "e1" }, Vehicles = { "2tnk", "ifv.ai", "2tnk", "ifv.ai" }, MaxTime = DateTime.Minutes(10) }, -- 10 minutes onwards - { Infantry = { "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", AlliedAdvancedInfantry, "e1", "e3", "e1", "e1" }, Vehicles = { "2tnk", "ifv.ai", "aapc.ai", "apc.ai", "arty" }, MinTime = DateTime.Minutes(10) }, - { Infantry = { "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", AlliedAdvancedInfantry, "e1", "e3", "e1", "e1" }, Vehicles = { "2tnk", "2tnk", "2tnk", "ifv.ai", "ptnk", "ptnk" }, MinTime = DateTime.Minutes(10) }, - { Infantry = { "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", AlliedAdvancedInfantry, "e1", "e3", "e1", "e1" }, Vehicles = { "2tnk", "ifv.ai", "ptnk", "2tnk", "2tnk", AlliedT3SupportVehicle }, MinTime = DateTime.Minutes(10) }, - { Infantry = { "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", "e3", "e1", "e1" }, Vehicles = { "batf.ai", "ifv.ai", "batf.ai", "ifv.ai" }, MinTime = DateTime.Minutes(10) }, - { Infantry = {}, Vehicles = { "apc.ai", "jeep", "ifv.ai", "aapc.ai", "ifv.ai", "apc.ai" }, MinTime = DateTime.Minutes(10) }, + { Infantry = { "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", AlliedAdvancedInfantry, "e1", "e3", "e1", "e1" }, Vehicles = { "2tnk", "ifv.ai", "aapc.ai", "apc.ai", "arty" }, MinTime = DateTime.Minutes(10) }, + { Infantry = { "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", AlliedAdvancedInfantry, "e1", "e3", "e1", "e1" }, Vehicles = { "2tnk", "2tnk", "2tnk", "ifv.ai", "ptnk", "ptnk" }, MinTime = DateTime.Minutes(10) }, + { Infantry = { "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", AlliedAdvancedInfantry, "e1", "e3", "e1", "e1" }, Vehicles = { "2tnk", "ifv.ai", "ptnk", "2tnk", "2tnk", AlliedT3SupportVehicle }, MinTime = DateTime.Minutes(10) }, + { Infantry = { "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", "e3", "e1", "e1" }, Vehicles = { "batf.ai", "ifv.ai", "batf.ai", "ifv.ai" }, MinTime = DateTime.Minutes(10) }, + { Infantry = {}, Vehicles = { "apc.ai", "jeep", "ifv.ai", "aapc.ai", "ifv.ai", "apc.ai" }, MinTime = DateTime.Minutes(10) }, -- 16 minutes onwards - { Infantry = { "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", AlliedAdvancedInfantry, "e1", "e3", "e1", "e1", "e3", "e1", "e1" }, Vehicles = { "2tnk", "2tnk", "ifv.ai", AlliedT3SupportVehicle, "2tnk", PrismCannonOrZeus }, MinTime = DateTime.Minutes(16) }, - { Infantry = { "e3", "enfo", "enfo", "enfo", "enfo", "e1", "e1", "e1", "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", "e3", "enfo", "enfo" }, Vehicles = { "2tnk", "ptnk", "2tnk", "2tnk", "2tnk", "ptnk" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, + { Infantry = { "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", AlliedAdvancedInfantry, "e1", "e3", "e1", "e1", "e3", "e1", "e1" }, Vehicles = { "2tnk", "2tnk", "ifv.ai", AlliedT3SupportVehicle, "2tnk", PrismCannonOrZeus }, MinTime = DateTime.Minutes(16) }, + { Infantry = { "e3", "enfo", "enfo", "enfo", "enfo", "e1", "e1", "e1", "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", "e3", "e1", "e1", "e1", "e3", "enfo", "enfo" }, Vehicles = { "2tnk", "ptnk", "2tnk", "2tnk", "2tnk", "ptnk" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, ------ Anti-tank - { Infantry = { "e3", "e1", "e3", "e3", "e1", "e3", "e1", "e3", "e1", "e3", "e3", "e1", "e3", "e3", "e3", "e3" }, Vehicles = { "tnkd", "tnkd", "tnkd", "tnkd", "tnkd", "tnkd" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassHeavy" } }, + { Infantry = { "e3", "e1", "e3", "e3", "e1", "e3", "e1", "e3", "e1", "e3", "e3", "e1", "e3", "e3", "e3", "e3" }, Vehicles = { "tnkd", "tnkd", "tnkd", "tnkd", "tnkd", "tnkd" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassHeavy" } }, ------ Anti-infantry - { Infantry = { "e3", "enfo", "e1", "e1", "e1", "enfo", "e1", "e1", "enfo", "e3", "e1", "e1", "enfo", "e1", "e1", "e1", "e1", "e1", "e1", "enfo", "enfo" }, Vehicles = { "ptnk", "ptnk", "ptnk", "cryo", "ptnk", "ptnk" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassInfantry" } }, + { Infantry = { "e3", "enfo", "e1", "e1", "e1", "enfo", "e1", "e1", "enfo", "e3", "e1", "e1", "enfo", "e1", "e1", "e1", "e1", "e1", "e1", "enfo", "enfo" }, Vehicles = { "ptnk", "ptnk", "ptnk", "cryo", "ptnk", "ptnk" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassInfantry" } }, -- Specials - { Infantry = {}, Vehicles = { "ctnk", "ctnk", "ctnk", "ctnk", "ctnk", "ctnk" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, - { Infantry = { "seal", "seal", "seal", "seal", "seal", "seal", "e7" }, Vehicles = { }, MinTime = DateTime.Minutes(18), IsSpecial = true }, - { Infantry = { "snip", "snip", "snip", "snip", "snip", "snip", "snip", "snip" }, Vehicles = { "rtnk", "rtnk", "rtnk", "rtnk", "rtnk", "rtnk", "rtnk" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, - { Infantry = { "e3", "cryt", "cryt", "cryt", "e3", "e1", "e1", "e1", "e1", "e1", "e1", "cryt", "cryt", "cryt", }, Vehicles = { "cryo", "2tnk", "2tnk", "cryo", "ifv", "cryo", "2tnk" }, MinTime = DateTime.Minutes(18), IsSpecial = true } + { Infantry = {}, Vehicles = { "ctnk", "ctnk", "ctnk", "ctnk", "ctnk", "ctnk" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, + { Infantry = { "seal", "seal", "seal", "seal", "seal", "seal", "e7" }, Vehicles = {}, MinTime = DateTime.Minutes(18), IsSpecial = true }, + { Infantry = { "snip", "snip", "snip", "snip", "snip", "snip", "snip", "snip" }, Vehicles = { "rtnk", "rtnk", "rtnk", "rtnk", "rtnk", "rtnk", "rtnk" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, + { Infantry = { "e3", "cryt", "cryt", "cryt", "e3", "e1", "e1", "e1", "e1", "e1", "e1", "cryt", "cryt", "cryt", }, Vehicles = { "cryo", "2tnk", "2tnk", "cryo", "ifv", "cryo", "2tnk" }, MinTime = DateTime.Minutes(18), IsSpecial = true } }, Soviet = { -- 0 to 10 minutes - { Infantry = { "e3", "e1", "e1", "e1", "e1", "e1", "e2", "e3", "e4" }, Vehicles = { "3tnk", "btr.ai", "3tnk" }, MaxTime = DateTime.Minutes(10), }, + { Infantry = { "e3", "e1", "e1", "e1", "e1", "e1", "e2", "e3", "e4" }, Vehicles = { "3tnk", "btr.ai", "3tnk" }, MaxTime = DateTime.Minutes(10), }, -- 10 to 16 minutes - { Infantry = { "e3", "e1", "e1", "e3", "shok", "e1", "shok", "e1", "e2", "e3", "e4", "e1" }, Vehicles = { "3tnk", SovietMammothVariant, "btr.ai", TeslaVariant, SovietBasicArty }, MinTime = DateTime.Minutes(10), MaxTime = DateTime.Minutes(16), }, - { Infantry = { "e3", "e1", "shok", "e3", "shok", "e1", "shok", "e1", "shok", "e3", "e4", "e1" }, Vehicles = { TeslaVariant, SovietMammothVariant, "btr.ai", TeslaVariant, SovietBasicArty }, MinTime = DateTime.Minutes(10), MaxTime = DateTime.Minutes(16), }, + { Infantry = { "e3", "e1", "e1", "e3", "shok", "e1", "shok", "e1", "e2", "e3", "e4", "e1" }, Vehicles = { "3tnk", SovietMammothVariant, "btr.ai", TeslaVariant, SovietBasicArty }, MinTime = DateTime.Minutes(10), MaxTime = DateTime.Minutes(16), }, + { Infantry = { "e3", "e1", "shok", "e3", "shok", "e1", "shok", "e1", "shok", "e3", "e4", "e1" }, Vehicles = { TeslaVariant, SovietMammothVariant, "btr.ai", TeslaVariant, SovietBasicArty }, MinTime = DateTime.Minutes(10), MaxTime = DateTime.Minutes(16), }, -- 16 minutes onwards { Infantry = { "e3", "e1", "e1", "e3", "shok", "e1", "e1", "cmsr", "e1", "e2", "e3", "e4", "e1", "e1", "e1", "e1", "shok" }, Vehicles = { "3tnk", SovietMammothVariant, "btr.ai", TeslaVariant, SovietBasicArty, SovietAdvancedArty }, MinTime = DateTime.Minutes(16), }, - { Infantry = { "e3", "e1", "e1", "e3", "ttrp", "e1", "ttrp", "e1", "cmsr", "e2", "e3", "e4", "e1", "e1", "e1", "e1" }, Vehicles = { "3tnk", SovietMammothVariant, "btr.ai", TeslaVariant, SovietBasicArty, SovietAdvancedArty }, MinTime = DateTime.Minutes(16), }, - { Infantry = { "e3", "e1", "e1", "e3", "e8", "e1", "e8", "e1", "deso", "deso", "e2", "e3", "e4", "e1", "e1", "e1", "e1" }, Vehicles = { SovietMammothVariant, "3tnk.atomic", "btr.ai", "3tnk.atomic", "apoc", "v3rl" }, MinTime = DateTime.Minutes(16), }, + { Infantry = { "e3", "e1", "e1", "e3", "ttrp", "e1", "ttrp", "e1", "cmsr", "e2", "e3", "e4", "e1", "e1", "e1", "e1" }, Vehicles = { "3tnk", SovietMammothVariant, "btr.ai", TeslaVariant, SovietBasicArty, SovietAdvancedArty }, MinTime = DateTime.Minutes(16), }, + { Infantry = { "e3", "e1", "e1", "e3", "e8", "e1", "e8", "e1", "deso", "deso", "e2", "e3", "e4", "e1", "e1", "e1", "e1" }, Vehicles = { SovietMammothVariant, "3tnk.atomic", "btr.ai", "3tnk.atomic", "apoc", "v3rl" }, MinTime = DateTime.Minutes(16), }, ------ Anti-tank - { Infantry = { "e3", "e1", "e3", "e3", "e1", "e3", "e1", "e3", "e1", "e3", "e3", "e1", "e3", "e3", "e3", "e3" }, Vehicles = { "ttra", "ttra", "ttra", "ttra", "ttra", "ttra" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassHeavy" } }, + { Infantry = { "e3", "e1", "e3", "e3", "e1", "e3", "e1", "e3", "e1", "e3", "e3", "e1", "e3", "e3", "e3", "e3" }, Vehicles = { "ttra", "ttra", "ttra", "ttra", "ttra", "ttra" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassHeavy" } }, ------ Anti-infantry - { Infantry = { "e3", "e1", "e1", "e1", "e1", "shok", "shok", "ttrp", "e1", "e1", "e1", "e1", "e1", "e1", "e1", "e1", "e1" }, Vehicles = { "btr", "btr", "ttnk", "v2rl", "ttnk", "btr", "v2rl", "v2rl" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassInfantry" } }, + { Infantry = { "e3", "e1", "e1", "e1", "e1", "shok", "shok", "ttrp", "e1", "e1", "e1", "e1", "e1", "e1", "e1", "e1", "e1" }, Vehicles = { "btr", "btr", "ttnk", "v2rl", "ttnk", "btr", "v2rl", "v2rl" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassInfantry" } }, -- Specials - { Infantry = { "ttrp", "ttrp", "ttrp", "ttrp", "ttrp", "ttrp", "ttrp", "ttrp" }, Vehicles = { "ttnk", "ttra", "ttnk", "ttra", "ttnk", "ttnk", "ttra" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, - { Infantry = { "deso", "deso", "deso", "deso", "deso", "deso", "deso", "deso" }, Vehicles = { "4tnk.erad", "4tnk.erad", "4tnk.erad", "4tnk.erad", "4tnk.erad" }, MinTime = DateTime.Minutes(18), IsSpecial = true } + { Infantry = { "ttrp", "ttrp", "ttrp", "ttrp", "ttrp", "ttrp", "ttrp", "ttrp" }, Vehicles = { "ttnk", "ttra", "ttnk", "ttra", "ttnk", "ttnk", "ttra" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, + { Infantry = { "deso", "deso", "deso", "deso", "deso", "deso", "deso", "deso" }, Vehicles = { "4tnk.erad", "4tnk.erad", "4tnk.erad", "4tnk.erad", "4tnk.erad" }, MinTime = DateTime.Minutes(18), IsSpecial = true } }, GDI = { -- 0 to 10 minutes - { Infantry = {}, Vehicles = { HumveeOrGuardianDrone, HumveeOrGuardianDrone, HumveeOrGuardianDrone, HumveeOrGuardianDrone, HumveeOrGuardianDrone }, MaxTime = DateTime.Minutes(10) }, - { Infantry = { "n3", "n1", "n1", "n1", "n3", "n1", "n2", "n2" }, Vehicles = { "mtnk", "mtnk", "msam", "vulc", "vulc.ai" }, MaxTime = DateTime.Minutes(10) }, - { Infantry = { "n3", "n1", "n1", "n1", "n3", "n1", "n2", "n2", "n1", "n1", "n1", "n3" }, Vehicles = { "mtnk", "mtnk", HumveeOrGuardianDrone, HumveeOrGuardianDrone }, MaxTime = DateTime.Minutes(10) }, + { Infantry = {}, Vehicles = { HumveeOrGuardianDrone, HumveeOrGuardianDrone, HumveeOrGuardianDrone, HumveeOrGuardianDrone, HumveeOrGuardianDrone }, MaxTime = DateTime.Minutes(10) }, + { Infantry = { "n3", "n1", "n1", "n1", "n3", "n1", "n2", "n2" }, Vehicles = { "mtnk", "mtnk", "msam", "vulc", "vulc.ai" }, MaxTime = DateTime.Minutes(10) }, + { Infantry = { "n3", "n1", "n1", "n1", "n3", "n1", "n2", "n2", "n1", "n1", "n1", "n3" }, Vehicles = { "mtnk", "mtnk", HumveeOrGuardianDrone, HumveeOrGuardianDrone }, MaxTime = DateTime.Minutes(10) }, -- 10 minutes onwards - { Infantry = { "n3", "n1", "n1", "n1", "n3", "n1", "n1", "n1", "n3", "n1", "n1", "n1" }, Vehicles = { "mtnk", "mtnk", "vulc", "vulc.ai", "jugg" }, MinTime = DateTime.Minutes(10) }, - { Infantry = { "n3", "n1", "n1", "n1", "n3", "n1", "n1", "n1", "jjet", "n1", "n3", "n1", "n1", "n1" }, Vehicles = { "mtnk", "vulc", "vulc.ai", "msam", "vulc.ai" }, MinTime = DateTime.Minutes(10) }, - { Infantry = { "n3", "n1", "n1", "n1", "n3", "n1", "n1", "n1", "jjet", "n1", "n3", "n1", "n1", "n1" }, Vehicles = { "titn", "mtnk", "msam", "vulc", GDIMammothVariant }, MinTime = DateTime.Minutes(10) }, - { Infantry = { "jjet", "bjet", "jjet", "jjet", "bjet", "jjet" }, Vehicles = { TOWHumveeOrGuardianDrone, TOWHumveeOrGuardianDrone, TOWHumveeOrGuardianDrone, TOWHumveeOrGuardianDrone, TOWHumveeOrGuardianDrone }, MinTime = DateTime.Minutes(10) }, - { Infantry = {}, Vehicles = { "hsam", "hsam", "hsam", "hsam", "hsam", "hsam", "hsam" }, MinTime = DateTime.Minutes(10) }, + { Infantry = { "n3", "n1", "n1", "n1", "n3", "n1", "n1", "n1", "n3", "n1", "n1", "n1" }, Vehicles = { "mtnk", "mtnk", "vulc", "vulc.ai", "jugg" }, MinTime = DateTime.Minutes(10) }, + { Infantry = { "n3", "n1", "n1", "n1", "n3", "n1", "n1", "n1", "jjet", "n1", "n3", "n1", "n1", "n1" }, Vehicles = { "mtnk", "vulc", "vulc.ai", "msam", "vulc.ai" }, MinTime = DateTime.Minutes(10) }, + { Infantry = { "n3", "n1", "n1", "n1", "n3", "n1", "n1", "n1", "jjet", "n1", "n3", "n1", "n1", "n1" }, Vehicles = { "titn", "mtnk", "msam", "vulc", GDIMammothVariant }, MinTime = DateTime.Minutes(10) }, + { Infantry = { "jjet", "bjet", "jjet", "jjet", "bjet", "jjet" }, Vehicles = { TOWHumveeOrGuardianDrone, TOWHumveeOrGuardianDrone, TOWHumveeOrGuardianDrone, TOWHumveeOrGuardianDrone, TOWHumveeOrGuardianDrone }, MinTime = DateTime.Minutes(10) }, + { Infantry = {}, Vehicles = { "hsam", "hsam", "hsam", "hsam", "hsam", "hsam", "hsam" }, MinTime = DateTime.Minutes(10) }, -- 10 to 16 minutes - { Infantry = { "n1", "n1", "n3", "n1", "n1", "n1", "n1", "n1", "n3", "n1", "n1" }, Vehicles = { "htnk", WolverineOrXO, WolverineOrXO, "hsam", "vulc", GDIMammothVariant, WolverineOrXO }, MinTime = DateTime.Minutes(10), MaxTime = DateTime.Minutes(16) }, + { Infantry = { "n1", "n1", "n3", "n1", "n1", "n1", "n1", "n1", "n3", "n1", "n1" }, Vehicles = { "htnk", WolverineOrXO, WolverineOrXO, "hsam", "vulc", GDIMammothVariant, WolverineOrXO }, MinTime = DateTime.Minutes(10), MaxTime = DateTime.Minutes(16) }, -- 16 minutes onwards - { Infantry = { "n3", "n1", "n1", "n1", "n3", "n1", "n1", ZoneTrooperVariant, "n1", "n1", ZoneTrooperVariant, ZoneTrooperVariant }, Vehicles = { "mtnk", GDIMammothVariant, "vulc", "mtnk", "msam", GDIMammothVariant }, MinTime = DateTime.Minutes(16) }, - { Infantry = { "n3", "n1", "n1", "n1", "n3", "n1", "n1", "n1", "n1", "n1", "n3", "n1", "n1", "n1", "n1", "n3" }, Vehicles = { "vulc.ai", "disr", "vulc", "disr", "disr" }, MinTime = DateTime.Minutes(16) }, - { Infantry = { "n3", "rmbo", "n3", "n1", "n1", "n1", "n1", "n1", "n3", "n1", "n1", "n3", "n1", "n1" }, Vehicles = { GDIMammothVariant, "msam", "vulc", "msam", GDIMammothVariant }, MinTime = DateTime.Minutes(16) }, - { Infantry = { "n3", "n1", "n1", ZoneTrooperVariant, ZoneTrooperVariant, ZoneTrooperVariant, ZoneTrooperVariant, "n1", "n1" }, Vehicles = { GDIMammothVariant, "mtnk", WolverineOrXO, WolverineOrXO, GDIMammothVariant, GDIMammothVariant }, MinTime = DateTime.Minutes(16) }, + { Infantry = { "n3", "n1", "n1", "n1", "n3", "n1", "n1", ZoneTrooperVariant, "n1", "n1", ZoneTrooperVariant, ZoneTrooperVariant }, Vehicles = { "mtnk", GDIMammothVariant, "vulc", "mtnk", "msam", GDIMammothVariant }, MinTime = DateTime.Minutes(16) }, + { Infantry = { "n3", "n1", "n1", "n1", "n3", "n1", "n1", "n1", "n1", "n1", "n3", "n1", "n1", "n1", "n1", "n3" }, Vehicles = { "vulc.ai", "disr", "vulc", "disr", "disr" }, MinTime = DateTime.Minutes(16) }, + { Infantry = { "n3", "rmbo", "n3", "n1", "n1", "n1", "n1", "n1", "n3", "n1", "n1", "n3", "n1", "n1" }, Vehicles = { GDIMammothVariant, "msam", "vulc", "msam", GDIMammothVariant }, MinTime = DateTime.Minutes(16) }, + { Infantry = { "n3", "n1", "n1", ZoneTrooperVariant, ZoneTrooperVariant, ZoneTrooperVariant, ZoneTrooperVariant, "n1", "n1" }, Vehicles = { GDIMammothVariant, "mtnk", WolverineOrXO, WolverineOrXO, GDIMammothVariant, GDIMammothVariant }, MinTime = DateTime.Minutes(16) }, ------ Anti-tank - { Infantry = { "n3", "n3", "n3", "ztrp", "n3", "n3", "ztrp", "ztrp", "ztrp", "ztrp" }, Vehicles = { GDIMammothVariant, "xo", GDIMammothVariant, "xo", GDIMammothVariant, "xo" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassHeavy" } }, + { Infantry = { "n3", "n3", "n3", "ztrp", "n3", "n3", "ztrp", "ztrp", "ztrp", "ztrp" }, Vehicles = { GDIMammothVariant, "xo", GDIMammothVariant, "xo", GDIMammothVariant, "xo" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassHeavy" } }, ------ Anti-infantry - { Infantry = { "n3", "n1", "n1", "n1", "n3", "n1", "n1", "n1", "n3", "n1", "n1", "n1", "n1", "n1", "n1", "n1", "n1" }, Vehicles = { "wolv", "wolv", "vulc", "vulc.ai", "wolv", "disr", "jugg", "wolv" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassInfantry" } }, + { Infantry = { "n3", "n1", "n1", "n1", "n3", "n1", "n1", "n1", "n3", "n1", "n1", "n1", "n1", "n1", "n1", "n1", "n1" }, Vehicles = { "wolv", "wolv", "vulc", "vulc.ai", "wolv", "disr", "jugg", "wolv" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassInfantry" } }, -- Specials - { Infantry = { "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2" }, Vehicles = { "htnk.ion", "htnk.ion", "htnk.ion" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, - { Infantry = {}, Vehicles = { "memp", "memp", "memp", "memp", "memp" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, - { Infantry = {}, Vehicles = { "mtnk", "vulc", "thwk", "mtnk", "vulc", "thwk", "thwk", "thwk" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, - { Infantry = { "ztrp", "ztrp", "ztrp", "ztrp", "ztrp", "ztrp", "ztrp", "ztrp" }, Vehicles = { "titn.rail", "titn.rail", "titn.rail", "titn.rail", "titn.rail" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, + { Infantry = { "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2", "n2" }, Vehicles = { "htnk.ion", "htnk.ion", "htnk.ion" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, + { Infantry = {}, Vehicles = { "memp", "memp", "memp", "memp", "memp" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, + { Infantry = {}, Vehicles = { "mtnk", "vulc", "thwk", "mtnk", "vulc", "thwk", "thwk", "thwk" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, + { Infantry = { "ztrp", "ztrp", "ztrp", "ztrp", "ztrp", "ztrp", "ztrp", "ztrp" }, Vehicles = { "titn.rail", "titn.rail", "titn.rail", "titn.rail", "titn.rail" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, }, Nod = { -- 0 to 10 minutes - { Infantry = {}, Vehicles = { "bike", "bike", "bike", "bike" }, MaxTime = DateTime.Minutes(10) }, - { Infantry = { "n3", "n1", "n1", "n1", "n4", "n1", "n1", "n1" }, Vehicles = { "bggy", "bggy", "bike", "bike" }, MaxTime = DateTime.Minutes(10) }, - { Infantry = { "n3", "n1", "n1", "n4", "n1", "n1", "n1" }, Vehicles = { "ltnk", "bggy", "bike" }, MaxTime = DateTime.Minutes(10) }, + { Infantry = {}, Vehicles = { "bike", "bike", "bike", "bike" }, MaxTime = DateTime.Minutes(10) }, + { Infantry = { "n3", "n1", "n1", "n1", "n4", "n1", "n1", "n1" }, Vehicles = { "bggy", "bggy", "bike", "bike" }, MaxTime = DateTime.Minutes(10) }, + { Infantry = { "n3", "n1", "n1", "n4", "n1", "n1", "n1" }, Vehicles = { "ltnk", "bggy", "bike" }, MaxTime = DateTime.Minutes(10) }, -- 10 minutes onwards - { Infantry = { "n3", "n1", "n1", "n1", "n1", "n4", "n3", "bh", "n1", "n1", "n1", "n1", "n1", "n3", "n1", "n1" }, Vehicles = { "ltnk", "ltnk", FlameTankHeavyFlameTankOrHowitzer, "arty.nod" }, MinTime = DateTime.Minutes(10) }, - { Infantry = { "n3", "n1", "n1", "n1", "n4", "n1", "n3", "n1", "n1", "n1", "n1", "n1", "n1", "n3", "n1", "n1" }, Vehicles = { "ltnk", "arty.nod", FlameTankHeavyFlameTankOrHowitzer, "mlrs" }, MinTime = DateTime.Minutes(10) }, - { Infantry = { BasicCyborg, BasicCyborg, BasicCyborg, BasicCyborg, "tplr", AdvancedCyborg, "n1c", "n1c", BasicCyborg, AdvancedCyborg }, Vehicles = { "ltnk", FlameTankHeavyFlameTankOrHowitzer, "ltnk" }, MinTime = DateTime.Minutes(10) }, + { Infantry = { "n3", "n1", "n1", "n1", "n1", "n4", "n3", "bh", "n1", "n1", "n1", "n1", "n1", "n3", "n1", "n1" }, Vehicles = { "ltnk", "ltnk", FlameTankHeavyFlameTankOrHowitzer, "arty.nod" }, MinTime = DateTime.Minutes(10) }, + { Infantry = { "n3", "n1", "n1", "n1", "n4", "n1", "n3", "n1", "n1", "n1", "n1", "n1", "n1", "n3", "n1", "n1" }, Vehicles = { "ltnk", "arty.nod", FlameTankHeavyFlameTankOrHowitzer, "mlrs" }, MinTime = DateTime.Minutes(10) }, + { Infantry = { BasicCyborg, BasicCyborg, BasicCyborg, BasicCyborg, "tplr", AdvancedCyborg, "n1c", "n1c", BasicCyborg, AdvancedCyborg }, Vehicles = { "ltnk", FlameTankHeavyFlameTankOrHowitzer, "ltnk" }, MinTime = DateTime.Minutes(10) }, -- 10 to 16 minutes - { Infantry = {}, Vehicles = { "stnk.nod", "sapc.ai", "stnk.nod", "stnk.nod", "sapc.ai" }, MinTime = DateTime.Minutes(10), MaxTime = DateTime.Minutes(16) }, + { Infantry = {}, Vehicles = { "stnk.nod", "sapc.ai", "stnk.nod", "stnk.nod", "sapc.ai" }, MinTime = DateTime.Minutes(10), MaxTime = DateTime.Minutes(16) }, -- 16 minutes onwards - { Infantry = {}, Vehicles = { "stnk.nod", "stnk.nod", "sapc.ai", "stnk.nod", "stnk.nod", "sapc.ai", "stnk.nod", "stnk.nod" }, MinTime = DateTime.Minutes(16) }, - { Infantry = { BasicCyborg, BasicCyborg, BasicCyborg, BasicCyborg, BasicCyborg, AdvancedCyborg, "n1c", "n1c", BasicCyborg, BasicCyborg, "rmbc", AdvancedCyborg, AdvancedCyborg }, Vehicles = { "ltnk", "ltnk", FlameTankHeavyFlameTankOrHowitzer, "mlrs" }, MinTime = DateTime.Minutes(16) }, + { Infantry = {}, Vehicles = { "stnk.nod", "stnk.nod", "sapc.ai", "stnk.nod", "stnk.nod", "sapc.ai", "stnk.nod", "stnk.nod" }, MinTime = DateTime.Minutes(16) }, + { Infantry = { BasicCyborg, BasicCyborg, BasicCyborg, BasicCyborg, BasicCyborg, AdvancedCyborg, "n1c", "n1c", BasicCyborg, BasicCyborg, "rmbc", AdvancedCyborg, AdvancedCyborg }, Vehicles = { "ltnk", "ltnk", FlameTankHeavyFlameTankOrHowitzer, "mlrs" }, MinTime = DateTime.Minutes(16) }, ------ Anti-tank - { Infantry = { "n3c", "n3c", "n1", "enli", "n4", "n1", "n3c", "enli", "n3c", "n3c", "enli", "n1" }, Vehicles = { "ltnk", "ltnk", "wtnk", "wtnk", "stnk.nod", "ltnk" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassHeavy" } }, + { Infantry = { "n3c", "n3c", "n1", "enli", "n4", "n1", "n3c", "enli", "n3c", "n3c", "enli", "n1" }, Vehicles = { "ltnk", "ltnk", "wtnk", "wtnk", "stnk.nod", "ltnk" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassHeavy" } }, ------ Anti-infantry - { Infantry = { "n4", "n4", "n1", "n1", "n1", "n1", "n1", "n1", "n4", "n4", "n4", "n4", "n1", "n1", "n1", "n1", "n4", "n4" }, Vehicles = { "ltnk.laser", "ltnk.laser", "mlrs", FlameTankHeavyFlameTankOrHowitzer, FlameTankHeavyFlameTankOrHowitzer, "mlrs" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassInfantry" } }, + { Infantry = { "n4", "n4", "n1", "n1", "n1", "n1", "n1", "n1", "n4", "n4", "n4", "n4", "n1", "n1", "n1", "n1", "n4", "n4" }, Vehicles = { "ltnk.laser", "ltnk.laser", "mlrs", FlameTankHeavyFlameTankOrHowitzer, FlameTankHeavyFlameTankOrHowitzer, "mlrs" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassInfantry" } }, -- Specials - { Infantry = { "bh", "bh", "bh", "bh", "bh", "bh", "bh", "bh", "bh" }, Vehicles = { "hftk", "hftk", "hftk", "hftk", "hftk", "hftk" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, - { Infantry = { "n3", "n1", "n1", "n1", "n4", "n1", "n3", "n1", "n1", "n1", "n1", "n1", "n1", "n3", "n1", "n1" }, Vehicles = { "wtnk", "wtnk", "wtnk", "wtnk", "wtnk" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, - { Infantry = { }, Vehicles = { "bike", "bike", "bike", "bike", "bike", "bike", "bike", "bike", "bike", "bike", "bike", "bike", "bike", "bike" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, - { Infantry = { "rmbc", "rmbc", "rmbc", "rmbc", "rmbc", "enli", "rmbc", "rmbc", "enli" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, - { Infantry = { "reap", "reap", "reap", "reap", "reap", "reap", "reap", "reap", "reap" }, Vehicles = { "ltnk", "bike", "bike", "ltnk" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, + { Infantry = { "bh", "bh", "bh", "bh", "bh", "bh", "bh", "bh", "bh" }, Vehicles = { "hftk", "hftk", "hftk", "hftk", "hftk", "hftk" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, + { Infantry = { "n3", "n1", "n1", "n1", "n4", "n1", "n3", "n1", "n1", "n1", "n1", "n1", "n1", "n3", "n1", "n1" }, Vehicles = { "wtnk", "wtnk", "wtnk", "wtnk", "wtnk" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, + { Infantry = {}, Vehicles = { "bike", "bike", "bike", "bike", "bike", "bike", "bike", "bike", "bike", "bike", "bike", "bike", "bike", "bike" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, + { Infantry = { "rmbc", "rmbc", "rmbc", "rmbc", "rmbc", "enli", "rmbc", "rmbc", "enli" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, + { Infantry = { "reap", "reap", "reap", "reap", "reap", "reap", "reap", "reap", "reap" }, Vehicles = { "ltnk", "bike", "bike", "ltnk" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, }, Scrin = { -- 0 to 10 minutes - { Infantry = { "s3", "s1", "s1", "s1", "s3", "s3", "s4" }, Vehicles = { "intl.ai2", GunWalkerSeekerOrLacerator, "intl.ai2", GunWalkerSeekerOrLacerator }, MaxTime = DateTime.Minutes(10), }, + { Infantry = { "s3", "s1", "s1", "s1", "s3", "s3", "s4" }, Vehicles = { "intl.ai2", GunWalkerSeekerOrLacerator, "intl.ai2", GunWalkerSeekerOrLacerator }, MaxTime = DateTime.Minutes(10), }, -- 10 to 13 minutes - { Infantry = { "s3", "s1", "s1", "s1", "s1", "s1", "s2", "s2", "s3", "s3" }, Vehicles = { "intl.ai2", GunWalkerSeekerOrLacerator, "intl.ai2", CorrupterOrDevourer, GunWalkerSeekerOrLacerator, "tpod", GunWalkerSeekerOrLacerator, CorrupterOrDevourer }, MinTime = DateTime.Minutes(10), MaxTime = DateTime.Minutes(13), }, + { Infantry = { "s3", "s1", "s1", "s1", "s1", "s1", "s2", "s2", "s3", "s3" }, Vehicles = { "intl.ai2", GunWalkerSeekerOrLacerator, "intl.ai2", CorrupterOrDevourer, GunWalkerSeekerOrLacerator, "tpod", GunWalkerSeekerOrLacerator, CorrupterOrDevourer }, MinTime = DateTime.Minutes(10), MaxTime = DateTime.Minutes(13), }, -- 13 to 19 minutes - { Infantry = { "s3", "s1", "s1", "s1", "s1", "s1", "s2", "s2", "s3", "s3", "s3", "s4", "s4" }, Vehicles = { "intl.ai2", GunWalkerSeekerOrLacerator, "intl.ai2", CorrupterOrDevourer, GunWalkerSeekerOrLacerator, TripodVariant, CorrupterOrDevourer }, Aircraft = { PacOrDevastator }, MinTime = DateTime.Minutes(13), MaxTime = DateTime.Minutes(19), }, + { Infantry = { "s3", "s1", "s1", "s1", "s1", "s1", "s2", "s2", "s3", "s3", "s3", "s4", "s4" }, Vehicles = { "intl.ai2", GunWalkerSeekerOrLacerator, "intl.ai2", CorrupterOrDevourer, GunWalkerSeekerOrLacerator, TripodVariant, CorrupterOrDevourer }, Aircraft = { PacOrDevastator }, MinTime = DateTime.Minutes(13), MaxTime = DateTime.Minutes(19), }, ------ Anti-infantry - { Infantry = { "s3", "s1", "s1", "s1", "s1", "s1", "s2", "s2", "s3", "s3", "s1", "s1", "s1", "s2" }, Vehicles = { "shrw", "corr", "corr", "shrw", "corr", "shrw", "corr", "corr" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassInfantry" } }, + { Infantry = { "s3", "s1", "s1", "s1", "s1", "s1", "s2", "s2", "s3", "s3", "s1", "s1", "s1", "s2" }, Vehicles = { "shrw", "corr", "corr", "shrw", "corr", "shrw", "corr", "corr" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassInfantry" } }, ------ Anti-tank - { Infantry = { "s3", "s4", "s1", "s4", "s4", "s1", "s4", "s4", "s3", "s1", "s4", "s1", "s4", "s4", "s4" }, Vehicles = { "gunw", "devo", "devo", "gunw", "devo", "atmz", "devo", "tpod", "atmz", "devo" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassHeavy" } }, + { Infantry = { "s3", "s4", "s1", "s4", "s4", "s1", "s4", "s4", "s3", "s1", "s4", "s1", "s4", "s4", "s4" }, Vehicles = { "gunw", "devo", "devo", "gunw", "devo", "atmz", "devo", "tpod", "atmz", "devo" }, MinTime = DateTime.Minutes(16), RequiredTargetCharacteristics = { "MassHeavy" } }, -- 19 minutes onwards - { Infantry = { "s3", "s1", "s1", "s1", "s1", "s1", "s2", "s2", "s3", "s3", "s3", "s4", "s4" }, Vehicles = { "intl.ai2", GunWalkerSeekerOrLacerator, "intl.ai2", CorrupterOrDevourer, GunWalkerSeekerOrLacerator, TripodVariant, AtomizerObliteratorOrRuiner }, Aircraft = { PacOrDevastator, "pac" }, MinTime = DateTime.Minutes(19), }, + { Infantry = { "s3", "s1", "s1", "s1", "s1", "s1", "s2", "s2", "s3", "s3", "s3", "s4", "s4" }, Vehicles = { "intl.ai2", GunWalkerSeekerOrLacerator, "intl.ai2", CorrupterOrDevourer, GunWalkerSeekerOrLacerator, TripodVariant, AtomizerObliteratorOrRuiner }, Aircraft = { PacOrDevastator, "pac" }, MinTime = DateTime.Minutes(19), }, -- Specials - { Infantry = { "brst", "brst", "brst", "brst", "brst", "brst", "brst" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, - { Infantry = { "s3", "s3", "s2", "s2", "s2", "s2", "s2", "s2", "s2", "s2" }, Vehicles = { "tpod", "tpod", "tpod", "tpod", "tpod", "tpod" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, - { Infantry = { "s3", "s1", "s1", "s1", "s1", "s3", "s1", "s1", "s1", "s1", "s3", "s1", "s1", "s1", "s1", "s3", "s1", "s1", "s1", "s1", "s3", "s1", "s1", "s1" }, Vehicles = { "stcr", "gunw", "stcr", "gunw", "stcr", "gunw", "stcr" }, MinTime = DateTime.Minutes(18), IsSpecial = true } + { Infantry = { "brst", "brst", "brst", "brst", "brst", "brst", "brst" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, + { Infantry = { "s3", "s3", "s2", "s2", "s2", "s2", "s2", "s2", "s2", "s2" }, Vehicles = { "tpod", "tpod", "tpod", "tpod", "tpod", "tpod" }, MinTime = DateTime.Minutes(18), IsSpecial = true }, + { Infantry = { "s3", "s1", "s1", "s1", "s1", "s3", "s1", "s1", "s1", "s1", "s3", "s1", "s1", "s1", "s1", "s3", "s1", "s1", "s1", "s1", "s3", "s1", "s1", "s1" }, Vehicles = { "stcr", "gunw", "stcr", "gunw", "stcr", "gunw", "stcr" }, MinTime = DateTime.Minutes(18), IsSpecial = true } } } @@ -2527,23 +2545,23 @@ ScrinWaterCompositions = { { Vehicles = { "intl", "intl.ai2", "seek" }, }, { Vehicles = { "seek", "seek", "seek" }, }, { Vehicles = { "lace", "lace", "seek", "seek" }, }, - { Vehicles = { "devo", "intl.ai2", "ruin" }, MinTime = DateTime.Minutes(7) }, - { Vehicles = { "intl", "intl.ai2", { "seek", "lace" }, { "devo", "devo", "ruin" }, { "devo", "atmz", "ruin" } }, MinTime = DateTime.Minutes(12) } + { Vehicles = { "devo", "intl.ai2", "ruin" }, MinTime = DateTime.Minutes(7) }, + { Vehicles = { "intl", "intl.ai2", { "seek", "lace" }, { "devo", "devo", "ruin" }, { "devo", "atmz", "ruin" } }, MinTime = DateTime.Minutes(12) } }, vhard = { { Vehicles = { "intl", "intl.ai2", "seek" }, }, { Vehicles = { "seek", "seek", "seek" }, }, { Vehicles = { "lace", "lace", "lace" }, }, - { Vehicles = { "devo", "intl.ai2", "ruin" }, MinTime = DateTime.Minutes(7) }, - { Vehicles = { "intl", "intl.ai2", { "seek", "lace" }, { "devo", "devo", "ruin" }, { "devo", "atmz", "ruin" } }, MinTime = DateTime.Minutes(12) } + { Vehicles = { "devo", "intl.ai2", "ruin" }, MinTime = DateTime.Minutes(7) }, + { Vehicles = { "intl", "intl.ai2", { "seek", "lace" }, { "devo", "devo", "ruin" }, { "devo", "atmz", "ruin" } }, MinTime = DateTime.Minutes(12) } }, brutal = { - { Vehicles = { "intl", "intl.ai2", "seek", "atmz" }, MaxTime = DateTime.Minutes(7) }, - { Vehicles = { "seek", "seek", "seek", "seek" }, MaxTime = DateTime.Minutes(7) }, - { Vehicles = { "lace", "lace", "lace", "lace" }, MaxTime = DateTime.Minutes(8) }, - { Vehicles = { "lace", "lace", "lace", "lace", "lace", "lace", "lace", "lace" }, MinTime = DateTime.Minutes(8) }, - { Vehicles = { "devo", "intl.ai2", "ruin", "ruin" }, MinTime = DateTime.Minutes(7), MaxTime = DateTime.Minutes(13) }, - { Vehicles = { "intl", "intl.ai2", { "seek", "lace" }, { "seek", "lace" }, { "devo", "devo", "ruin" }, { "devo", "atmz", "ruin" }, "ruin", "ruin" }, MinTime = DateTime.Minutes(13) } + { Vehicles = { "intl", "intl.ai2", "seek", "atmz" }, MaxTime = DateTime.Minutes(7) }, + { Vehicles = { "seek", "seek", "seek", "seek" }, MaxTime = DateTime.Minutes(7) }, + { Vehicles = { "lace", "lace", "lace", "lace" }, MaxTime = DateTime.Minutes(8) }, + { Vehicles = { "lace", "lace", "lace", "lace", "lace", "lace", "lace", "lace" }, MinTime = DateTime.Minutes(8) }, + { Vehicles = { "devo", "intl.ai2", "ruin", "ruin" }, MinTime = DateTime.Minutes(7), MaxTime = DateTime.Minutes(13) }, + { Vehicles = { "intl", "intl.ai2", { "seek", "lace" }, { "seek", "lace" }, { "devo", "devo", "ruin" }, { "devo", "atmz", "ruin" }, "ruin", "ruin" }, MinTime = DateTime.Minutes(13) } } } diff --git a/mods/ca/scripts/coop.lua b/mods/ca/scripts/coop.lua index e113e7c8e0..acde0f79cb 100644 --- a/mods/ca/scripts/coop.lua +++ b/mods/ca/scripts/coop.lua @@ -303,26 +303,23 @@ GoodSpread = function() end local function SyncObjectives() - local texts = { - primary = "Primary", - secondary = "Secondary", - newPrimary = "New primary objective", - newSecondary = "New secondary objective" - } + local primaryType = UserInterface.GetFluentMessage("primary") + local newPrimaryMsg = UserInterface.GetFluentMessage("new-primary-objective") + local newSecondaryMsg = UserInterface.GetFluentMessage("new-secondary-objective") Trigger.OnObjectiveAdded(MainPlayer, function(_, obid) local description = MainPlayer.GetObjectiveDescription(obid) local type = MainPlayer.GetObjectiveType(obid) - local required = type == texts.primary + local required = type == primaryType ForEachPlayer(function(player) player.AddObjective(description, type, required) local OBJcolour = HSLColor.Yellow if required then - Media.DisplayMessageToPlayer(player, description, texts.newPrimary, OBJcolour) + Media.DisplayMessageToPlayer(player, description, newPrimaryMsg, OBJcolour) else OBJcolour = HSLColor.Gray - Media.DisplayMessageToPlayer(player, description, texts.newSecondary, OBJcolour) + Media.DisplayMessageToPlayer(player, description, newSecondaryMsg, OBJcolour) end end) end) @@ -348,7 +345,7 @@ local function SyncObjectives() player.MarkCompletedObjective(obid) if player.IsLocalPlayer then Media.PlaySoundNotification(player, "AlertBleep") - Media.DisplayMessage(MainPlayer.GetObjectiveDescription(obid), "Objective completed", HSLColor.LimeGreen) + Media.DisplayMessage(MainPlayer.GetObjectiveDescription(obid), UserInterface.GetFluentMessage("objective-completed"), HSLColor.LimeGreen) end end) end) @@ -358,7 +355,7 @@ local function SyncObjectives() player.MarkFailedObjective(obid) if player.IsLocalPlayer then Media.PlaySoundNotification(player, "AlertBleep") - Media.DisplayMessage(MainPlayer.GetObjectiveDescription(obid), "Objective failed", HSLColor.Red) + Media.DisplayMessage(MainPlayer.GetObjectiveDescription(obid), UserInterface.GetFluentMessage("objective-failed"), HSLColor.Red) end end) end) @@ -444,7 +441,7 @@ PlayerDefeatedOrDisconnected = function(player) local playerName = player.Name if selectedMessage ~= nil then local formattedMessage = string.gsub(selectedMessage, "PID", playerName) - Media.DisplayMessage(formattedMessage, "High Command", player.Color) + Media.DisplayMessage(formattedMessage, UserInterface.GetFluentMessage("high-command"), player.Color) end end end) @@ -854,7 +851,7 @@ local function SetExtraMines() Utils.Do(AllSpawners,function(SID) SID.Destroy() end) - Media.DisplayMessage("All resource spawners are deleted now. Good luck!") + Media.DisplayMessage(UserInterface.GetFluentMessage("resource-spawners-deleted")) end) end end