Merge pull request #11428 from reaperrr/attack-delay

Allow attack anims/overlays to start before weapons fire
This commit is contained in:
abcdefg30
2016-07-03 18:58:07 +02:00
committed by GitHub
16 changed files with 193 additions and 23 deletions

View File

@@ -428,6 +428,7 @@
<Compile Include="Traits\Render\WithSpriteBarrel.cs" /> <Compile Include="Traits\Render\WithSpriteBarrel.cs" />
<Compile Include="Traits\Render\WithBuildingExplosion.cs" /> <Compile Include="Traits\Render\WithBuildingExplosion.cs" />
<Compile Include="Traits\Render\WithAttackAnimation.cs" /> <Compile Include="Traits\Render\WithAttackAnimation.cs" />
<Compile Include="Traits\Render\WithAttackOverlay.cs" />
<Compile Include="Traits\Render\WithMoveAnimation.cs" /> <Compile Include="Traits\Render\WithMoveAnimation.cs" />
<Compile Include="Traits\Render\WithSiloAnimation.cs" /> <Compile Include="Traits\Render\WithSiloAnimation.cs" />
<Compile Include="Traits\Render\WithBuildingPlacedAnimation.cs" /> <Compile Include="Traits\Render\WithBuildingPlacedAnimation.cs" />
@@ -480,6 +481,7 @@
<Compile Include="Traits\Sound\CaptureNotification.cs" /> <Compile Include="Traits\Sound\CaptureNotification.cs" />
<Compile Include="Traits\Sound\DeathSounds.cs" /> <Compile Include="Traits\Sound\DeathSounds.cs" />
<Compile Include="Traits\Sound\SoundOnDamageTransition.cs" /> <Compile Include="Traits\Sound\SoundOnDamageTransition.cs" />
<Compile Include="Traits\Sound\AttackSounds.cs" />
<Compile Include="Traits\SupplyTruck.cs" /> <Compile Include="Traits\SupplyTruck.cs" />
<Compile Include="Traits\SupportPowers\AirstrikePower.cs" /> <Compile Include="Traits\SupportPowers\AirstrikePower.cs" />
<Compile Include="Traits\SupportPowers\GrantUpgradePower.cs" /> <Compile Include="Traits\SupportPowers\GrantUpgradePower.cs" />

View File

@@ -98,12 +98,14 @@ namespace OpenRA.Mods.Common.Traits
return true; return true;
} }
public void Attacking(Actor self, Target target, Armament a, Barrel barrel) void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel)
{ {
if (a != null && a.Info.AmmoPoolName == Info.Name) if (a != null && a.Info.AmmoPoolName == Info.Name)
TakeAmmo(); TakeAmmo();
} }
void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel) { }
public void Tick(Actor self) public void Tick(Actor self)
{ {
if (!Info.SelfReloads) if (!Info.SelfReloads)

View File

@@ -223,6 +223,9 @@ namespace OpenRA.Mods.Common.Traits
GuidedTarget = target GuidedTarget = target
}; };
foreach (var na in self.TraitsImplementing<INotifyAttack>())
na.PreparingAttack(self, target, this, barrel);
ScheduleDelayedAction(Info.FireDelay, () => ScheduleDelayedAction(Info.FireDelay, () =>
{ {
if (args.Weapon.Projectile != null) if (args.Weapon.Projectile != null)

View File

@@ -145,6 +145,9 @@ namespace OpenRA.Mods.Common.Traits
self.SetTargetLine(target, Color.Red); self.SetTargetLine(target, Color.Red);
AttackTarget(target, order.Queued, true, forceAttack); AttackTarget(target, order.Queued, true, forceAttack);
} }
if (order.OrderString == "Stop")
self.CancelActivity();
} }
static Target TargetFromOrder(Actor self, Order order) static Target TargetFromOrder(Actor self, Order order)

View File

