diff --git a/OpenRA.Mods.Cnc/Traits/DrainPrerequisitePowerOnDamage.cs b/OpenRA.Mods.Cnc/Traits/DrainPrerequisitePowerOnDamage.cs new file mode 100644 index 0000000000..c600a35f1e --- /dev/null +++ b/OpenRA.Mods.Cnc/Traits/DrainPrerequisitePowerOnDamage.cs @@ -0,0 +1,68 @@ +#region Copyright & License Information +/* + * Copyright 2007-2019 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using OpenRA.Mods.Common.Traits; +using OpenRA.Traits; + +namespace OpenRA.Mods.Cnc.Traits +{ + [Desc("Converts damage to a charge level of a GrantPrerequisiteChargeDrainPower.")] + public class DrainPrerequisitePowerOnDamageInfo : ConditionalTraitInfo + { + [Desc("The OrderName of the GrantPrerequisiteChargeDrainPower to drain.")] + public readonly string OrderName = "GrantPrerequisiteChargeDrainPowerInfoOrder"; + + [Desc("Damage is multiplied by this number when converting damage to drain ticks.")] + public readonly int DamageMultiplier = 1; + + [Desc("Damage is divided by this number when converting damage to drain ticks.")] + public readonly int DamageDivisor = 600; + + public override object Create(ActorInitializer init) { return new DrainPrerequisitePowerOnDamage(init.Self, this); } + } + + public class DrainPrerequisitePowerOnDamage : ConditionalTrait, INotifyOwnerChanged, IDamageModifier + { + SupportPowerManager spm; + + public DrainPrerequisitePowerOnDamage(Actor self, DrainPrerequisitePowerOnDamageInfo info) + : base(info) { } + + protected override void Created(Actor self) + { + base.Created(self); + spm = self.Owner.PlayerActor.Trait(); + } + + void INotifyOwnerChanged.OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) + { + spm = newOwner.PlayerActor.Trait(); + } + + int IDamageModifier.GetDamageModifier(Actor self, Damage damage) + { + if (!IsTraitDisabled && damage != null) + { + var damageSubTicks = (int)(damage.Value * 100L * Info.DamageMultiplier / Info.DamageDivisor); + + SupportPowerInstance spi; + if (spm.Powers.TryGetValue(Info.OrderName, out spi)) + { + var dspi = spi as GrantPrerequisiteChargeDrainPower.DischargeableSupportPowerInstance; + if (dspi != null) + dspi.Discharge(damageSubTicks); + } + } + + return 100; + } + } +} diff --git a/OpenRA.Mods.Cnc/Traits/SupportPowers/GrantPrerequisiteChargeDrainPower.cs b/OpenRA.Mods.Cnc/Traits/SupportPowers/GrantPrerequisiteChargeDrainPower.cs new file mode 100644 index 0000000000..9285a7144e --- /dev/null +++ b/OpenRA.Mods.Cnc/Traits/SupportPowers/GrantPrerequisiteChargeDrainPower.cs @@ -0,0 +1,211 @@ +#region Copyright & License Information +/* + * Copyright 2007-2019 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.Mods.Common.Traits; +using OpenRA.Traits; + +namespace OpenRA.Mods.Cnc.Traits +{ + [Desc("Grants a prerequisite while discharging at a configurable rate.")] + public class GrantPrerequisiteChargeDrainPowerInfo : SupportPowerInfo, ITechTreePrerequisiteInfo + { + [Desc("Rate at which the power discharges compared to charging")] + public readonly int DischargeModifier = 300; + + [FieldLoader.Require] + [Desc("The prerequisite type that this provides.")] + public readonly string Prerequisite = null; + + [Translate] + [Desc("Label to display over the support power icon and in its tooltip while the power is active.")] + public readonly string ActiveText = "ACTIVE"; + + [Translate] + [Desc("Label to display over the support power icon and in its tooltip while the power is available but not active.")] + public readonly string AvailableText = "READY"; + + IEnumerable ITechTreePrerequisiteInfo.Prerequisites(ActorInfo info) + { + yield return Prerequisite; + } + + public override object Create(ActorInitializer init) { return new GrantPrerequisiteChargeDrainPower(init.Self, this); } + } + + public class GrantPrerequisiteChargeDrainPower : SupportPower, ITechTreePrerequisite, INotifyOwnerChanged + { + readonly GrantPrerequisiteChargeDrainPowerInfo info; + TechTree techTree; + bool active; + + public GrantPrerequisiteChargeDrainPower(Actor self, GrantPrerequisiteChargeDrainPowerInfo info) + : base(self, info) + { + this.info = info; + } + + protected override void Created(Actor self) + { + // Special case handling is required for the Player actor. + // Created is called before Player.PlayerActor is assigned, + // so we must query other player traits from self, knowing that + // it refers to the same actor as self.Owner.PlayerActor + var playerActor = self.Info.Name == "player" ? self : self.Owner.PlayerActor; + + techTree = playerActor.Trait(); + + base.Created(self); + } + + void INotifyOwnerChanged.OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) + { + techTree = newOwner.PlayerActor.Trait(); + active = false; + } + + public override SupportPowerInstance CreateInstance(string key, SupportPowerManager manager) + { + return new DischargeableSupportPowerInstance(key, info, manager); + } + + public void Activate(Actor self, SupportPowerInstance instance) + { + active = true; + techTree.ActorChanged(self); + } + + public void Deactivate(Actor self, SupportPowerInstance instance) + { + active = false; + techTree.ActorChanged(self); + } + + IEnumerable ITechTreePrerequisite.ProvidesPrerequisites + { + get + { + if (!active) + yield break; + + yield return info.Prerequisite; + } + } + + public class DischargeableSupportPowerInstance : SupportPowerInstance + { + // Whether the power is available to activate (even if not fully charged) + bool available; + + // Whether the power is active right now + // Note that this is fundamentally different to SupportPowerInstance.Active + // which has a much closer meaning to available above. + bool active; + + // Additional discharge rate accrued from damage + int additionalDischargeSubTicks = 0; + + public DischargeableSupportPowerInstance(string key, GrantPrerequisiteChargeDrainPowerInfo info, SupportPowerManager manager) + : base(key, info, manager) { } + + void Deactivate() + { + active = false; + notifiedCharging = false; + + // Fully depleting the charge disables the power until it is again fully charged + if (!Active || remainingSubTicks >= TotalTicks * 100) + available = false; + + foreach (var p in Instances) + ((GrantPrerequisiteChargeDrainPower)p).Deactivate(p.Self, this); + } + + public override void Tick() + { + var orig = remainingSubTicks; + base.Tick(); + + if (Ready) + available = true; + + if (active && !Active) + Deactivate(); + + if (active) + { + remainingSubTicks = orig + ((GrantPrerequisiteChargeDrainPowerInfo)Info).DischargeModifier + additionalDischargeSubTicks; + additionalDischargeSubTicks = 0; + + if (remainingSubTicks > TotalTicks * 100) + { + remainingSubTicks = TotalTicks * 100; + Deactivate(); + } + } + } + + public void Discharge(int subTicks) + { + additionalDischargeSubTicks += subTicks; + } + + public override void Target() + { + if (available && Active) + Manager.Self.World.IssueOrder(new Order(Key, Manager.Self, false) { ExtraData = active ? 0U : 1U }); + } + + public override void Activate(Order order) + { + if (active && order.ExtraData == 0) + { + Deactivate(); + return; + } + + if (!available || order.ExtraData != 1) + return; + + var power = Instances.FirstOrDefault(i => !i.IsTraitPaused); + if (power == null) + return; + + active = true; + + // Only play the activation sound once! + power.PlayLaunchSounds(); + + foreach (var p in Instances) + ((GrantPrerequisiteChargeDrainPower)p).Activate(p.Self, this); + } + + public override string IconOverlayTextOverride() + { + var info = Info as GrantPrerequisiteChargeDrainPowerInfo; + if (info == null || !Active) + return null; + + return active ? info.ActiveText : available ? info.AvailableText : null; + } + + public override string TooltipTimeTextOverride() + { + var info = Info as GrantPrerequisiteChargeDrainPowerInfo; + if (info == null || !Active) + return null; + + return active ? info.ActiveText : available ? info.AvailableText : null; + } + } + } +} diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs index 19a7b7dc8e..30e0909ae1 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs @@ -159,7 +159,7 @@ namespace OpenRA.Mods.Common.Traits bool instancesEnabled; bool prereqsAvailable = true; bool oneShotFired; - bool notifiedCharging; + protected bool notifiedCharging; bool notifiedReady; public SupportPowerInstance(string key, SupportPowerInfo info, SupportPowerManager manager)