From 24b9482cc14b9b5bd6c225f0195bbf124ba316b0 Mon Sep 17 00:00:00 2001 From: Ivaylo Draganov Date: Sat, 8 Jan 2022 18:28:25 +0200 Subject: [PATCH] Add support for transient text notifications matching speech notifications --- OpenRA.Game/TextNotificationsManager.cs | 5 ++- OpenRA.Mods.Cnc/Activities/Infiltrate.cs | 2 + .../Traits/Infiltration/InfiltrateForCash.cs | 9 +++++ .../Infiltration/InfiltrateForExploration.cs | 9 +++++ .../Infiltration/InfiltrateForPowerOutage.cs | 9 +++++ .../Infiltration/InfiltrateForSupportPower.cs | 9 +++++ .../InfiltrateForSupportPowerReset.cs | 11 +++++- .../Traits/Infiltration/Infiltrates.cs | 3 ++ OpenRA.Mods.Common/Activities/RepairBridge.cs | 11 ++++-- OpenRA.Mods.Common/Activities/Resupply.cs | 2 + OpenRA.Mods.Common/Activities/Sell.cs | 1 + OpenRA.Mods.Common/Activities/Transform.cs | 2 + .../Orders/PlaceBuildingOrderGenerator.cs | 4 ++ .../Traits/Buildings/PrimaryBuilding.cs | 6 ++- .../Traits/Buildings/ProductionAirdrop.cs | 5 +++ .../Traits/Buildings/RallyPoint.cs | 6 ++- .../Traits/Buildings/RepairableBuilding.cs | 5 +++ .../Conditions/ToggleConditionOnOrder.cs | 8 ++++ .../Traits/Crates/CrateAction.cs | 7 +++- OpenRA.Mods.Common/Traits/GainsExperience.cs | 4 ++ .../Traits/Player/BaseAttackNotifier.cs | 30 +++++++++++---- .../Player/ConquestVictoryConditions.cs | 6 +++ .../Traits/Player/EnemyWatcher.cs | 7 ++-- .../Traits/Player/HarvesterAttackNotifier.cs | 7 +++- .../Traits/Player/MissionObjectives.cs | 6 +++ .../Traits/Player/PlaceBuilding.cs | 11 +++++- .../Traits/Player/PlayerResources.cs | 7 +++- .../Traits/Player/ProductionQueue.cs | 35 +++++++++++++++++- .../Traits/Player/ResourceStorageWarning.cs | 8 +++- .../Player/StrategicVictoryConditions.cs | 6 +++ .../Traits/Power/Player/PowerManager.cs | 4 ++ .../Traits/ProductionParadrop.cs | 6 ++- OpenRA.Mods.Common/Traits/RepairsBridges.cs | 5 ++- OpenRA.Mods.Common/Traits/RepairsUnits.cs | 10 ++++- OpenRA.Mods.Common/Traits/Sellable.cs | 5 ++- .../Traits/Sound/ActorLostNotification.cs | 13 ++++++- .../Traits/Sound/AnnounceOnSeen.cs | 7 ++++ .../Traits/Sound/CaptureNotification.cs | 12 +++++- .../Traits/SupportPowers/ParatroopersPower.cs | 9 ++++- .../Traits/SupportPowers/ProduceActorPower.cs | 16 +++++++- .../Traits/SupportPowers/SpawnActorPower.cs | 3 ++ .../Traits/SupportPowers/SupportPower.cs | 37 ++++++++++++++++--- .../SupportPowers/SupportPowerManager.cs | 2 + OpenRA.Mods.Common/Traits/Transforms.cs | 12 +++++- .../Traits/World/StartGameNotification.cs | 15 ++++++++ .../Widgets/Logic/Ingame/IngameMenuLogic.cs | 1 + .../Widgets/ProductionPaletteWidget.cs | 13 ++++++- .../Widgets/SupportPowersWidget.cs | 2 + OpenRA.Mods.D2k/Activities/SwallowActor.cs | 3 ++ OpenRA.Mods.D2k/Traits/AttackSwallow.cs | 2 + 50 files changed, 372 insertions(+), 46 deletions(-) diff --git a/OpenRA.Game/TextNotificationsManager.cs b/OpenRA.Game/TextNotificationsManager.cs index 46aacbcab5..e15e8e7f7a 100644 --- a/OpenRA.Game/TextNotificationsManager.cs +++ b/OpenRA.Game/TextNotificationsManager.cs @@ -26,12 +26,13 @@ namespace OpenRA SystemMessageLabel = "Battlefield Control"; } - public static void AddTransientLine(string text) + public static void AddTransientLine(string text, Player player) { if (string.IsNullOrEmpty(text)) return; - AddTextNotification(TextNotificationPool.Transients, SystemMessageLabel, text); + if (player == null || player == player.World.LocalPlayer) + AddTextNotification(TextNotificationPool.Transients, SystemMessageLabel, text); } public static void AddFeedbackLine(string text) diff --git a/OpenRA.Mods.Cnc/Activities/Infiltrate.cs b/OpenRA.Mods.Cnc/Activities/Infiltrate.cs index f0622304c0..8bb8dade73 100644 --- a/OpenRA.Mods.Cnc/Activities/Infiltrate.cs +++ b/OpenRA.Mods.Cnc/Activities/Infiltrate.cs @@ -71,6 +71,8 @@ namespace OpenRA.Mods.Cnc.Activities Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", infiltrates.Info.Notification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(infiltrates.Info.TextNotification, self.Owner); + if (infiltrates.Info.EnterBehaviour == EnterBehaviour.Dispose) self.Dispose(); else if (infiltrates.Info.EnterBehaviour == EnterBehaviour.Suicide) diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForCash.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForCash.cs index d8c00114bf..9acf1d8645 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForCash.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForCash.cs @@ -37,10 +37,16 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Sound the victim will hear when they get robbed.")] public readonly string InfiltratedNotification = null; + [Desc("Text notification the victim will see when they get robbed.")] + public readonly string InfiltratedTextNotification = null; + [NotificationReference("Speech")] [Desc("Sound the perpetrator will hear after successful infiltration.")] public readonly string InfiltrationNotification = null; + [Desc("Text notification the perpetrator will see after successful infiltration.")] + public readonly string InfiltrationTextNotification = null; + [Desc("Whether to show the cash tick indicators rising from the actor.")] public readonly bool ShowTicks = true; @@ -74,6 +80,9 @@ namespace OpenRA.Mods.Cnc.Traits if (info.InfiltrationNotification != null) Game.Sound.PlayNotification(self.World.Map.Rules, infiltrator.Owner, "Speech", info.InfiltrationNotification, infiltrator.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.InfiltratedTextNotification, self.Owner); + TextNotificationsManager.AddTransientLine(info.InfiltrationTextNotification, infiltrator.Owner); + if (info.ShowTicks) self.World.AddFrameEndTask(w => w.Add(new FloatingText(self.CenterPosition, infiltrator.Owner.Color, FloatingText.FormatCashTick(toGive), 30))); } diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForExploration.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForExploration.cs index 5b0d532745..09f2237bdf 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForExploration.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForExploration.cs @@ -26,10 +26,16 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Sound the victim will hear when they get sabotaged.")] public readonly string InfiltratedNotification = null; + [Desc("Text notification the victim will see when they get sabotaged.")] + public readonly string InfiltratedTextNotification = null; + [NotificationReference("Speech")] [Desc("Sound the perpetrator will hear after successful infiltration.")] public readonly string InfiltrationNotification = null; + [Desc("Text notification the perpetrator will see after successful infiltration.")] + public readonly string InfiltrationTextNotification = null; + public override object Create(ActorInitializer init) { return new InfiltrateForExploration(this); } } @@ -53,6 +59,9 @@ namespace OpenRA.Mods.Cnc.Traits if (info.InfiltrationNotification != null) Game.Sound.PlayNotification(self.World.Map.Rules, infiltrator.Owner, "Speech", info.InfiltrationNotification, infiltrator.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.InfiltratedTextNotification, self.Owner); + TextNotificationsManager.AddTransientLine(info.InfiltrationTextNotification, infiltrator.Owner); + infiltrator.Owner.Shroud.Explore(self.Owner.Shroud); var preventReset = self.Owner.PlayerActor.TraitsImplementing() .Any(p => p.PreventShroudReset(self)); diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForPowerOutage.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForPowerOutage.cs index 4c82e17c49..e00b48ff54 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForPowerOutage.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForPowerOutage.cs @@ -27,10 +27,16 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Sound the victim will hear when they get sabotaged.")] public readonly string InfiltratedNotification = null; + [Desc("Text notification the victim will see when they get sabotaged.")] + public readonly string InfiltratedTextNotification = null; + [NotificationReference("Speech")] [Desc("Sound the perpetrator will hear after successful infiltration.")] public readonly string InfiltrationNotification = null; + [Desc("Text notification the perpetrator will see after successful infiltration.")] + public readonly string InfiltrationTextNotification = null; + public override object Create(ActorInitializer init) { return new InfiltrateForPowerOutage(init.Self, this); } } @@ -56,6 +62,9 @@ namespace OpenRA.Mods.Cnc.Traits if (info.InfiltrationNotification != null) Game.Sound.PlayNotification(self.World.Map.Rules, infiltrator.Owner, "Speech", info.InfiltrationNotification, infiltrator.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.InfiltratedTextNotification, self.Owner); + TextNotificationsManager.AddTransientLine(info.InfiltrationTextNotification, infiltrator.Owner); + playerPower.TriggerPowerOutage(info.Duration); } diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPower.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPower.cs index f62cc89828..b356c0012a 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPower.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPower.cs @@ -28,10 +28,16 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Sound the victim will hear when technology gets stolen.")] public readonly string InfiltratedNotification = null; + [Desc("Text notification the victim will see when technology gets stolen.")] + public readonly string InfiltratedTextNotification = null; + [NotificationReference("Speech")] [Desc("Sound the perpetrator will hear after successful infiltration.")] public readonly string InfiltrationNotification = null; + [Desc("Text notification the perpetrator will see after successful infiltration.")] + public readonly string InfiltrationTextNotification = null; + public override object Create(ActorInitializer init) { return new InfiltrateForSupportPower(this); } } @@ -55,6 +61,9 @@ namespace OpenRA.Mods.Cnc.Traits if (info.InfiltrationNotification != null) Game.Sound.PlayNotification(self.World.Map.Rules, infiltrator.Owner, "Speech", info.InfiltrationNotification, infiltrator.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.InfiltratedTextNotification, self.Owner); + TextNotificationsManager.AddTransientLine(info.InfiltrationTextNotification, infiltrator.Owner); + infiltrator.World.AddFrameEndTask(w => w.CreateActor(info.Proxy, new TypeDictionary { new OwnerInit(infiltrator.Owner) diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPowerReset.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPowerReset.cs index d392bbfadf..e88c4691ad 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPowerReset.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPowerReset.cs @@ -22,13 +22,19 @@ namespace OpenRA.Mods.Cnc.Traits public readonly BitSet Types = default(BitSet); [NotificationReference("Speech")] - [Desc("Sound the victim will hear when technology gets stolen.")] + [Desc("Sound the victim will hear when they get sabotaged.")] public readonly string InfiltratedNotification = null; + [Desc("Text notification the victim will see when they get sabotaged.")] + public readonly string InfiltratedTextNotification = null; + [NotificationReference("Speech")] [Desc("Sound the perpetrator will hear after successful infiltration.")] public readonly string InfiltrationNotification = null; + [Desc("Text notification the perpetrator will see after successful infiltration.")] + public readonly string InfiltrationTextNotification = null; + public override object Create(ActorInitializer init) { return new InfiltrateForSupportPowerReset(this); } } @@ -52,6 +58,9 @@ namespace OpenRA.Mods.Cnc.Traits if (info.InfiltrationNotification != null) Game.Sound.PlayNotification(self.World.Map.Rules, infiltrator.Owner, "Speech", info.InfiltrationNotification, infiltrator.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.InfiltratedTextNotification, self.Owner); + TextNotificationsManager.AddTransientLine(info.InfiltrationTextNotification, infiltrator.Owner); + var manager = self.Owner.PlayerActor.Trait(); var powers = manager.GetPowersForActor(self).Where(sp => !sp.Disabled); foreach (var power in powers) diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/Infiltrates.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/Infiltrates.cs index f7b2184729..5077d951d7 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/Infiltrates.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/Infiltrates.cs @@ -41,6 +41,9 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Notification to play when a target is infiltrated.")] public readonly string Notification = null; + [Desc("Text notification to display when a target is infiltrated.")] + public readonly string TextNotification = null; + [Desc("Experience to grant to the infiltrating player.")] public readonly int PlayerExperience = 0; diff --git a/OpenRA.Mods.Common/Activities/RepairBridge.cs b/OpenRA.Mods.Common/Activities/RepairBridge.cs index 9affd3bbc7..f5a9cbfa6a 100644 --- a/OpenRA.Mods.Common/Activities/RepairBridge.cs +++ b/OpenRA.Mods.Common/Activities/RepairBridge.cs @@ -18,17 +18,19 @@ namespace OpenRA.Mods.Common.Activities class RepairBridge : Enter { readonly EnterBehaviour enterBehaviour; - readonly string notification; + readonly string speechNotification; + readonly string textNotification; Actor enterActor; BridgeHut enterHut; LegacyBridgeHut enterLegacyHut; - public RepairBridge(Actor self, in Target target, EnterBehaviour enterBehaviour, string notification, Color targetLineColor) + public RepairBridge(Actor self, in Target target, EnterBehaviour enterBehaviour, string speechNotification, string textNotification, Color targetLineColor) : base(self, target, targetLineColor) { this.enterBehaviour = enterBehaviour; - this.notification = notification; + this.speechNotification = speechNotification; + this.textNotification = textNotification; } bool CanEnterHut() @@ -75,7 +77,8 @@ namespace OpenRA.Mods.Common.Activities else if (enterHut != null) enterHut.Repair(self); - Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", notification, self.Owner.Faction.InternalName); + Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", speechNotification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(textNotification, self.Owner); if (enterBehaviour == EnterBehaviour.Dispose) self.Dispose(); diff --git a/OpenRA.Mods.Common/Activities/Resupply.cs b/OpenRA.Mods.Common/Activities/Resupply.cs index a4a393b875..fb4dca04db 100644 --- a/OpenRA.Mods.Common/Activities/Resupply.cs +++ b/OpenRA.Mods.Common/Activities/Resupply.cs @@ -262,6 +262,7 @@ namespace OpenRA.Mods.Common.Activities host.Actor.Owner.PlayerActor.TraitOrDefault()?.GiveExperience(repairsUnits.Info.PlayerExperience); Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", repairsUnits.Info.FinishRepairingNotification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(repairsUnits.Info.FinishRepairingTextNotification, self.Owner); activeResupplyTypes &= ~ResupplyType.Repair; return; @@ -279,6 +280,7 @@ namespace OpenRA.Mods.Common.Activities { played = true; Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", repairsUnits.Info.StartRepairingNotification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(repairsUnits.Info.StartRepairingTextNotification, self.Owner); } if (!playerResources.TakeCash(cost, true)) diff --git a/OpenRA.Mods.Common/Activities/Sell.cs b/OpenRA.Mods.Common/Activities/Sell.cs index e700b0c96a..7c6227c538 100644 --- a/OpenRA.Mods.Common/Activities/Sell.cs +++ b/OpenRA.Mods.Common/Activities/Sell.cs @@ -49,6 +49,7 @@ namespace OpenRA.Mods.Common.Activities self.World.AddFrameEndTask(w => w.Add(new FloatingText(self.CenterPosition, self.Owner.Color, FloatingText.FormatCashTick(refund), 30))); Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", sellableInfo.Notification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(sellableInfo.TextNotification, self.Owner); self.Dispose(); return false; diff --git a/OpenRA.Mods.Common/Activities/Transform.cs b/OpenRA.Mods.Common/Activities/Transform.cs index 7959738b6b..16e958e5a4 100644 --- a/OpenRA.Mods.Common/Activities/Transform.cs +++ b/OpenRA.Mods.Common/Activities/Transform.cs @@ -26,6 +26,7 @@ namespace OpenRA.Mods.Common.Activities public WAngle Facing = new WAngle(384); public string[] Sounds = Array.Empty(); public string Notification = null; + public string TextNotification = null; public int ForceHealthPercentage = 0; public bool SkipMakeAnims = false; public string Faction = null; @@ -95,6 +96,7 @@ namespace OpenRA.Mods.Common.Activities Game.Sound.PlayToPlayer(SoundType.World, self.Owner, s, self.CenterPosition); Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Notification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(TextNotification, self.Owner); var init = new TypeDictionary { diff --git a/OpenRA.Mods.Common/Orders/PlaceBuildingOrderGenerator.cs b/OpenRA.Mods.Common/Orders/PlaceBuildingOrderGenerator.cs index ca52f818f1..d60223d6ac 100644 --- a/OpenRA.Mods.Common/Orders/PlaceBuildingOrderGenerator.cs +++ b/OpenRA.Mods.Common/Orders/PlaceBuildingOrderGenerator.cs @@ -180,6 +180,8 @@ namespace OpenRA.Mods.Common.Orders if (!AcceptsPlug(topLeft, plugInfo)) { Game.Sound.PlayNotification(world.Map.Rules, owner, "Speech", notification, owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(placeBuildingInfo.CannotPlaceTextNotification, owner); + yield break; } } @@ -192,6 +194,8 @@ namespace OpenRA.Mods.Common.Orders yield return order; Game.Sound.PlayNotification(world.Map.Rules, owner, "Speech", notification, owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(placeBuildingInfo.CannotPlaceTextNotification, owner); + yield break; } diff --git a/OpenRA.Mods.Common/Traits/Buildings/PrimaryBuilding.cs b/OpenRA.Mods.Common/Traits/Buildings/PrimaryBuilding.cs index 5a254b3b56..e729582885 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/PrimaryBuilding.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/PrimaryBuilding.cs @@ -34,9 +34,12 @@ namespace OpenRA.Mods.Common.Traits public readonly string PrimaryCondition = null; [NotificationReference("Speech")] - [Desc("The speech notification to play when selecting a primary building.")] + [Desc("Speech notification to play when selecting a primary building.")] public readonly string SelectionNotification = null; + [Desc("Text notification to display when selecting a primary building.")] + public readonly string SelectionTextNotification = null; + [Desc("List of production queues for which the primary flag should be set.", "If empty, the list given in the `Produces` property of the `" + nameof(Production) + "` trait will be used.")] public readonly string[] ProductionQueues = Array.Empty(); @@ -113,6 +116,7 @@ namespace OpenRA.Mods.Common.Traits primaryToken = self.GrantCondition(Info.PrimaryCondition); Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.SelectionNotification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(Info.SelectionTextNotification, self.Owner); } else if (primaryToken != Actor.InvalidConditionToken) primaryToken = self.RevokeCondition(primaryToken); diff --git a/OpenRA.Mods.Common/Traits/Buildings/ProductionAirdrop.cs b/OpenRA.Mods.Common/Traits/Buildings/ProductionAirdrop.cs index 376293f0bc..c0eacb0860 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/ProductionAirdrop.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/ProductionAirdrop.cs @@ -21,8 +21,12 @@ namespace OpenRA.Mods.Common.Traits public class ProductionAirdropInfo : ProductionInfo { [NotificationReference("Speech")] + [Desc("Speech notification to play when a unit is delivered.")] public readonly string ReadyAudio = "Reinforce"; + [Desc("Text notification to display when a unit is delivered.")] + public readonly string ReadyTextNotification = null; + [FieldLoader.Require] [ActorReference(typeof(AircraftInfo))] [Desc("Cargo aircraft used for delivery. Must have the `" + nameof(Aircraft) + "` trait.")] @@ -112,6 +116,7 @@ namespace OpenRA.Mods.Common.Traits self.World.AddFrameEndTask(ww => DoProduction(self, producee, exit, productionType, inits)); Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.ReadyAudio, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.ReadyTextNotification, self.Owner); })); actor.QueueActivity(new FlyOffMap(actor, Target.FromCell(w, endPos))); diff --git a/OpenRA.Mods.Common/Traits/Buildings/RallyPoint.cs b/OpenRA.Mods.Common/Traits/Buildings/RallyPoint.cs index 1a494ae7e5..833998284e 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/RallyPoint.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/RallyPoint.cs @@ -46,9 +46,12 @@ namespace OpenRA.Mods.Common.Traits public readonly CVec[] Path = Array.Empty(); [NotificationReference("Speech")] - [Desc("The speech notification to play when setting a new rallypoint.")] + [Desc("Speech notification to play when setting a new rallypoint.")] public readonly string Notification = null; + [Desc("Text notification to display when setting a new rallypoint.")] + public readonly string TextNotification = null; + [Desc("Used to group equivalent actors to allow force-setting a rallypoint (e.g. for Primary production).")] public readonly string ForceSetType = null; @@ -101,6 +104,7 @@ namespace OpenRA.Mods.Common.Traits if (order.OrderID == OrderID) { Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.Notification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(Info.TextNotification, self.Owner); return new Order(order.OrderID, self, target, queued) { diff --git a/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs b/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs index 338bf0e5e4..3d5f3ab8c2 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs @@ -49,6 +49,8 @@ namespace OpenRA.Mods.Common.Traits [NotificationReference("Speech")] public readonly string RepairingNotification = null; + public readonly string RepairingTextNotification = null; + public override object Create(ActorInitializer init) { return new RepairableBuilding(init.Self, this); } } @@ -111,7 +113,10 @@ namespace OpenRA.Mods.Common.Traits return; Repairers.Add(player); + Game.Sound.PlayNotification(self.World.Map.Rules, player, "Speech", Info.RepairingNotification, player.Faction.InternalName); + TextNotificationsManager.AddTransientLine(Info.RepairingTextNotification, self.Owner); + UpdateCondition(self); } diff --git a/OpenRA.Mods.Common/Traits/Conditions/ToggleConditionOnOrder.cs b/OpenRA.Mods.Common/Traits/Conditions/ToggleConditionOnOrder.cs index 76e2b0812b..378300a4c7 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/ToggleConditionOnOrder.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/ToggleConditionOnOrder.cs @@ -31,12 +31,16 @@ namespace OpenRA.Mods.Common.Traits [NotificationReference("Speech")] public readonly string EnabledSpeech = null; + public readonly string EnabledTextNotification = null; + [NotificationReference("Sounds")] public readonly string DisabledSound = null; [NotificationReference("Speech")] public readonly string DisabledSpeech = null; + public readonly string DisabledTextNotification = null; + public override object Create(ActorInitializer init) { return new ToggleConditionOnOrder(this); } } @@ -62,6 +66,8 @@ namespace OpenRA.Mods.Common.Traits if (Info.EnabledSpeech != null) Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.EnabledSpeech, self.Owner.Faction.InternalName); + + TextNotificationsManager.AddTransientLine(Info.EnabledTextNotification, self.Owner); } else if (!granted && conditionToken != Actor.InvalidConditionToken) { @@ -72,6 +78,8 @@ namespace OpenRA.Mods.Common.Traits if (Info.DisabledSpeech != null) Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.DisabledSpeech, self.Owner.Faction.InternalName); + + TextNotificationsManager.AddTransientLine(Info.DisabledTextNotification, self.Owner); } } diff --git a/OpenRA.Mods.Common/Traits/Crates/CrateAction.cs b/OpenRA.Mods.Common/Traits/Crates/CrateAction.cs index c5dd60e0e1..78c6ef8ce9 100644 --- a/OpenRA.Mods.Common/Traits/Crates/CrateAction.cs +++ b/OpenRA.Mods.Common/Traits/Crates/CrateAction.cs @@ -36,9 +36,12 @@ namespace OpenRA.Mods.Common.Traits public readonly string Sound = null; [NotificationReference("Speech")] - [Desc("Notification to play when the crate is collected.")] + [Desc("Speech notification to play when the crate is collected.")] public readonly string Notification = null; + [Desc("Text notification to display when the crate is collected.")] + public readonly string TextNotification = null; + [Desc("The earliest time (in ticks) that this crate action can occur on.")] public readonly int TimeDelay = 0; @@ -92,6 +95,8 @@ namespace OpenRA.Mods.Common.Traits Game.Sound.PlayNotification(self.World.Map.Rules, collector.Owner, "Speech", Info.Notification, collector.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(Info.TextNotification, collector.Owner); + if (Info.Image != null && Info.Sequence != null) collector.World.AddFrameEndTask(w => w.Add(new SpriteEffect(collector, w, Info.Image, Info.Sequence, Info.Palette))); } diff --git a/OpenRA.Mods.Common/Traits/GainsExperience.cs b/OpenRA.Mods.Common/Traits/GainsExperience.cs index c1106071b3..551696e28d 100644 --- a/OpenRA.Mods.Common/Traits/GainsExperience.cs +++ b/OpenRA.Mods.Common/Traits/GainsExperience.cs @@ -49,6 +49,8 @@ namespace OpenRA.Mods.Common.Traits [NotificationReference("Sounds")] public readonly string LevelUpNotification = null; + public readonly string LevelUpTextNotification = null; + public override object Create(ActorInitializer init) { return new GainsExperience(init, this); } } @@ -119,6 +121,8 @@ namespace OpenRA.Mods.Common.Traits if (!silent) { Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Sounds", info.LevelUpNotification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.LevelUpTextNotification, self.Owner); + if (info.LevelUpImage != null && info.LevelUpSequence != null) self.World.AddFrameEndTask(w => w.Add(new SpriteEffect(self, w, info.LevelUpImage, info.LevelUpSequence, info.LevelUpPalette))); } diff --git a/OpenRA.Mods.Common/Traits/Player/BaseAttackNotifier.cs b/OpenRA.Mods.Common/Traits/Player/BaseAttackNotifier.cs index 19ce928e38..955788c8fe 100644 --- a/OpenRA.Mods.Common/Traits/Player/BaseAttackNotifier.cs +++ b/OpenRA.Mods.Common/Traits/Player/BaseAttackNotifier.cs @@ -28,14 +28,20 @@ namespace OpenRA.Mods.Common.Traits public readonly int RadarPingDuration = 250; [NotificationReference("Speech")] - [Desc("The audio notification type to play.")] + [Desc("Speech notification type to play.")] public readonly string Notification = "BaseAttack"; + [Desc("Text notification to display.")] + public string TextNotification = null; + [NotificationReference("Speech")] - [Desc("The audio notification to play to allies when under attack.", + [Desc("Speech notification to play to allies when under attack.", "Won't play a notification to allies if this is null.")] public readonly string AllyNotification = null; + [Desc("Text notification to display to allies when under attack.")] + public string AllyTextNotification = null; + public override object Create(ActorInitializer init) { return new BaseAttackNotifier(init.Self, this); } } @@ -55,6 +61,11 @@ namespace OpenRA.Mods.Common.Traits void INotifyDamage.Damaged(Actor self, AttackInfo e) { + var localPlayer = self.World.LocalPlayer; + + if (localPlayer == null || localPlayer.Spectating) + return; + if (e.Attacker == null) return; @@ -74,12 +85,17 @@ namespace OpenRA.Mods.Common.Traits if (Game.RunTime > lastAttackTime + info.NotifyInterval) { var rules = self.World.Map.Rules; - Game.Sound.PlayNotification(rules, self.Owner, "Speech", info.Notification, self.Owner.Faction.InternalName); - if (info.AllyNotification != null) - foreach (Player p in self.World.Players) - if (p != self.Owner && p.IsAlliedWith(self.Owner) && p != e.Attacker.Owner) - Game.Sound.PlayNotification(rules, p, "Speech", info.AllyNotification, p.Faction.InternalName); + if (self.Owner == localPlayer) + { + Game.Sound.PlayNotification(rules, self.Owner, "Speech", info.Notification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.TextNotification, self.Owner); + } + else if (localPlayer.IsAlliedWith(self.Owner) && localPlayer != e.Attacker.Owner) + { + Game.Sound.PlayNotification(rules, localPlayer, "Speech", info.AllyNotification, localPlayer.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.AllyTextNotification, localPlayer); + } radarPings?.Add(() => self.Owner.IsAlliedWith(self.World.RenderPlayer), self.CenterPosition, info.RadarPingColor, info.RadarPingDuration); diff --git a/OpenRA.Mods.Common/Traits/Player/ConquestVictoryConditions.cs b/OpenRA.Mods.Common/Traits/Player/ConquestVictoryConditions.cs index 2a02a0266a..b9e57e28ac 100644 --- a/OpenRA.Mods.Common/Traits/Player/ConquestVictoryConditions.cs +++ b/OpenRA.Mods.Common/Traits/Player/ConquestVictoryConditions.cs @@ -103,7 +103,10 @@ namespace OpenRA.Mods.Common.Traits Game.RunAfterDelay(info.NotificationDelay, () => { if (Game.IsCurrentWorld(player.World) && player == player.World.LocalPlayer) + { Game.Sound.PlayNotification(player.World.Map.Rules, player, "Speech", mo.Info.LoseNotification, player.Faction.InternalName); + TextNotificationsManager.AddTransientLine(mo.Info.LoseTextNotification, player); + } }); } @@ -116,7 +119,10 @@ namespace OpenRA.Mods.Common.Traits Game.RunAfterDelay(info.NotificationDelay, () => { if (Game.IsCurrentWorld(player.World) && player == player.World.LocalPlayer) + { Game.Sound.PlayNotification(player.World.Map.Rules, player, "Speech", mo.Info.WinNotification, player.Faction.InternalName); + TextNotificationsManager.AddTransientLine(mo.Info.WinTextNotification, player); + } }); } } diff --git a/OpenRA.Mods.Common/Traits/Player/EnemyWatcher.cs b/OpenRA.Mods.Common/Traits/Player/EnemyWatcher.cs index 09ffcfb74f..45de6b5aab 100644 --- a/OpenRA.Mods.Common/Traits/Player/EnemyWatcher.cs +++ b/OpenRA.Mods.Common/Traits/Player/EnemyWatcher.cs @@ -89,8 +89,9 @@ namespace OpenRA.Mods.Common.Traits if (lastKnownActorIds.Contains(actor.Actor.ActorID)) continue; - // Should we play a sound notification? - var playNotification = !playedNotifications.Contains(actor.Trait.Info.Notification) && ticksBeforeNextNotification <= 0; + // Should we play a notification? + var notificationId = $"{actor.Trait.Info.Notification} {actor.Trait.Info.TextNotification}"; + var playNotification = !playedNotifications.Contains(notificationId) && ticksBeforeNextNotification <= 0; // Notify the actor that he has been discovered foreach (var trait in actor.Actor.TraitsImplementing()) @@ -109,7 +110,7 @@ namespace OpenRA.Mods.Common.Traits if (!playNotification) continue; - playedNotifications.Add(actor.Trait.Info.Notification); + playedNotifications.Add(notificationId); announcedAny = true; } diff --git a/OpenRA.Mods.Common/Traits/Player/HarvesterAttackNotifier.cs b/OpenRA.Mods.Common/Traits/Player/HarvesterAttackNotifier.cs index 3ae1b698dd..087159bc26 100644 --- a/OpenRA.Mods.Common/Traits/Player/HarvesterAttackNotifier.cs +++ b/OpenRA.Mods.Common/Traits/Player/HarvesterAttackNotifier.cs @@ -28,9 +28,12 @@ namespace OpenRA.Mods.Common.Traits public readonly int RadarPingDuration = 250; [NotificationReference("Speech")] - [Desc("The audio notification type to play.")] + [Desc("Speech notification type to play.")] public readonly string Notification = "HarvesterAttack"; + [Desc("Text notification to display.")] + public string TextNotification = null; + public override object Create(ActorInitializer init) { return new HarvesterAttackNotifier(init.Self, this); } } @@ -61,6 +64,8 @@ namespace OpenRA.Mods.Common.Traits if (Game.RunTime > lastAttackTime + info.NotifyInterval) { Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.Notification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.TextNotification, self.Owner); + radarPings?.Add(() => self.Owner.IsAlliedWith(self.World.RenderPlayer), self.CenterPosition, info.RadarPingColor, info.RadarPingDuration); lastAttackTime = Game.RunTime; diff --git a/OpenRA.Mods.Common/Traits/Player/MissionObjectives.cs b/OpenRA.Mods.Common/Traits/Player/MissionObjectives.cs index 4c7ffa3fb5..abd9077362 100644 --- a/OpenRA.Mods.Common/Traits/Player/MissionObjectives.cs +++ b/OpenRA.Mods.Common/Traits/Player/MissionObjectives.cs @@ -54,12 +54,18 @@ namespace OpenRA.Mods.Common.Traits [NotificationReference("Speech")] public readonly string WinNotification = null; + public readonly string WinTextNotification = null; + [NotificationReference("Speech")] public readonly string LoseNotification = null; + public readonly string LoseTextNotification = null; + [NotificationReference("Speech")] public readonly string LeaveNotification = null; + public readonly string LeaveTextNotification = null; + public override object Create(ActorInitializer init) { return new MissionObjectives(init.Self.Owner, this); } } diff --git a/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs b/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs index 2b8efe39d0..e140ccb663 100644 --- a/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs +++ b/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs @@ -26,12 +26,19 @@ namespace OpenRA.Mods.Common.Traits public readonly int NewOptionsNotificationDelay = 10; [NotificationReference("Speech")] - [Desc("Notification to play after building placement if new construction options are available.")] + [Desc("Speech notification to play after building placement if new construction options are available.")] public readonly string NewOptionsNotification = null; + [Desc("Text notification to display after building placement if new construction options are available.")] + public readonly string NewOptionsTextNotification = null; + [NotificationReference("Speech")] + [Desc("Speech notification to play if building placement is not possible.")] public readonly string CannotPlaceNotification = null; + [Desc("Text notification to display if building placement is not possible.")] + public readonly string CannotPlaceTextNotification = null; + [Desc("Hotkey to toggle between PlaceBuildingVariants when placing a structure.")] public readonly HotkeyReference ToggleVariantKey = new HotkeyReference(); @@ -223,6 +230,8 @@ namespace OpenRA.Mods.Common.Traits void PlayNotification(Actor self) { Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.NewOptionsNotification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.NewOptionsTextNotification, self.Owner); + triggerNotification = false; tick = 0; } diff --git a/OpenRA.Mods.Common/Traits/Player/PlayerResources.cs b/OpenRA.Mods.Common/Traits/Player/PlayerResources.cs index b9bc169fc2..748f88eab1 100644 --- a/OpenRA.Mods.Common/Traits/Player/PlayerResources.cs +++ b/OpenRA.Mods.Common/Traits/Player/PlayerResources.cs @@ -44,6 +44,9 @@ namespace OpenRA.Mods.Common.Traits [Desc("Speech notification to play when the player does not have any funds.")] public readonly string InsufficientFundsNotification = null; + [Desc("Text notification to display when the player does not have any funds.")] + public readonly string InsufficientFundsTextNotification = null; + [Desc("Delay (in ticks) during which warnings will be muted.")] public readonly int InsufficientFundsNotificationInterval = 30000; @@ -179,11 +182,11 @@ namespace OpenRA.Mods.Common.Traits { if (Cash + Resources < num) { - if (notifyLowFunds && !string.IsNullOrEmpty(Info.InsufficientFundsNotification) && - Game.RunTime > lastNotificationTime + Info.InsufficientFundsNotificationInterval) + if (notifyLowFunds && Game.RunTime > lastNotificationTime + Info.InsufficientFundsNotificationInterval) { lastNotificationTime = Game.RunTime; Game.Sound.PlayNotification(owner.World.Map.Rules, owner, "Speech", Info.InsufficientFundsNotification, owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(Info.InsufficientFundsTextNotification, owner); } return false; diff --git a/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs b/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs index a9e15ff007..a7aa277143 100644 --- a/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs +++ b/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs @@ -61,18 +61,29 @@ namespace OpenRA.Mods.Common.Traits "The filename of the audio is defined per faction in notifications.yaml.")] public readonly string ReadyAudio = null; + [Desc("Notification displayed when production is complete.")] + public readonly string ReadyTextNotification = null; + [NotificationReference("Speech")] [Desc("Notification played when you can't train another actor", "when the build limit exceeded or the exit is jammed.", "The filename of the audio is defined per faction in notifications.yaml.")] public readonly string BlockedAudio = null; + [Desc("Notification displayed when you can't train another actor", + "when the build limit exceeded or the exit is jammed.")] + public readonly string BlockedTextNotification = null; + [NotificationReference("Speech")] [Desc("Notification played when you can't queue another actor", "when the queue length limit is exceeded.", "The filename of the audio is defined per faction in notifications.yaml.")] public readonly string LimitedAudio = null; + [Desc("Notification displayed when you can't queue another actor", + "when the queue length limit is exceeded.")] + public readonly string LimitedTextNotification = null; + [NotificationReference("Speech")] [Desc("Notification played when you can't place a building.", "Overrides PlaceBuilding.CannotPlaceNotification for this queue.", @@ -84,16 +95,25 @@ namespace OpenRA.Mods.Common.Traits "The filename of the audio is defined per faction in notifications.yaml.")] public readonly string QueuedAudio = null; + [Desc("Notification displayed when user clicks on the build palette icon.")] + public readonly string QueuedTextNotification = null; + [NotificationReference("Speech")] [Desc("Notification played when player right-clicks on the build palette icon.", "The filename of the audio is defined per faction in notifications.yaml.")] public readonly string OnHoldAudio = null; + [Desc("Notification displayed when player right-clicks on the build palette icon.")] + public readonly string OnHoldTextNotification = null; + [NotificationReference("Speech")] [Desc("Notification played when player right-clicks on a build palette icon that is already on hold.", "The filename of the audio is defined per faction in notifications.yaml.")] public readonly string CancelledAudio = null; + [Desc("Notification displayed when player right-clicks on a build palette icon that is already on hold.")] + public readonly string CancelledTextNotification = null; + public override object Create(ActorInitializer init) { return new ProductionQueue(init, this); } public void RulesetLoaded(Ruleset rules, ActorInfo ai) @@ -333,9 +353,10 @@ namespace OpenRA.Mods.Common.Traits } } - public bool CanQueue(ActorInfo actor, out string notificationAudio) + public bool CanQueue(ActorInfo actor, out string notificationAudio, out string notificationText) { notificationAudio = Info.BlockedAudio; + notificationText = Info.BlockedTextNotification; var bi = actor.TraitInfoOrDefault(); if (bi == null) @@ -346,6 +367,7 @@ namespace OpenRA.Mods.Common.Traits if (Info.QueueLimit > 0 && Queue.Count >= Info.QueueLimit) { notificationAudio = Info.LimitedAudio; + notificationText = Info.LimitedTextNotification; return false; } @@ -353,6 +375,7 @@ namespace OpenRA.Mods.Common.Traits if (Info.ItemLimit > 0 && queueCount >= Info.ItemLimit) { notificationAudio = Info.LimitedAudio; + notificationText = Info.LimitedTextNotification; return false; } @@ -366,6 +389,7 @@ namespace OpenRA.Mods.Common.Traits } notificationAudio = Info.QueuedAudio; + notificationText = Info.QueuedTextNotification; return true; } @@ -424,13 +448,22 @@ namespace OpenRA.Mods.Common.Traits var isBuilding = unit.HasTraitInfo(); if (isBuilding && !hasPlayedSound) + { hasPlayedSound = Game.Sound.PlayNotification(rules, self.Owner, "Speech", Info.ReadyAudio, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(Info.ReadyTextNotification, self.Owner); + } else if (!isBuilding) { if (BuildUnit(unit)) + { Game.Sound.PlayNotification(rules, self.Owner, "Speech", Info.ReadyAudio, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(Info.ReadyTextNotification, self.Owner); + } else if (!hasPlayedSound && time > 0) + { hasPlayedSound = Game.Sound.PlayNotification(rules, self.Owner, "Speech", Info.BlockedAudio, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(Info.BlockedTextNotification, self.Owner); + } } })), !order.Queued); } diff --git a/OpenRA.Mods.Common/Traits/Player/ResourceStorageWarning.cs b/OpenRA.Mods.Common/Traits/Player/ResourceStorageWarning.cs index eebbf73b6a..f8fdcc9bf3 100644 --- a/OpenRA.Mods.Common/Traits/Player/ResourceStorageWarning.cs +++ b/OpenRA.Mods.Common/Traits/Player/ResourceStorageWarning.cs @@ -24,9 +24,12 @@ namespace OpenRA.Mods.Common.Traits public readonly int Threshold = 80; [NotificationReference("Speech")] - [Desc("The speech to play for the warning.")] + [Desc("Speech to play for the warning.")] public readonly string Notification = "SilosNeeded"; + [Desc("Text to display for the warning.")] + public readonly string TextNotification = null; + public override object Create(ActorInitializer init) { return new ResourceStorageWarning(init.Self, this); } } @@ -50,7 +53,10 @@ namespace OpenRA.Mods.Common.Traits var owner = self.Owner; if (resources.Resources > info.Threshold * resources.ResourceCapacity / 100) + { Game.Sound.PlayNotification(self.World.Map.Rules, owner, "Speech", info.Notification, owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.TextNotification, owner); + } lastSiloAdviceTime = Game.RunTime; } diff --git a/OpenRA.Mods.Common/Traits/Player/StrategicVictoryConditions.cs b/OpenRA.Mods.Common/Traits/Player/StrategicVictoryConditions.cs index 47be5b3dcf..52241368ad 100644 --- a/OpenRA.Mods.Common/Traits/Player/StrategicVictoryConditions.cs +++ b/OpenRA.Mods.Common/Traits/Player/StrategicVictoryConditions.cs @@ -140,7 +140,10 @@ namespace OpenRA.Mods.Common.Traits Game.RunAfterDelay(info.NotificationDelay, () => { if (Game.IsCurrentWorld(player.World) && player == player.World.LocalPlayer) + { Game.Sound.PlayNotification(player.World.Map.Rules, player, "Speech", mo.Info.LoseNotification, player.Faction.InternalName); + TextNotificationsManager.AddTransientLine(mo.Info.LoseTextNotification, player); + } }); } @@ -153,7 +156,10 @@ namespace OpenRA.Mods.Common.Traits Game.RunAfterDelay(info.NotificationDelay, () => { if (Game.IsCurrentWorld(player.World) && player == player.World.LocalPlayer) + { Game.Sound.PlayNotification(player.World.Map.Rules, player, "Speech", mo.Info.WinNotification, player.Faction.InternalName); + TextNotificationsManager.AddTransientLine(mo.Info.WinTextNotification, player); + } }); } } diff --git a/OpenRA.Mods.Common/Traits/Power/Player/PowerManager.cs b/OpenRA.Mods.Common/Traits/Power/Player/PowerManager.cs index 9ae5941dc9..08762e5b5b 100644 --- a/OpenRA.Mods.Common/Traits/Power/Player/PowerManager.cs +++ b/OpenRA.Mods.Common/Traits/Power/Player/PowerManager.cs @@ -25,6 +25,8 @@ namespace OpenRA.Mods.Common.Traits [NotificationReference("Speech")] public readonly string SpeechNotification = null; + public readonly string TextNotification = null; + public override object Create(ActorInitializer init) { return new PowerManager(init.Self, this); } } @@ -161,6 +163,8 @@ namespace OpenRA.Mods.Common.Traits if (isLowPower && Game.RunTime > lastPowerAdviceTime + info.AdviceInterval) { Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.SpeechNotification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.TextNotification, self.Owner); + lastPowerAdviceTime = Game.RunTime; } diff --git a/OpenRA.Mods.Common/Traits/ProductionParadrop.cs b/OpenRA.Mods.Common/Traits/ProductionParadrop.cs index d9e72a896c..b2366ff666 100644 --- a/OpenRA.Mods.Common/Traits/ProductionParadrop.cs +++ b/OpenRA.Mods.Common/Traits/ProductionParadrop.cs @@ -29,9 +29,12 @@ namespace OpenRA.Mods.Common.Traits public readonly string ChuteSound = null; [NotificationReference("Speech")] - [Desc("Notification to play when dropping the unit.")] + [Desc("Speech notification to play when dropping the unit.")] public readonly string ReadyAudio = null; + [Desc("Text notification to display when dropping the unit.")] + public readonly string ReadyTextNotification = null; + public override object Create(ActorInitializer init) { return new ProductionParadrop(init, this); } } @@ -97,6 +100,7 @@ namespace OpenRA.Mods.Common.Traits self.World.AddFrameEndTask(ww => DoProduction(self, producee, exit?.Info, productionType, inits)); Game.Sound.Play(SoundType.World, info.ChuteSound, self.CenterPosition); Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.ReadyAudio, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.ReadyTextNotification, self.Owner); })); actor.QueueActivity(new Fly(actor, Target.FromCell(w, endPos))); diff --git a/OpenRA.Mods.Common/Traits/RepairsBridges.cs b/OpenRA.Mods.Common/Traits/RepairsBridges.cs index 1a24362fe4..e3b5525dd8 100644 --- a/OpenRA.Mods.Common/Traits/RepairsBridges.cs +++ b/OpenRA.Mods.Common/Traits/RepairsBridges.cs @@ -42,6 +42,9 @@ namespace OpenRA.Mods.Common.Traits [Desc("Speech notification to play when a bridge is repaired.")] public readonly string RepairNotification = null; + [Desc("Text notification to display when a bridge is repaired.")] + public readonly string RepairTextNotification = null; + public override object Create(ActorInitializer init) { return new RepairsBridges(this); } } @@ -107,7 +110,7 @@ namespace OpenRA.Mods.Common.Traits else return; - self.QueueActivity(order.Queued, new RepairBridge(self, order.Target, info.EnterBehaviour, info.RepairNotification, info.TargetLineColor)); + self.QueueActivity(order.Queued, new RepairBridge(self, order.Target, info.EnterBehaviour, info.RepairNotification, info.RepairTextNotification, info.TargetLineColor)); self.ShowTargetLines(); } } diff --git a/OpenRA.Mods.Common/Traits/RepairsUnits.cs b/OpenRA.Mods.Common/Traits/RepairsUnits.cs index 71674a1b12..9c26d791a2 100644 --- a/OpenRA.Mods.Common/Traits/RepairsUnits.cs +++ b/OpenRA.Mods.Common/Traits/RepairsUnits.cs @@ -28,13 +28,19 @@ namespace OpenRA.Mods.Common.Traits public readonly BitSet RepairDamageTypes = default(BitSet); [NotificationReference("Speech")] - [Desc("The sound played when starting to repair a unit.")] + [Desc("Speech notification played when starting to repair a unit.")] public readonly string StartRepairingNotification = null; + [Desc("Text notification displayed when starting to repair a unit.")] + public readonly string StartRepairingTextNotification = null; + [NotificationReference("Speech")] - [Desc("The sound played when repairing a unit is done.")] + [Desc("Speech notification played when repairing a unit is done.")] public readonly string FinishRepairingNotification = null; + [Desc("Text notification displayed when repairing a unit is done.")] + public readonly string FinishRepairingTextNotification = null; + [Desc("Experience gained by the player owning this actor for repairing an allied unit.")] public readonly int PlayerExperience = 0; diff --git a/OpenRA.Mods.Common/Traits/Sellable.cs b/OpenRA.Mods.Common/Traits/Sellable.cs index e5c69555e1..0be14670d6 100644 --- a/OpenRA.Mods.Common/Traits/Sellable.cs +++ b/OpenRA.Mods.Common/Traits/Sellable.cs @@ -27,9 +27,12 @@ namespace OpenRA.Mods.Common.Traits public readonly string[] SellSounds = Array.Empty(); [NotificationReference("Speech")] - [Desc("The audio notification type to play.")] + [Desc("Speech notification to play.")] public readonly string Notification = null; + [Desc("Text notification to display.")] + public string TextNotification = null; + [Desc("Whether to show the cash tick indicators rising from the actor.")] public readonly bool ShowTicks = true; diff --git a/OpenRA.Mods.Common/Traits/Sound/ActorLostNotification.cs b/OpenRA.Mods.Common/Traits/Sound/ActorLostNotification.cs index ce7c092c20..8dc43a7fb4 100644 --- a/OpenRA.Mods.Common/Traits/Sound/ActorLostNotification.cs +++ b/OpenRA.Mods.Common/Traits/Sound/ActorLostNotification.cs @@ -16,8 +16,12 @@ namespace OpenRA.Mods.Common.Traits.Sound class ActorLostNotificationInfo : ConditionalTraitInfo { [NotificationReference("Speech")] + [Desc("Speech notification to play.")] public readonly string Notification = "UnitLost"; + [Desc("Text notification to display.")] + public readonly string TextNotification = null; + public readonly bool NotifyAll = false; public override object Create(ActorInitializer init) { return new ActorLostNotification(this); } @@ -33,8 +37,15 @@ namespace OpenRA.Mods.Common.Traits.Sound if (IsTraitDisabled) return; - var player = Info.NotifyAll ? self.World.LocalPlayer : self.Owner; + var localPlayer = self.World.LocalPlayer; + + if (localPlayer == null || localPlayer.Spectating) + return; + + var player = Info.NotifyAll ? localPlayer : self.Owner; + Game.Sound.PlayNotification(self.World.Map.Rules, player, "Speech", Info.Notification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(Info.TextNotification, player); } } } diff --git a/OpenRA.Mods.Common/Traits/Sound/AnnounceOnSeen.cs b/OpenRA.Mods.Common/Traits/Sound/AnnounceOnSeen.cs index 03f8a53337..96957ebb14 100644 --- a/OpenRA.Mods.Common/Traits/Sound/AnnounceOnSeen.cs +++ b/OpenRA.Mods.Common/Traits/Sound/AnnounceOnSeen.cs @@ -23,8 +23,12 @@ namespace OpenRA.Mods.Common.Traits.Sound public readonly bool PingRadar = false; [NotificationReference("Speech")] + [Desc("Speech notification to play.")] public readonly string Notification = null; + [Desc("Text notification to display.")] + public readonly string TextNotification = null; + public readonly bool AnnounceNeutrals = false; public override object Create(ActorInitializer init) { return new AnnounceOnSeen(init.Self, this); } @@ -56,6 +60,9 @@ namespace OpenRA.Mods.Common.Traits.Sound if (discoverer != null && !string.IsNullOrEmpty(Info.Notification)) Game.Sound.PlayNotification(self.World.Map.Rules, discoverer, "Speech", Info.Notification, discoverer.Faction.InternalName); + if (discoverer != null) + TextNotificationsManager.AddTransientLine(Info.TextNotification, discoverer); + // Radar notification if (Info.PingRadar) radarPings.Value?.Add(() => true, self.CenterPosition, Color.Red, 50); diff --git a/OpenRA.Mods.Common/Traits/Sound/CaptureNotification.cs b/OpenRA.Mods.Common/Traits/Sound/CaptureNotification.cs index 206fc5b989..5662a57b8a 100644 --- a/OpenRA.Mods.Common/Traits/Sound/CaptureNotification.cs +++ b/OpenRA.Mods.Common/Traits/Sound/CaptureNotification.cs @@ -17,16 +17,22 @@ namespace OpenRA.Mods.Common.Traits.Sound public class CaptureNotificationInfo : TraitInfo { [NotificationReference("Speech")] - [Desc("The speech notification to play to the new owner.")] + [Desc("Speech notification to play to the new owner.")] public readonly string Notification = "BuildingCaptured"; + [Desc("Text notification to display to the new owner.")] + public readonly string TextNotification = null; + [Desc("Specifies if Notification is played with the voice of the new owners faction.")] public readonly bool NewOwnerVoice = true; [NotificationReference("Speech")] - [Desc("The speech notification to play to the old owner.")] + [Desc("Speech notification to play to the old owner.")] public readonly string LoseNotification = null; + [Desc("Text notification to display to the old owner.")] + public readonly string LoseTextNotification = null; + [Desc("Specifies if LoseNotification is played with the voice of the new owners faction.")] public readonly bool LoseNewOwnerVoice = false; @@ -45,9 +51,11 @@ namespace OpenRA.Mods.Common.Traits.Sound { var faction = info.NewOwnerVoice ? newOwner.Faction.InternalName : oldOwner.Faction.InternalName; Game.Sound.PlayNotification(self.World.Map.Rules, newOwner, "Speech", info.Notification, faction); + TextNotificationsManager.AddTransientLine(info.TextNotification, newOwner); var loseFaction = info.LoseNewOwnerVoice ? newOwner.Faction.InternalName : oldOwner.Faction.InternalName; Game.Sound.PlayNotification(self.World.Map.Rules, oldOwner, "Speech", info.LoseNotification, loseFaction); + TextNotificationsManager.AddTransientLine(info.LoseTextNotification, oldOwner); } } } diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/ParatroopersPower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/ParatroopersPower.cs index ce9134be49..0d6fd7b304 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/ParatroopersPower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/ParatroopersPower.cs @@ -27,9 +27,12 @@ namespace OpenRA.Mods.Common.Traits public readonly WVec SquadOffset = new WVec(-1536, 1536, 0); [NotificationReference("Speech")] - [Desc("Notification to play when entering the drop zone.")] + [Desc("Speech notification to play when entering the drop zone.")] public readonly string ReinforcementsArrivedSpeechNotification = null; + [Desc("Text notification to display when entering the drop zone.")] + public readonly string ReinforcementsArrivedTextNotification = null; + [Desc("Number of facings that the delivery aircraft may approach from.")] public readonly int QuantizedFacings = 32; @@ -134,9 +137,13 @@ namespace OpenRA.Mods.Common.Traits RemoveBeacon(beacon); if (!aircraftInRange.Any(kv => kv.Value)) + { Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.ReinforcementsArrivedSpeechNotification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.ReinforcementsArrivedTextNotification, self.Owner); + } + aircraftInRange[a] = true; }; diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/ProduceActorPower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/ProduceActorPower.cs index c9eaebc606..d069a32bc1 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/ProduceActorPower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/ProduceActorPower.cs @@ -28,15 +28,21 @@ namespace OpenRA.Mods.Common.Traits public readonly string Type = null; [NotificationReference("Speech")] - [Desc("Notification played when production is activated.", + [Desc("Speech notification played when production is activated.", "The filename of the audio is defined per faction in notifications.yaml.")] public readonly string ReadyAudio = null; + [Desc("Text notification displayed when production is activated.")] + public readonly string ReadyTextNotification = null; + [NotificationReference("Speech")] - [Desc("Notification played when the exit is jammed.", + [Desc("Speech notification played when the exit is jammed.", "The filename of the audio is defined per faction in notifications.yaml.")] public readonly string BlockedAudio = null; + [Desc("Text notification displayed when the exit is jammed.")] + public readonly string BlockedTextNotification = null; + public override object Create(ActorInitializer init) { return new ProduceActorPower(init, this); } } @@ -91,9 +97,15 @@ namespace OpenRA.Mods.Common.Traits } if (activated) + { Game.Sound.PlayNotification(self.World.Map.Rules, manager.Self.Owner, "Speech", info.ReadyAudio, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.ReadyTextNotification, manager.Self.Owner); + } else + { Game.Sound.PlayNotification(self.World.Map.Rules, manager.Self.Owner, "Speech", info.BlockedAudio, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.BlockedTextNotification, manager.Self.Owner); + } } } } diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/SpawnActorPower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/SpawnActorPower.cs index 41d9654c91..3b29f678f6 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/SpawnActorPower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/SpawnActorPower.cs @@ -95,6 +95,9 @@ namespace OpenRA.Mods.Common.Traits Game.Sound.PlayToPlayer(SoundType.UI, manager.Self.Owner, Info.SelectTargetSound); Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.SelectTargetSpeechNotification, self.Owner.Faction.InternalName); + + TextNotificationsManager.AddTransientLine(Info.SelectTargetTextNotification, manager.Self.Owner); + self.World.OrderGenerator = new SelectSpawnActorPowerTarget(order, manager, this, MouseButton.Left); } diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs index 392df47ea5..1ce7097174 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs @@ -53,36 +53,50 @@ namespace OpenRA.Mods.Common.Traits [NotificationReference("Speech")] public readonly string DetectedSpeechNotification = null; + public readonly string DetectedTextNotification = null; + public readonly string BeginChargeSound = null; [NotificationReference("Speech")] public readonly string BeginChargeSpeechNotification = null; + public readonly string BeginChargeTextNotification = null; + public readonly string EndChargeSound = null; [NotificationReference("Speech")] public readonly string EndChargeSpeechNotification = null; + public readonly string EndChargeTextNotification = null; + public readonly string SelectTargetSound = null; [NotificationReference("Speech")] public readonly string SelectTargetSpeechNotification = null; + public readonly string SelectTargetTextNotification = null; + public readonly string InsufficientPowerSound = null; [NotificationReference("Speech")] public readonly string InsufficientPowerSpeechNotification = null; + public readonly string InsufficientPowerTextNotification = null; + public readonly string LaunchSound = null; [NotificationReference("Speech")] public readonly string LaunchSpeechNotification = null; + public readonly string LaunchTextNotification = null; + public readonly string IncomingSound = null; [NotificationReference("Speech")] public readonly string IncomingSpeechNotification = null; + public readonly string IncomingTextNotification = null; + [Desc("Defines to which players the timer is shown.")] public readonly PlayerRelationship DisplayTimerRelationships = PlayerRelationship.None; @@ -147,11 +161,12 @@ namespace OpenRA.Mods.Common.Traits { base.Created(self); - var renderPlayer = self.World.RenderPlayer; - if (renderPlayer != null && renderPlayer != self.Owner) + var player = self.World.LocalPlayer; + if (player != null && player != self.Owner) { Game.Sound.Play(SoundType.UI, Info.DetectedSound); - Game.Sound.PlayNotification(self.World.Map.Rules, renderPlayer, "Speech", info.DetectedSpeechNotification, renderPlayer.Faction.InternalName); + Game.Sound.PlayNotification(self.World.Map.Rules, player, "Speech", info.DetectedSpeechNotification, player.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.DetectedTextNotification, player); } } @@ -165,6 +180,8 @@ namespace OpenRA.Mods.Common.Traits Game.Sound.PlayToPlayer(SoundType.UI, self.Owner, Info.BeginChargeSound); Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.BeginChargeSpeechNotification, self.Owner.Faction.InternalName); + + TextNotificationsManager.AddTransientLine(Info.BeginChargeTextNotification, self.Owner); } public virtual void Charged(Actor self, string key) @@ -173,6 +190,8 @@ namespace OpenRA.Mods.Common.Traits Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.EndChargeSpeechNotification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(Info.EndChargeTextNotification, self.Owner); + foreach (var notify in self.TraitsImplementing()) notify.Charged(self); } @@ -199,13 +218,19 @@ namespace OpenRA.Mods.Common.Traits public virtual void PlayLaunchSounds() { - var renderPlayer = Self.World.RenderPlayer; - var isAllied = Self.Owner.IsAlliedWith(renderPlayer); + var localPlayer = Self.World.LocalPlayer; + + if (localPlayer == null || localPlayer.Spectating) + return; + + var isAllied = Self.Owner.IsAlliedWith(localPlayer); Game.Sound.Play(SoundType.UI, isAllied ? Info.LaunchSound : Info.IncomingSound); - var toPlayer = isAllied ? renderPlayer ?? Self.Owner : renderPlayer; + var toPlayer = isAllied ? localPlayer ?? Self.Owner : localPlayer; var speech = isAllied ? Info.LaunchSpeechNotification : Info.IncomingSpeechNotification; Game.Sound.PlayNotification(Self.World.Map.Rules, toPlayer, "Speech", speech, toPlayer.Faction.InternalName); + + TextNotificationsManager.AddTransientLine(isAllied ? Info.LaunchTextNotification : Info.IncomingTextNotification, toPlayer); } public IEnumerable CellsMatching(CPos location, char[] footprint, CVec dimensions) diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs index d082c81513..43bb27689e 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs @@ -231,6 +231,8 @@ namespace OpenRA.Mods.Common.Traits Game.Sound.PlayNotification(power.Self.World.Map.Rules, power.Self.Owner, "Speech", Info.SelectTargetSpeechNotification, power.Self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(Info.SelectTargetTextNotification, power.Self.Owner); + power.SelectTarget(power.Self, Key, Manager); } diff --git a/OpenRA.Mods.Common/Traits/Transforms.cs b/OpenRA.Mods.Common/Traits/Transforms.cs index fc22c8c1c5..b32f904699 100644 --- a/OpenRA.Mods.Common/Traits/Transforms.cs +++ b/OpenRA.Mods.Common/Traits/Transforms.cs @@ -39,13 +39,19 @@ namespace OpenRA.Mods.Common.Traits public readonly string[] NoTransformSounds = Array.Empty(); [NotificationReference("Speech")] - [Desc("Notification to play when transforming.")] + [Desc("Speech notification to play when transforming.")] public readonly string TransformNotification = null; + [Desc("Text notification to display when transforming.")] + public readonly string TransformTextNotification = null; + [NotificationReference("Speech")] - [Desc("Notification to play when the transformation is blocked.")] + [Desc("Speech notification to play when the transformation is blocked.")] public readonly string NoTransformNotification = null; + [Desc("Text notification to display when the transformation is blocked.")] + public readonly string NoTransformTextNotification = null; + [CursorReference] [Desc("Cursor to display when able to (un)deploy the actor.")] public readonly string DeployCursor = "deploy"; @@ -97,6 +103,7 @@ namespace OpenRA.Mods.Common.Traits Facing = Info.Facing, Sounds = Info.TransformSounds, Notification = Info.TransformNotification, + TextNotification = Info.TransformTextNotification, Faction = faction }; } @@ -136,6 +143,7 @@ namespace OpenRA.Mods.Common.Traits Game.Sound.PlayToPlayer(SoundType.World, self.Owner, s); Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.NoTransformNotification, self.Owner.Faction.InternalName); + TextNotificationsManager.AddTransientLine(Info.NoTransformTextNotification, self.Owner); return; } diff --git a/OpenRA.Mods.Common/Traits/World/StartGameNotification.cs b/OpenRA.Mods.Common/Traits/World/StartGameNotification.cs index f1393193f3..1ce78af6a1 100644 --- a/OpenRA.Mods.Common/Traits/World/StartGameNotification.cs +++ b/OpenRA.Mods.Common/Traits/World/StartGameNotification.cs @@ -20,12 +20,18 @@ namespace OpenRA.Mods.Common.Traits [NotificationReference("Speech")] public readonly string Notification = "StartGame"; + public readonly string TextNotification = null; + [NotificationReference("Speech")] public readonly string LoadedNotification = "GameLoaded"; + public readonly string LoadedTextNotification = null; + [NotificationReference("Speech")] public readonly string SavedNotification = "GameSaved"; + public readonly string SavedTextNotification = null; + public override object Create(ActorInitializer init) { return new StartGameNotification(this); } } @@ -40,19 +46,28 @@ namespace OpenRA.Mods.Common.Traits void IWorldLoaded.WorldLoaded(World world, WorldRenderer wr) { if (!world.IsLoadingGameSave) + { Game.Sound.PlayNotification(world.Map.Rules, null, "Speech", info.Notification, world.RenderPlayer == null ? null : world.RenderPlayer.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.TextNotification, null); + } } void INotifyGameLoaded.GameLoaded(World world) { if (!world.IsReplay) + { Game.Sound.PlayNotification(world.Map.Rules, null, "Speech", info.LoadedNotification, world.RenderPlayer == null ? null : world.RenderPlayer.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.LoadedTextNotification, null); + } } void INotifyGameSaved.GameSaved(World world) { if (!world.IsReplay) + { Game.Sound.PlayNotification(world.Map.Rules, null, "Speech", info.SavedNotification, world.RenderPlayer == null ? null : world.RenderPlayer.Faction.InternalName); + TextNotificationsManager.AddTransientLine(info.SavedTextNotification, null); + } } } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameMenuLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameMenuLogic.cs index f63f65ea77..2985b6efdb 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameMenuLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameMenuLogic.cs @@ -121,6 +121,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic { var faction = world.LocalPlayer?.Faction.InternalName; Game.Sound.PlayNotification(world.Map.Rules, null, "Speech", moi.LeaveNotification, faction); + TextNotificationsManager.AddTransientLine(moi.LeaveTextNotification, null); } } diff --git a/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs b/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs index 4a2993552e..405d90f619 100644 --- a/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs @@ -305,6 +305,8 @@ namespace OpenRA.Mods.Common.Widgets // Resume a paused item Game.Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Sounds", ClickSound, null); Game.Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Speech", CurrentQueue.Info.QueuedAudio, World.LocalPlayer.Faction.InternalName); + TextNotificationsManager.AddTransientLine(CurrentQueue.Info.QueuedTextNotification, World.LocalPlayer); + World.IssueOrder(Order.PauseProduction(CurrentQueue.Actor, icon.Name, false)); return true; } @@ -315,10 +317,13 @@ namespace OpenRA.Mods.Common.Widgets { // Queue a new item Game.Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Sounds", ClickSound, null); - var canQueue = CurrentQueue.CanQueue(buildable, out var notification); + var canQueue = CurrentQueue.CanQueue(buildable, out var notification, out var textNotification); if (!CurrentQueue.AllQueued().Any()) + { Game.Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Speech", notification, World.LocalPlayer.Faction.InternalName); + TextNotificationsManager.AddTransientLine(textNotification, World.LocalPlayer); + } if (canQueue) { @@ -342,12 +347,16 @@ namespace OpenRA.Mods.Common.Widgets { // Instantly cancel items that haven't started, have finished, or if the queue doesn't support pausing Game.Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Speech", CurrentQueue.Info.CancelledAudio, World.LocalPlayer.Faction.InternalName); + TextNotificationsManager.AddTransientLine(CurrentQueue.Info.CancelledTextNotification, World.LocalPlayer); + World.IssueOrder(Order.CancelProduction(CurrentQueue.Actor, icon.Name, handleCount)); } else { // Pause an existing item Game.Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Speech", CurrentQueue.Info.OnHoldAudio, World.LocalPlayer.Faction.InternalName); + TextNotificationsManager.AddTransientLine(CurrentQueue.Info.OnHoldTextNotification, World.LocalPlayer); + World.IssueOrder(Order.PauseProduction(CurrentQueue.Actor, icon.Name, true)); } @@ -362,6 +371,8 @@ namespace OpenRA.Mods.Common.Widgets // Directly cancel, skipping "on-hold" Game.Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Sounds", ClickSound, null); Game.Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Speech", CurrentQueue.Info.CancelledAudio, World.LocalPlayer.Faction.InternalName); + TextNotificationsManager.AddTransientLine(CurrentQueue.Info.CancelledTextNotification, World.LocalPlayer); + World.IssueOrder(Order.CancelProduction(CurrentQueue.Actor, icon.Name, handleCount)); return true; diff --git a/OpenRA.Mods.Common/Widgets/SupportPowersWidget.cs b/OpenRA.Mods.Common/Widgets/SupportPowersWidget.cs index cea3a914db..1f5c7d64d4 100644 --- a/OpenRA.Mods.Common/Widgets/SupportPowersWidget.cs +++ b/OpenRA.Mods.Common/Widgets/SupportPowersWidget.cs @@ -173,6 +173,8 @@ namespace OpenRA.Mods.Common.Widgets Game.Sound.PlayToPlayer(SoundType.UI, spm.Self.Owner, clicked.Power.Info.InsufficientPowerSound); Game.Sound.PlayNotification(spm.Self.World.Map.Rules, spm.Self.Owner, "Speech", clicked.Power.Info.InsufficientPowerSpeechNotification, spm.Self.Owner.Faction.InternalName); + + TextNotificationsManager.AddTransientLine(clicked.Power.Info.InsufficientPowerTextNotification, spm.Self.Owner); } else clicked.Power.Target(); diff --git a/OpenRA.Mods.D2k/Activities/SwallowActor.cs b/OpenRA.Mods.D2k/Activities/SwallowActor.cs index 12b91da486..b76e4c5d82 100644 --- a/OpenRA.Mods.D2k/Activities/SwallowActor.cs +++ b/OpenRA.Mods.D2k/Activities/SwallowActor.cs @@ -82,6 +82,9 @@ namespace OpenRA.Mods.D2k.Activities foreach (var player in affectedPlayers) self.World.AddFrameEndTask(w => w.Add(new MapNotificationEffect(player, "Speech", swallow.Info.WormAttackNotification, 25, true, attackPosition, Color.Red))); + if (affectedPlayers.Contains(self.World.LocalPlayer)) + TextNotificationsManager.AddTransientLine(swallow.Info.WormAttackTextNotification, self.World.LocalPlayer); + var barrel = armament.CheckFire(self, facing, target); if (barrel == null) return false; diff --git a/OpenRA.Mods.D2k/Traits/AttackSwallow.cs b/OpenRA.Mods.D2k/Traits/AttackSwallow.cs index 7c343cd5fa..4e8af81fb5 100644 --- a/OpenRA.Mods.D2k/Traits/AttackSwallow.cs +++ b/OpenRA.Mods.D2k/Traits/AttackSwallow.cs @@ -38,6 +38,8 @@ namespace OpenRA.Mods.D2k.Traits [NotificationReference("Speech")] public readonly string WormAttackNotification = "WormAttack"; + public readonly string WormAttackTextNotification = "Worm attack."; + public override object Create(ActorInitializer init) { return new AttackSwallow(init.Self, this); } }