@@ -64,25 +64,19 @@ namespace OpenRA.Mods.Common.Traits
return base.CanAttack(self, target); return base.CanAttack(self, target);
} }
public void Attacking(Actor self, Target target, Armament a, Barrel barrel) void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel)
{ {
--charges; --charges;
timeToRecharge = info.ReloadDelay; timeToRecharge = info.ReloadDelay;
} }
void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel) { }
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove, bool forceAttack) public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove, bool forceAttack)
{ {
return new ChargeAttack(this, newTarget); return new ChargeAttack(this, newTarget);
} }
public override void ResolveOrder(Actor self, Order order)
{
base.ResolveOrder(self, order);
if (order.OrderString == "Stop")
self.CancelActivity();
}
class ChargeAttack : Activity class ChargeAttack : Activity
{ {
readonly AttackCharge attack; readonly AttackCharge attack;

View File

@@ -98,6 +98,8 @@ namespace OpenRA.Mods.Common.Traits
void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel) { if (Info.UncloakOn.HasFlag(UncloakType.Attack)) Uncloak(); } void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel) { if (Info.UncloakOn.HasFlag(UncloakType.Attack)) Uncloak(); }
void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel) { }
void INotifyDamageStateChanged.DamageStateChanged(Actor self, AttackInfo e) void INotifyDamageStateChanged.DamageStateChanged(Actor self, AttackInfo e)
{ {
damageDisabled = e.DamageState >= DamageState.Critical; damageDisabled = e.DamageState >= DamageState.Critical;

View File

@@ -82,12 +82,14 @@ namespace OpenRA.Mods.Common.Traits
Panic(); Panic();
} }
public void Attacking(Actor self, Target target, Armament a, Barrel barrel) void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel)
{ {
if (self.World.SharedRandom.Next(100 / info.AttackPanicChance) == 0) if (self.World.SharedRandom.Next(100 / info.AttackPanicChance) == 0)
Panic(); Panic();
} }
void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel) { }
public int GetSpeedModifier() public int GetSpeedModifier()
{ {
return Panicking ? info.PanicSpeedModifier : 100; return Panicking ? info.PanicSpeedModifier : 100;

View File

@@ -28,6 +28,12 @@ namespace OpenRA.Mods.Common.Traits.Render
[Desc("Shown while reloading.")] [Desc("Shown while reloading.")]
[SequenceReference(null, true)] public readonly string ReloadPrefix = null; [SequenceReference(null, true)] public readonly string ReloadPrefix = null;
[Desc("Delay in ticks before animation starts, either relative to attack preparation or attack.")]
public readonly int Delay = 0;
[Desc("Should the animation be delayed relative to preparation or actual attack?")]
public readonly AttackDelayType DelayRelativeTo = AttackDelayType.Preparation;
public object Create(ActorInitializer init) { return new WithAttackAnimation(init, this); } public object Create(ActorInitializer init) { return new WithAttackAnimation(init, this); }
} }
@@ -38,6 +44,8 @@ namespace OpenRA.Mods.Common.Traits.Render
readonly Armament armament; readonly Armament armament;
readonly WithSpriteBody wsb; readonly WithSpriteBody wsb;
int tick;
public WithAttackAnimation(ActorInitializer init, WithAttackAnimationInfo info) public WithAttackAnimation(ActorInitializer init, WithAttackAnimationInfo info)
{ {
this.info = info; this.info = info;
@@ -47,14 +55,39 @@ namespace OpenRA.Mods.Common.Traits.Render
wsb = init.Self.Trait<WithSpriteBody>(); wsb = init.Self.Trait<WithSpriteBody>();
} }
public void Attacking(Actor self, Target target, Armament a, Barrel barrel) void PlayAttackAnimation(Actor self)
{ {
if (!string.IsNullOrEmpty(info.AttackSequence)) if (!string.IsNullOrEmpty(info.AttackSequence))
wsb.PlayCustomAnimation(self, info.AttackSequence, () => wsb.CancelCustomAnimation(self)); wsb.PlayCustomAnimation(self, info.AttackSequence, () => wsb.CancelCustomAnimation(self));
} }
public void Tick(Actor self) void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel)
{ {
if (info.DelayRelativeTo == AttackDelayType.Attack)
{
if (info.Delay > 0)
tick = info.Delay;
else
PlayAttackAnimation(self);
}
}
void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel)
{
if (info.DelayRelativeTo == AttackDelayType.Preparation)
{
if (info.Delay > 0)
tick = info.Delay;
else
PlayAttackAnimation(self);
}
}
void ITick.Tick(Actor self)
{
if (info.Delay > 0 && --tick == 0)
PlayAttackAnimation(self);
if (string.IsNullOrEmpty(info.AimSequence) && string.IsNullOrEmpty(info.ReloadPrefix)) if (string.IsNullOrEmpty(info.AimSequence) && string.IsNullOrEmpty(info.ReloadPrefix))
return; return;

View File

@@ -10,11 +10,9 @@
#endregion #endregion
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Mods.Common.Traits.Render;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.D2k.Traits.Render namespace OpenRA.Mods.Common.Traits.Render
{ {
[Desc("Rendered together with an attack.")] [Desc("Rendered together with an attack.")]
public class WithAttackOverlayInfo : ITraitInfo, Requires<RenderSpritesInfo> public class WithAttackOverlayInfo : ITraitInfo, Requires<RenderSpritesInfo>
@@ -29,16 +27,23 @@ namespace OpenRA.Mods.D2k.Traits.Render
[Desc("Custom palette is a player palette BaseName")] [Desc("Custom palette is a player palette BaseName")]
public readonly bool IsPlayerPalette = false; public readonly bool IsPlayerPalette = false;
[Desc("Delay in ticks before overlay starts, either relative to attack preparation or attack.")]
public readonly int Delay = 0;
[Desc("Should the overlay be delayed relative to preparation or actual attack?")]
public readonly AttackDelayType DelayRelativeTo = AttackDelayType.Preparation;
public object Create(ActorInitializer init) { return new WithAttackOverlay(init, this); } public object Create(ActorInitializer init) { return new WithAttackOverlay(init, this); }
} }
public class WithAttackOverlay : INotifyAttack public class WithAttackOverlay : INotifyAttack, ITick
{ {
readonly Animation overlay; readonly Animation overlay;
readonly RenderSprites renderSprites; readonly RenderSprites renderSprites;
readonly WithAttackOverlayInfo info; readonly WithAttackOverlayInfo info;
bool attacking; bool attacking;
int tick;
public WithAttackOverlay(ActorInitializer init, WithAttackOverlayInfo info) public WithAttackOverlay(ActorInitializer init, WithAttackOverlayInfo info)
{ {
@@ -52,10 +57,38 @@ namespace OpenRA.Mods.D2k.Traits.Render
info.Palette, info.IsPlayerPalette); info.Palette, info.IsPlayerPalette);
} }
public void Attacking(Actor self, Target target, Armament a, Barrel barrel) void PlayOverlay(Actor self)
{ {
attacking = true; attacking = true;
overlay.PlayThen(info.Sequence, () => attacking = false); overlay.PlayThen(info.Sequence, () => attacking = false);
} }
void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel)
{
if (info.DelayRelativeTo == AttackDelayType.Attack)
{
if (info.Delay > 0)
tick = info.Delay;
else
PlayOverlay(self);
}
}
void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel)
{
if (info.DelayRelativeTo == AttackDelayType.Preparation)
{
if (info.Delay > 0)
tick = info.Delay;
else
PlayOverlay(self);
}
}
void ITick.Tick(Actor self)
{
if (info.Delay > 0 && --tick == 0)
PlayOverlay(self);
}
} }
} }

