#region Copyright & License Information /* * Copyright (c) The OpenRA Developers and Contributors * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. For more * information, see COPYING. */ #endregion using System; using System.Collections.Generic; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { public abstract class SupportPowerInfo : PausableConditionalTraitInfo { [Desc("Measured in ticks.")] public readonly int ChargeInterval = 0; public readonly string IconImage = "icon"; [SequenceReference(nameof(IconImage))] [Desc("Icon sprite displayed in the support power palette.")] public readonly string Icon = null; [PaletteReference] [Desc("Palette used for the icon.")] public readonly string IconPalette = "chrome"; [FluentReference(optional: true)] public readonly string Name = null; [FluentReference(optional: true)] public readonly string Description = null; [Desc("Allow multiple instances of the same support power.")] public readonly bool AllowMultiple = false; [Desc("Allow this to be used only once.")] public readonly bool OneShot = false; [CursorReference] [Desc("Cursor to display for using this support power.")] public readonly string Cursor = "ability"; [CursorReference] [Desc("Cursor when unable to activate on this position. ")] public readonly string BlockedCursor = "generic-blocked"; [Desc("If set to true, the support power will be fully charged when it becomes available. " + "Normal rules apply for subsequent charges.")] public readonly bool StartFullyCharged = false; public readonly string[] Prerequisites = Array.Empty(); public readonly string DetectedSound = null; [NotificationReference("Speech")] public readonly string DetectedSpeechNotification = null; [FluentReference(optional: true)] public readonly string DetectedTextNotification = null; public readonly string BeginChargeSound = null; [NotificationReference("Speech")] public readonly string BeginChargeSpeechNotification = null; [FluentReference(optional: true)] public readonly string BeginChargeTextNotification = null; public readonly string EndChargeSound = null; [NotificationReference("Speech")] public readonly string EndChargeSpeechNotification = null; [FluentReference(optional: true)] public readonly string EndChargeTextNotification = null; public readonly string SelectTargetSound = null; [NotificationReference("Speech")] public readonly string SelectTargetSpeechNotification = null; [FluentReference(optional: true)] public readonly string SelectTargetTextNotification = null; public readonly string InsufficientPowerSound = null; [NotificationReference("Speech")] public readonly string InsufficientPowerSpeechNotification = null; [FluentReference(optional: true)] public readonly string InsufficientPowerTextNotification = null; public readonly string LaunchSound = null; [NotificationReference("Speech")] public readonly string LaunchSpeechNotification = null; [FluentReference(optional: true)] public readonly string LaunchTextNotification = null; public readonly string IncomingSound = null; [NotificationReference("Speech")] public readonly string IncomingSpeechNotification = null; [FluentReference(optional: true)] public readonly string IncomingTextNotification = null; [Desc("Defines to which players the timer is shown.")] public readonly PlayerRelationship DisplayTimerRelationships = PlayerRelationship.None; [Desc("Beacons are only supported on the Airstrike, Paratroopers, and Nuke powers")] public readonly bool DisplayBeacon = false; public readonly bool BeaconPaletteIsPlayerPalette = true; [PaletteReference(nameof(BeaconPaletteIsPlayerPalette))] public readonly string BeaconPalette = "player"; public readonly string BeaconImage = "beacon"; [SequenceReference(nameof(BeaconImage))] public readonly string BeaconPoster = null; [PaletteReference] public readonly string BeaconPosterPalette = "chrome"; [SequenceReference(nameof(BeaconImage))] public readonly string ClockSequence = null; [SequenceReference(nameof(BeaconImage))] public readonly string BeaconSequence = null; [SequenceReference(nameof(BeaconImage))] public readonly string ArrowSequence = null; [SequenceReference(nameof(BeaconImage))] public readonly string CircleSequence = null; [Desc("Delay after launch, measured in ticks.")] public readonly int BeaconDelay = 0; public readonly bool DisplayRadarPing = false; [Desc("Measured in ticks.")] public readonly int RadarPingDuration = 125; public readonly string OrderName; [Desc("Sort order for the support power palette. Smaller numbers are presented earlier.")] public readonly int SupportPowerPaletteOrder = 9999; protected SupportPowerInfo() { OrderName = GetType().Name + "Order"; } } public class SupportPower : PausableConditionalTrait { public readonly Actor Self; readonly SupportPowerInfo info; protected RadarPing ping; public SupportPower(Actor self, SupportPowerInfo info) : base(info) { Self = self; this.info = info; } protected override void Created(Actor self) { base.Created(self); 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, player, "Speech", info.DetectedSpeechNotification, player.Faction.InternalName); TextNotificationsManager.AddTransientLine(player, info.DetectedTextNotification); } } public virtual SupportPowerInstance CreateInstance(string key, SupportPowerManager manager) { return new SupportPowerInstance(key, info, manager); } public virtual void Charging(Actor self, string key) { 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(self.Owner, Info.BeginChargeTextNotification); } public virtual void Charged(Actor self, string key) { Game.Sound.PlayToPlayer(SoundType.UI, self.Owner, Info.EndChargeSound); Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.EndChargeSpeechNotification, self.Owner.Faction.InternalName); TextNotificationsManager.AddTransientLine(self.Owner, Info.EndChargeTextNotification); foreach (var notify in self.TraitsImplementing()) notify.Charged(self); } public virtual void SelectTarget(Actor self, string order, SupportPowerManager manager) { self.World.OrderGenerator = new SelectGenericPowerTarget(order, manager, info, MouseButton.Left); } public virtual void Activate(Actor self, Order order, SupportPowerManager manager) { if (Info.DisplayRadarPing && manager.RadarPings != null) { ping = manager.RadarPings.Value.Add( () => order.Player.IsAlliedWith(self.World.RenderPlayer), order.Target.CenterPosition, order.Player.Color, Info.RadarPingDuration); } foreach (var notify in self.TraitsImplementing()) notify.Activated(self); } public virtual void PlayLaunchSounds() { 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 speech = isAllied ? Info.LaunchSpeechNotification : Info.IncomingSpeechNotification; Game.Sound.PlayNotification(Self.World.Map.Rules, localPlayer, "Speech", speech, localPlayer.Faction.InternalName); var text = isAllied ? Info.LaunchTextNotification : Info.IncomingTextNotification; TextNotificationsManager.AddTransientLine(localPlayer, text); } public IEnumerable CellsMatching(CPos location, char[] footprint, CVec dimensions) { var index = 0; var x = location.X - (dimensions.X - 1) / 2; var y = location.Y - (dimensions.Y - 1) / 2; for (var j = 0; j < dimensions.Y; j++) for (var i = 0; i < dimensions.X; i++) if (footprint[index++] == 'x') yield return new CPos(x + i, y + j); } } }