Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement GrantPrerequisiteChargeDrainPower logic #16347

Merged
merged 5 commits into from Nov 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
68 changes: 68 additions & 0 deletions 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<DrainPrerequisitePowerOnDamageInfo>, 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<SupportPowerManager>();
}

void INotifyOwnerChanged.OnOwnerChanged(Actor self, Player oldOwner, Player newOwner)
{
spm = newOwner.PlayerActor.Trait<SupportPowerManager>();
}

int IDamageModifier.GetDamageModifier(Actor self, Damage damage)
{
if (!IsTraitDisabled && damage != null)
{
var damageSubTicks = (int)(damage.Value * 100L * Info.DamageMultiplier / Info.DamageDivisor);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guard for overflow?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The overflow is already guarded by casting to a long with the 100L.


SupportPowerInstance spi;
if (spm.Powers.TryGetValue(Info.OrderName, out spi))
{
var dspi = spi as GrantPrerequisiteChargeDrainPower.DischargeableSupportPowerInstance;
if (dspi != null)
dspi.Discharge(damageSubTicks);
}
}

return 100;
}
}
}
@@ -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<string> 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<TechTree>();

base.Created(self);
}

void INotifyOwnerChanged.OnOwnerChanged(Actor self, Player oldOwner, Player newOwner)
{
techTree = newOwner.PlayerActor.Trait<TechTree>();
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<string> 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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TotalTicks * 100
Not sure if its worth to extract this to a property and use it here and the two places below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO not worth it.

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;
}
}
}
}
2 changes: 1 addition & 1 deletion OpenRA.Mods.Common/Traits/Infantry/TakeCover.cs
Expand Up @@ -92,7 +92,7 @@ int IDamageModifier.GetDamageModifier(Actor attacker, Damage damage)
if (!IsProne)
return 100;

if (damage.DamageTypes.IsEmpty)
if (damage == null || damage.DamageTypes.IsEmpty)
return 100;

var modifierPercentages = info.DamageModifiers.Where(x => damage.DamageTypes.Contains(x.Key)).Select(x => x.Value);
Expand Down
Expand Up @@ -42,7 +42,7 @@ public TerrainModifiesDamage(Actor self, TerrainModifiesDamageInfo info)

int IDamageModifier.GetDamageModifier(Actor attacker, Damage damage)
{
if (attacker.Owner.IsAlliedWith(self.Owner) && damage.Value < 0 && !Info.ModifyHealing)
if (!Info.ModifyHealing && attacker.Owner.IsAlliedWith(self.Owner) && damage != null && damage.Value < 0)
return FullDamage;

var world = self.World;
Expand Down
2 changes: 1 addition & 1 deletion OpenRA.Mods.Common/Traits/Render/SupportPowerChargeBar.cs
Expand Up @@ -51,7 +51,7 @@ float ISelectionBar.GetValue()
if (viewer != null && !Info.DisplayStances.HasStance(self.Owner.Stances[viewer]))
return 0;

return 1 - (float)power.RemainingTime / power.TotalTime;
return 1 - (float)power.RemainingTicks / power.TotalTicks;
}

Color ISelectionBar.GetColor() { return Info.Color; }
Expand Down
5 changes: 5 additions & 0 deletions OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs
Expand Up @@ -125,6 +125,11 @@ public SupportPower(Actor self, SupportPowerInfo info)
this.info = info;
}

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);
Expand Down