View File

@@ -105,11 +105,13 @@ namespace OpenRA.Mods.Common.Traits.Render
} }
} }
public void Attacking(Actor self, Target target, Armament a, Barrel barrel) void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel)
{ {
Attacking(self, target); Attacking(self, target);
} }
void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel) { }
public virtual void Tick(Actor self) public virtual void Tick(Actor self)
{ {
if (rsm != null) if (rsm != null)

View File

@@ -75,7 +75,7 @@ namespace OpenRA.Mods.Common.Traits.Render
} }
} }
public void Attacking(Actor self, Target target, Armament a, Barrel barrel) void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel)
{ {
if (a == null) if (a == null)
return; return;
@@ -94,6 +94,8 @@ namespace OpenRA.Mods.Common.Traits.Render
anims[barrel].Animation.PlayThen(sequence, () => visible[barrel] = false); anims[barrel].Animation.PlayThen(sequence, () => visible[barrel] = false);
} }
void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel) { }
public IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr) public IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
{ {
foreach (var arm in armaments) foreach (var arm in armaments)

View File

@@ -0,0 +1,80 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 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.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits.Sound
{
[Desc("Played when preparing for an attack or attacking.")]
public class AttackSoundsInfo : UpgradableTraitInfo
{
[Desc("Play a randomly selected sound from this list when preparing for an attack or attacking.")]
public readonly string[] Sounds = { };
[Desc("Delay in ticks before sound starts, either relative to attack preparation or attack.")]
public readonly int Delay = 0;
[Desc("Should the sound be delayed relative to preparation or actual attack?")]
public readonly AttackDelayType DelayRelativeTo = AttackDelayType.Preparation;
public override object Create(ActorInitializer init) { return new AttackSounds(init, this); }
}
public class AttackSounds : UpgradableTrait<AttackSoundsInfo>, INotifyAttack, ITick
{
readonly AttackSoundsInfo info;
int tick;
public AttackSounds(ActorInitializer init, AttackSoundsInfo info)
: base(info)
{
this.info = info;
}
void PlaySound(Actor self)
{
if (info.Sounds.Any())
Game.Sound.Play(info.Sounds.Random(self.World.SharedRandom), self.CenterPosition);
}
void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel)
{
if (info.DelayRelativeTo == AttackDelayType.Attack)
{
if (info.Delay > 0)
tick = info.Delay;
else
PlaySound(self);
}
}
void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel)
{
if (info.DelayRelativeTo == AttackDelayType.Preparation)
{
if (info.Delay > 0)
tick = info.Delay;
else
PlaySound(self);
}
}
void ITick.Tick(Actor self)
{
if (IsTraitDisabled)
return;
if (info.Delay > 0 && --tick == 0)
PlaySound(self);
}
}
}

View File

@@ -19,6 +19,8 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
public enum AttackDelayType { Preparation, Attack }
public interface IQuantizeBodyOrientationInfo : ITraitInfo public interface IQuantizeBodyOrientationInfo : ITraitInfo
{ {
int QuantizedBodyFacings(ActorInfo ai, SequenceProvider sequenceProvider, string race); int QuantizedBodyFacings(ActorInfo ai, SequenceProvider sequenceProvider, string race);
@@ -34,7 +36,13 @@ namespace OpenRA.Mods.Common.Traits
IEnumerable<IRenderable> Render(WorldRenderer wr, World w, ActorInfo ai, WPos centerPosition); IEnumerable<IRenderable> Render(WorldRenderer wr, World w, ActorInfo ai, WPos centerPosition);
} }
public interface INotifyAttack { void Attacking(Actor self, Target target, Armament a, Barrel barrel); } [RequireExplicitImplementation]
public interface INotifyAttack
{
void Attacking(Actor self, Target target, Armament a, Barrel barrel);
void PreparingAttack(Actor self, Target target, Armament a, Barrel barrel);
}
public interface INotifyBurstComplete { void FiredBurst(Actor self, Target target, Armament a); } public interface INotifyBurstComplete { void FiredBurst(Actor self, Target target, Armament a); }
public interface INotifyCharging { void Charging(Actor self, Target target); } public interface INotifyCharging { void Charging(Actor self, Target target); }
public interface INotifyChat { bool OnChat(string from, string message); } public interface INotifyChat { bool OnChat(string from, string message); }

View File

@@ -92,7 +92,10 @@ namespace OpenRA.Mods.D2k.Activities
}); });
foreach (var notify in self.TraitsImplementing<INotifyAttack>()) foreach (var notify in self.TraitsImplementing<INotifyAttack>())
{
notify.PreparingAttack(self, target, null, null);
notify.Attacking(self, target, null, null); notify.Attacking(self, target, null, null);
}
return true; return true;
} }

View File

@@ -98,7 +98,6 @@
<Compile Include="Warheads\ChangeOwnerWarhead.cs" /> <Compile Include="Warheads\ChangeOwnerWarhead.cs" />
<Compile Include="UtilityCommands\D2kMapImporter.cs" /> <Compile Include="UtilityCommands\D2kMapImporter.cs" />
<Compile Include="UtilityCommands\ImportD2kMapCommand.cs" /> <Compile Include="UtilityCommands\ImportD2kMapCommand.cs" />
<Compile Include="Traits\Render\WithAttackOverlay.cs" />
<Compile Include="Traits\Render\WithDecorationCarryable.cs" /> <Compile Include="Traits\Render\WithDecorationCarryable.cs" />
<Compile Include="Traits\World\D2kEditorResourceLayer.cs" /> <Compile Include="Traits\World\D2kEditorResourceLayer.cs" />
<Compile Include="Lint\CheckImportActors.cs" /> <Compile Include="Lint\CheckImportActors.cs" />

View File

@@ -197,6 +197,8 @@ namespace OpenRA.Mods.RA.Traits
} }
} }
public void Attacking(Actor self, Target target, Armament a, Barrel barrel) { DisguiseAs(null); } void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel) { }
void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel) { DisguiseAs(null); }
} }
} }