Step in the direction of RA2 paratroopers.
This commit is contained in:
94
OpenRA.Mods.Common/Activities/Parachute.cs
Normal file
94
OpenRA.Mods.Common/Activities/Parachute.cs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2015 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. For more information,
|
||||||
|
* see COPYING.
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using OpenRA.Activities;
|
||||||
|
using OpenRA.Mods.Common.Traits;
|
||||||
|
using OpenRA.Traits;
|
||||||
|
|
||||||
|
namespace OpenRA.Mods.Common.Activities
|
||||||
|
{
|
||||||
|
public class Parachute : Activity
|
||||||
|
{
|
||||||
|
readonly UpgradeManager um;
|
||||||
|
readonly IPositionable pos;
|
||||||
|
readonly ParachutableInfo para;
|
||||||
|
readonly WVec fallVector;
|
||||||
|
|
||||||
|
WPos dropPosition;
|
||||||
|
WPos currentPosition;
|
||||||
|
bool triggered = false;
|
||||||
|
|
||||||
|
public Parachute(Actor self, WPos dropPosition)
|
||||||
|
{
|
||||||
|
um = self.TraitOrDefault<UpgradeManager>();
|
||||||
|
pos = self.TraitOrDefault<IPositionable>();
|
||||||
|
|
||||||
|
// Parachutable trait is a prerequisite for running this activity
|
||||||
|
para = self.Info.Traits.Get<ParachutableInfo>();
|
||||||
|
fallVector = new WVec(0, 0, para.FallRate);
|
||||||
|
this.dropPosition = dropPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
Activity FirstTick(Actor self)
|
||||||
|
{
|
||||||
|
triggered = true;
|
||||||
|
|
||||||
|
if (um != null)
|
||||||
|
foreach (var u in para.ParachuteUpgrade)
|
||||||
|
um.GrantUpgrade(self, u, this);
|
||||||
|
|
||||||
|
// Place the actor and retrieve its visual position (CenterPosition)
|
||||||
|
pos.SetPosition(self, dropPosition);
|
||||||
|
currentPosition = self.CenterPosition;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Activity LastTick(Actor self)
|
||||||
|
{
|
||||||
|
pos.SetPosition(self, currentPosition - new WVec(0, 0, currentPosition.Z));
|
||||||
|
|
||||||
|
if (um != null)
|
||||||
|
foreach (var u in para.ParachuteUpgrade)
|
||||||
|
um.RevokeUpgrade(self, u, this);
|
||||||
|
|
||||||
|
foreach (var npl in self.TraitsImplementing<INotifyParachuteLanded>())
|
||||||
|
npl.OnLanded();
|
||||||
|
|
||||||
|
return NextActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Activity Tick(Actor self)
|
||||||
|
{
|
||||||
|
// If this is the first tick
|
||||||
|
if (!triggered)
|
||||||
|
return FirstTick(self);
|
||||||
|
|
||||||
|
currentPosition -= fallVector;
|
||||||
|
|
||||||
|
// If the unit has landed, this will be the last tick
|
||||||
|
if (currentPosition.Z <= 0)
|
||||||
|
return LastTick(self);
|
||||||
|
|
||||||
|
pos.SetVisualPosition(self, currentPosition);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only the last queued activity (given order) is kept
|
||||||
|
public override void Queue(Activity activity)
|
||||||
|
{
|
||||||
|
NextActivity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cannot be cancelled
|
||||||
|
public override void Cancel(Actor self) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
#region Copyright & License Information
|
|
||||||
/*
|
|
||||||
* Copyright 2007-2015 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. For more information,
|
|
||||||
* see COPYING.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using OpenRA.Effects;
|
|
||||||
using OpenRA.Graphics;
|
|
||||||
using OpenRA.Mods.Common.Traits;
|
|
||||||
using OpenRA.Traits;
|
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Effects
|
|
||||||
{
|
|
||||||
public class Parachute : IEffect
|
|
||||||
{
|
|
||||||
readonly ParachutableInfo parachutableInfo;
|
|
||||||
readonly Animation parachute;
|
|
||||||
readonly Animation shadow;
|
|
||||||
readonly WVec parachuteOffset;
|
|
||||||
readonly Actor cargo;
|
|
||||||
WPos pos;
|
|
||||||
WVec fallVector;
|
|
||||||
|
|
||||||
public Parachute(Actor cargo, WPos dropPosition)
|
|
||||||
{
|
|
||||||
this.cargo = cargo;
|
|
||||||
|
|
||||||
parachutableInfo = cargo.Info.Traits.GetOrDefault<ParachutableInfo>();
|
|
||||||
|
|
||||||
if (parachutableInfo != null)
|
|
||||||
fallVector = new WVec(0, 0, parachutableInfo.FallRate);
|
|
||||||
|
|
||||||
var parachuteSprite = parachutableInfo != null ? parachutableInfo.ParachuteSequence : null;
|
|
||||||
if (parachuteSprite != null)
|
|
||||||
{
|
|
||||||
parachute = new Animation(cargo.World, parachuteSprite);
|
|
||||||
parachute.PlayThen(parachutableInfo.ParachuteOpenSequence, () => parachute.PlayRepeating(parachutableInfo.ParachuteIdleSequence));
|
|
||||||
}
|
|
||||||
|
|
||||||
var shadowSprite = parachutableInfo != null ? parachutableInfo.ShadowSequence : null;
|
|
||||||
if (shadowSprite != null)
|
|
||||||
{
|
|
||||||
shadow = new Animation(cargo.World, shadowSprite);
|
|
||||||
shadow.PlayRepeating(parachutableInfo.ParachuteIdleSequence);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parachutableInfo != null)
|
|
||||||
parachuteOffset = parachutableInfo.ParachuteOffset;
|
|
||||||
|
|
||||||
// Adjust x,y to match the target subcell
|
|
||||||
cargo.Trait<IPositionable>().SetPosition(cargo, cargo.World.Map.CellContaining(dropPosition));
|
|
||||||
var cp = cargo.CenterPosition;
|
|
||||||
pos = new WPos(cp.X, cp.Y, dropPosition.Z);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Tick(World world)
|
|
||||||
{
|
|
||||||
if (parachute != null)
|
|
||||||
parachute.Tick();
|
|
||||||
|
|
||||||
if (shadow != null)
|
|
||||||
shadow.Tick();
|
|
||||||
|
|
||||||
pos -= fallVector;
|
|
||||||
|
|
||||||
if (pos.Z <= 0)
|
|
||||||
{
|
|
||||||
world.AddFrameEndTask(w =>
|
|
||||||
{
|
|
||||||
w.Remove(this);
|
|
||||||
cargo.CancelActivity();
|
|
||||||
w.Add(cargo);
|
|
||||||
|
|
||||||
foreach (var npl in cargo.TraitsImplementing<INotifyParachuteLanded>())
|
|
||||||
npl.OnLanded();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<IRenderable> Render(WorldRenderer wr)
|
|
||||||
{
|
|
||||||
var rc = cargo.Render(wr);
|
|
||||||
|
|
||||||
// Don't render anything if the cargo is invisible (e.g. under fog)
|
|
||||||
if (!rc.Any())
|
|
||||||
yield break;
|
|
||||||
|
|
||||||
var parachuteShadowPalette = wr.Palette(parachutableInfo.ParachuteShadowPalette);
|
|
||||||
foreach (var c in rc)
|
|
||||||
{
|
|
||||||
if (!c.IsDecoration && shadow == null)
|
|
||||||
yield return c.WithPalette(parachuteShadowPalette).WithZOffset(c.ZOffset - 1).AsDecoration();
|
|
||||||
|
|
||||||
yield return c.OffsetBy(pos - c.Pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
var shadowPalette = !string.IsNullOrEmpty(parachutableInfo.ShadowPalette) ? wr.Palette(parachutableInfo.ShadowPalette) : rc.First().Palette;
|
|
||||||
if (shadow != null)
|
|
||||||
foreach (var r in shadow.Render(pos - new WVec(0, 0, pos.Z), WVec.Zero, 1, shadowPalette, 1f))
|
|
||||||
yield return r;
|
|
||||||
|
|
||||||
var parachutePalette = !string.IsNullOrEmpty(parachutableInfo.ParachutePalette) ? wr.Palette(parachutableInfo.ParachutePalette) : rc.First().Palette;
|
|
||||||
if (parachute != null)
|
|
||||||
foreach (var r in parachute.Render(pos, parachuteOffset, 1, parachutePalette, 1f))
|
|
||||||
yield return r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -122,6 +122,7 @@
|
|||||||
<Compile Include="Activities\Move\Move.cs" />
|
<Compile Include="Activities\Move\Move.cs" />
|
||||||
<Compile Include="Activities\Move\MoveAdjacentTo.cs" />
|
<Compile Include="Activities\Move\MoveAdjacentTo.cs" />
|
||||||
<Compile Include="Activities\Move\MoveWithinRange.cs" />
|
<Compile Include="Activities\Move\MoveWithinRange.cs" />
|
||||||
|
<Compile Include="Activities\Parachute.cs" />
|
||||||
<Compile Include="Activities\Rearm.cs" />
|
<Compile Include="Activities\Rearm.cs" />
|
||||||
<Compile Include="Activities\RemoveSelf.cs" />
|
<Compile Include="Activities\RemoveSelf.cs" />
|
||||||
<Compile Include="Activities\Repair.cs" />
|
<Compile Include="Activities\Repair.cs" />
|
||||||
@@ -159,7 +160,6 @@
|
|||||||
<Compile Include="Effects\LaserZap.cs" />
|
<Compile Include="Effects\LaserZap.cs" />
|
||||||
<Compile Include="Effects\Missile.cs" />
|
<Compile Include="Effects\Missile.cs" />
|
||||||
<Compile Include="Effects\NukeLaunch.cs" />
|
<Compile Include="Effects\NukeLaunch.cs" />
|
||||||
<Compile Include="Effects\Parachute.cs" />
|
|
||||||
<Compile Include="Effects\PowerdownIndicator.cs" />
|
<Compile Include="Effects\PowerdownIndicator.cs" />
|
||||||
<Compile Include="Effects\RallyPointIndicator.cs" />
|
<Compile Include="Effects\RallyPointIndicator.cs" />
|
||||||
<Compile Include="Effects\RepairIndicator.cs" />
|
<Compile Include="Effects\RepairIndicator.cs" />
|
||||||
@@ -433,6 +433,7 @@
|
|||||||
<Compile Include="Traits\Render\WithHarvestOverlay.cs" />
|
<Compile Include="Traits\Render\WithHarvestOverlay.cs" />
|
||||||
<Compile Include="Traits\Render\WithIdleOverlay.cs" />
|
<Compile Include="Traits\Render\WithIdleOverlay.cs" />
|
||||||
<Compile Include="Traits\Render\WithMuzzleFlash.cs" />
|
<Compile Include="Traits\Render\WithMuzzleFlash.cs" />
|
||||||
|
<Compile Include="Traits\Render\WithParachute.cs" />
|
||||||
<Compile Include="Traits\Render\WithRangeCircle.cs" />
|
<Compile Include="Traits\Render\WithRangeCircle.cs" />
|
||||||
<Compile Include="Traits\Render\WithRankDecoration.cs" />
|
<Compile Include="Traits\Render\WithRankDecoration.cs" />
|
||||||
<Compile Include="Traits\Render\WithRepairAnimation.cs" />
|
<Compile Include="Traits\Render\WithRepairAnimation.cs" />
|
||||||
|
|||||||
@@ -125,8 +125,40 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
public IEnumerable<Pair<CPos, SubCell>> OccupiedCells() { return new[] { Pair.New(Location, SubCell.FullCell) }; }
|
public IEnumerable<Pair<CPos, SubCell>> OccupiedCells() { return new[] { Pair.New(Location, SubCell.FullCell) }; }
|
||||||
|
|
||||||
public WPos CenterPosition { get; private set; }
|
public WPos CenterPosition { get; private set; }
|
||||||
public void SetPosition(Actor self, WPos pos) { SetPosition(self, self.World.Map.CellContaining(pos)); }
|
|
||||||
public void SetVisualPosition(Actor self, WPos pos) { SetPosition(self, self.World.Map.CellContaining(pos)); }
|
// Sets the location (Location) and visual position (CenterPosition)
|
||||||
|
public void SetPosition(Actor self, WPos pos)
|
||||||
|
{
|
||||||
|
var cell = self.World.Map.CellContaining(pos);
|
||||||
|
SetLocation(self, cell);
|
||||||
|
SetVisualPosition(self, self.World.Map.CenterOfCell(cell) + new WVec(0, 0, pos.Z));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the location (Location) and visual position (CenterPosition)
|
||||||
|
public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.Any)
|
||||||
|
{
|
||||||
|
SetLocation(self, cell, subCell);
|
||||||
|
SetVisualPosition(self, self.World.Map.CenterOfCell(cell));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets only the visual position (CenterPosition)
|
||||||
|
public void SetVisualPosition(Actor self, WPos pos)
|
||||||
|
{
|
||||||
|
CenterPosition = pos;
|
||||||
|
if (self.IsInWorld)
|
||||||
|
{
|
||||||
|
self.World.ScreenMap.Update(self);
|
||||||
|
self.World.ActorMap.UpdatePosition(self, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets only the location (Location)
|
||||||
|
void SetLocation(Actor self, CPos cell, SubCell subCell = SubCell.Any)
|
||||||
|
{
|
||||||
|
self.World.ActorMap.RemoveInfluence(self, this);
|
||||||
|
Location = cell;
|
||||||
|
self.World.ActorMap.AddInfluence(self, this);
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return self.Location == location && ticks + 1 == info.Lifetime * 25; }
|
public bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any) { return self.Location == location && ticks + 1 == info.Lifetime * 25; }
|
||||||
public SubCell GetValidSubCell(SubCell preferred = SubCell.Any) { return SubCell.FullCell; }
|
public SubCell GetValidSubCell(SubCell preferred = SubCell.Any) { return SubCell.FullCell; }
|
||||||
@@ -155,21 +187,12 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return GetAvailableSubCell(a, SubCell.Any, ignoreActor, checkTransientActors) != SubCell.Invalid;
|
return GetAvailableSubCell(a, SubCell.Any, ignoreActor, checkTransientActors) != SubCell.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.Any)
|
|
||||||
{
|
|
||||||
self.World.ActorMap.RemoveInfluence(self, this);
|
|
||||||
Location = cell;
|
|
||||||
CenterPosition = self.World.Map.CenterOfCell(cell);
|
|
||||||
|
|
||||||
if (self.IsInWorld)
|
|
||||||
{
|
|
||||||
self.World.ActorMap.UpdatePosition(self, this);
|
|
||||||
self.World.ScreenMap.Update(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CrushableBy(string[] crushClasses, Player owner)
|
public bool CrushableBy(string[] crushClasses, Player owner)
|
||||||
{
|
{
|
||||||
|
// Crate can only be crushed if it is not in the air or underground
|
||||||
|
if (CenterPosition.Z != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
return crushClasses.Contains(info.CrushClass);
|
return crushClasses.Contains(info.CrushClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,10 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public bool CrushableBy(string[] crushClasses, Player crushOwner)
|
public bool CrushableBy(string[] crushClasses, Player crushOwner)
|
||||||
{
|
{
|
||||||
|
// Only make actor crushable if it is on the ground
|
||||||
|
if (self.CenterPosition.Z != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!info.CrushedByFriendlies && crushOwner.IsAlliedWith(self.Owner))
|
if (!info.CrushedByFriendlies && crushOwner.IsAlliedWith(self.Owner))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using OpenRA.Mods.Common.Effects;
|
using OpenRA.Mods.Common.Activities;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
@@ -18,10 +18,19 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
public class EjectOnDeathInfo : ITraitInfo
|
public class EjectOnDeathInfo : ITraitInfo
|
||||||
{
|
{
|
||||||
[ActorReference]
|
[ActorReference]
|
||||||
|
[Desc("Name of the unit to eject. This actor type needs to have the Parachutable trait defined.")]
|
||||||
public readonly string PilotActor = "E1";
|
public readonly string PilotActor = "E1";
|
||||||
|
|
||||||
|
[Desc("Probability that the aircraft's pilot gets ejected once the aircraft is destroyed.")]
|
||||||
public readonly int SuccessRate = 50;
|
public readonly int SuccessRate = 50;
|
||||||
|
|
||||||
|
[Desc("Sound to play when ejecting the pilot from the aircraft.")]
|
||||||
public readonly string ChuteSound = "chute1.aud";
|
public readonly string ChuteSound = "chute1.aud";
|
||||||
|
|
||||||
|
[Desc("Can a destroyed aircraft eject its pilot while it has not yet fallen to ground level?")]
|
||||||
public readonly bool EjectInAir = false;
|
public readonly bool EjectInAir = false;
|
||||||
|
|
||||||
|
[Desc("Can a destroyed aircraft eject its pilot when it falls to ground level?")]
|
||||||
public readonly bool EjectOnGround = false;
|
public readonly bool EjectOnGround = false;
|
||||||
|
|
||||||
[Desc("Risks stuck units when they don't have the Paratrooper trait.")]
|
[Desc("Risks stuck units when they don't have the Paratrooper trait.")]
|
||||||
@@ -66,7 +75,11 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
{
|
{
|
||||||
if (cp.Z > 0)
|
if (cp.Z > 0)
|
||||||
{
|
{
|
||||||
self.World.AddFrameEndTask(w => w.Add(new Parachute(pilot, cp)));
|
self.World.AddFrameEndTask(w =>
|
||||||
|
{
|
||||||
|
w.Add(pilot);
|
||||||
|
pilot.QueueActivity(new Parachute(pilot, cp));
|
||||||
|
});
|
||||||
Sound.Play(info.ChuteSound, cp);
|
Sound.Play(info.ChuteSound, cp);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -308,6 +308,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
[Sync] public int PathHash; // written by Move.EvalPath, to temporarily debug this crap.
|
[Sync] public int PathHash; // written by Move.EvalPath, to temporarily debug this crap.
|
||||||
|
|
||||||
|
// Sets only the location (fromCell, toCell, FromSubCell, ToSubCell)
|
||||||
public void SetLocation(CPos from, SubCell fromSub, CPos to, SubCell toSub)
|
public void SetLocation(CPos from, SubCell fromSub, CPos to, SubCell toSub)
|
||||||
{
|
{
|
||||||
if (FromCell == from && ToCell == to && FromSubCell == fromSub && ToSubCell == toSub)
|
if (FromCell == from && ToCell == to && FromSubCell == fromSub && ToSubCell == toSub)
|
||||||
@@ -368,6 +369,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return preferred;
|
return preferred;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets the location (fromCell, toCell, FromSubCell, ToSubCell) and visual position (CenterPosition)
|
||||||
public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.Any)
|
public void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.Any)
|
||||||
{
|
{
|
||||||
subCell = GetValidSubCell(subCell);
|
subCell = GetValidSubCell(subCell);
|
||||||
@@ -376,14 +378,16 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
FinishedMoving(self);
|
FinishedMoving(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets the location (fromCell, toCell, FromSubCell, ToSubCell) and visual position (CenterPosition)
|
||||||
public void SetPosition(Actor self, WPos pos)
|
public void SetPosition(Actor self, WPos pos)
|
||||||
{
|
{
|
||||||
var cell = self.World.Map.CellContaining(pos);
|
var cell = self.World.Map.CellContaining(pos);
|
||||||
SetLocation(cell, FromSubCell, cell, FromSubCell);
|
SetLocation(cell, FromSubCell, cell, FromSubCell);
|
||||||
SetVisualPosition(self, pos);
|
SetVisualPosition(self, self.World.Map.CenterOfSubCell(cell, FromSubCell) + new WVec(0, 0, pos.Z));
|
||||||
FinishedMoving(self);
|
FinishedMoving(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets only the visual position (CenterPosition)
|
||||||
public void SetVisualPosition(Actor self, WPos pos)
|
public void SetVisualPosition(Actor self, WPos pos)
|
||||||
{
|
{
|
||||||
CenterPosition = pos;
|
CenterPosition = pos;
|
||||||
@@ -548,6 +552,10 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public void EnteringCell(Actor self)
|
public void EnteringCell(Actor self)
|
||||||
{
|
{
|
||||||
|
// Only make actor crush if it is on the ground
|
||||||
|
if (self.CenterPosition.Z != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
var crushables = self.World.ActorMap.GetUnitsAt(ToCell).Where(a => a != self)
|
var crushables = self.World.ActorMap.GetUnitsAt(ToCell).Where(a => a != self)
|
||||||
.SelectMany(a => a.TraitsImplementing<ICrushable>().Where(b => b.CrushableBy(Info.Crushes, self.Owner)));
|
.SelectMany(a => a.TraitsImplementing<ICrushable>().Where(b => b.CrushableBy(Info.Crushes, self.Owner)));
|
||||||
foreach (var crushable in crushables)
|
foreach (var crushable in crushables)
|
||||||
@@ -556,6 +564,10 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public void FinishedMoving(Actor self)
|
public void FinishedMoving(Actor self)
|
||||||
{
|
{
|
||||||
|
// Only make actor crush if it is on the ground
|
||||||
|
if (self.CenterPosition.Z != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
var crushables = self.World.ActorMap.GetUnitsAt(ToCell).Where(a => a != self)
|
var crushables = self.World.ActorMap.GetUnitsAt(ToCell).Where(a => a != self)
|
||||||
.SelectMany(a => a.TraitsImplementing<ICrushable>().Where(c => c.CrushableBy(Info.Crushes, self.Owner)));
|
.SelectMany(a => a.TraitsImplementing<ICrushable>().Where(c => c.CrushableBy(Info.Crushes, self.Owner)));
|
||||||
foreach (var crushable in crushables)
|
foreach (var crushable in crushables)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using OpenRA.Mods.Common.Effects;
|
using OpenRA.Mods.Common.Activities;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Traits
|
namespace OpenRA.Mods.Common.Traits
|
||||||
@@ -82,7 +82,11 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
droppedAt.Add(self.Location);
|
droppedAt.Add(self.Location);
|
||||||
|
|
||||||
var a = cargo.Unload(self);
|
var a = cargo.Unload(self);
|
||||||
self.World.AddFrameEndTask(w => w.Add(new Parachute(a, self.CenterPosition)));
|
self.World.AddFrameEndTask(w =>
|
||||||
|
{
|
||||||
|
w.Add(a);
|
||||||
|
a.QueueActivity(new Parachute(a, self.CenterPosition));
|
||||||
|
});
|
||||||
Sound.Play(info.ChuteSound, self.CenterPosition);
|
Sound.Play(info.ChuteSound, self.CenterPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,25 +30,11 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
[SequenceReference("CorpseSequenceCollection")] public readonly string WaterCorpseSequence = null;
|
[SequenceReference("CorpseSequenceCollection")] public readonly string WaterCorpseSequence = null;
|
||||||
public readonly string WaterCorpsePalette = "effect";
|
public readonly string WaterCorpsePalette = "effect";
|
||||||
|
|
||||||
public readonly string ParachuteSequence = null;
|
|
||||||
[SequenceReference("ParachuteSequence")] public readonly string ParachuteOpenSequence = null;
|
|
||||||
[SequenceReference("ParachuteSequence")] public readonly string ParachuteIdleSequence = null;
|
|
||||||
|
|
||||||
[Desc("Optional, otherwise defaults to the palette the actor is using.")]
|
|
||||||
public readonly string ParachutePalette = null;
|
|
||||||
|
|
||||||
[Desc("Used to clone the actor with this palette and render it with a visual offset below.")]
|
|
||||||
public readonly string ParachuteShadowPalette = "shadow";
|
|
||||||
|
|
||||||
public readonly WVec ParachuteOffset = WVec.Zero;
|
|
||||||
|
|
||||||
public readonly int FallRate = 13;
|
public readonly int FallRate = 13;
|
||||||
|
|
||||||
[Desc("Alternative to ParachuteShadowPalette which disables it and allows to set a custom sprite sequence instead.")]
|
[UpgradeGrantedReference]
|
||||||
public readonly string ShadowSequence = null;
|
[Desc("Upgrade to grant to this actor when parachuting. Normally used to render the parachute using the WithParachute trait.")]
|
||||||
|
public readonly string[] ParachuteUpgrade = { "parachute" };
|
||||||
[Desc("Optional, otherwise defaults to the palette the actor is using.")]
|
|
||||||
public readonly string ShadowPalette = null;
|
|
||||||
|
|
||||||
public object Create(ActorInitializer init) { return new Parachutable(init, this); }
|
public object Create(ActorInitializer init) { return new Parachutable(init, this); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,16 +127,17 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
wasModifying = rsm.IsModifyingSequence;
|
wasModifying = rsm.IsModifyingSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((state == AnimationState.Moving || dirty) && !move.IsMoving)
|
if ((state != AnimationState.Moving || dirty) && move.IsMoving)
|
||||||
{
|
|
||||||
state = AnimationState.Waiting;
|
|
||||||
PlayStandAnimation(self);
|
|
||||||
}
|
|
||||||
else if ((state != AnimationState.Moving || dirty) && move.IsMoving)
|
|
||||||
{
|
{
|
||||||
state = AnimationState.Moving;
|
state = AnimationState.Moving;
|
||||||
DefaultAnimation.PlayRepeating(NormalizeInfantrySequence(self, Info.MoveSequence));
|
DefaultAnimation.PlayRepeating(NormalizeInfantrySequence(self, Info.MoveSequence));
|
||||||
}
|
}
|
||||||
|
else if (((state == AnimationState.Moving || dirty) && !move.IsMoving)
|
||||||
|
|| ((state == AnimationState.Idle || state == AnimationState.IdleAnimating) && !self.IsIdle))
|
||||||
|
{
|
||||||
|
state = AnimationState.Waiting;
|
||||||
|
PlayStandAnimation(self);
|
||||||
|
}
|
||||||
|
|
||||||
dirty = false;
|
dirty = false;
|
||||||
}
|
}
|
||||||
|
|||||||
154
OpenRA.Mods.Common/Traits/Render/WithParachute.cs
Normal file
154
OpenRA.Mods.Common/Traits/Render/WithParachute.cs
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2015 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. For more information,
|
||||||
|
* see COPYING.
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using OpenRA.Graphics;
|
||||||
|
using OpenRA.Mods.Common.Graphics;
|
||||||
|
using OpenRA.Traits;
|
||||||
|
|
||||||
|
namespace OpenRA.Mods.Common.Traits
|
||||||
|
{
|
||||||
|
[Desc("Renders a parachute on units.")]
|
||||||
|
public class WithParachuteInfo : UpgradableTraitInfo, ITraitInfo, IRenderActorPreviewSpritesInfo, Requires<RenderSpritesInfo>, Requires<IBodyOrientationInfo>
|
||||||
|
{
|
||||||
|
[Desc("The image that contains the parachute sequences.")]
|
||||||
|
public readonly string Image = null;
|
||||||
|
|
||||||
|
[Desc("Parachute opening sequence.")]
|
||||||
|
[SequenceReference("Image")] public readonly string OpeningSequence = null;
|
||||||
|
|
||||||
|
[Desc("Parachute idle sequence.")]
|
||||||
|
[SequenceReference("Image")] public readonly string Sequence = null;
|
||||||
|
|
||||||
|
[Desc("Parachute closing sequence. Defaults to opening sequence played backwards.")]
|
||||||
|
[SequenceReference("Image")] public readonly string ClosingSequence = null;
|
||||||
|
|
||||||
|
[Desc("Palette used to render the parachute.")]
|
||||||
|
public readonly string Palette = "player";
|
||||||
|
|
||||||
|
[Desc("Parachute position relative to the paradropped unit.")]
|
||||||
|
public readonly WVec Offset = new WVec(0, 0, 384);
|
||||||
|
|
||||||
|
[Desc("The image that contains the shadow sequence for the paradropped unit.")]
|
||||||
|
public readonly string ShadowImage = null;
|
||||||
|
|
||||||
|
[Desc("Paradropped unit's shadow sequence.")]
|
||||||
|
[SequenceReference("ShadowImage")] public readonly string ShadowSequence = null;
|
||||||
|
|
||||||
|
[Desc("Palette used to render the paradropped unit's shadow.")]
|
||||||
|
public readonly string ShadowPalette = "player";
|
||||||
|
|
||||||
|
[Desc("Shadow position relative to the paradropped unit's intended landing position.")]
|
||||||
|
public readonly WVec ShadowOffset = new WVec(0, 128, 0);
|
||||||
|
|
||||||
|
[Desc("Z-offset to apply on the shadow sequence.")]
|
||||||
|
public readonly int ShadowZOffset = 0;
|
||||||
|
|
||||||
|
public override object Create(ActorInitializer init) { return new WithParachute(init.Self, this); }
|
||||||
|
|
||||||
|
public IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
|
||||||
|
{
|
||||||
|
if (UpgradeMinEnabledLevel > 0)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
if (image == null)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
// For this, image must not be null
|
||||||
|
if (Palette != null)
|
||||||
|
p = init.WorldRenderer.Palette(Palette);
|
||||||
|
|
||||||
|
var anim = new Animation(init.World, image);
|
||||||
|
anim.PlayThen(OpeningSequence, () => anim.PlayRepeating(Sequence));
|
||||||
|
|
||||||
|
var body = init.Actor.Traits.Get<BodyOrientationInfo>();
|
||||||
|
var facing = init.Contains<FacingInit>() ? init.Get<FacingInit, int>() : 0;
|
||||||
|
var orientation = body.QuantizeOrientation(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(facing)), facings);
|
||||||
|
var offset = body.LocalToWorld(Offset.Rotate(orientation));
|
||||||
|
yield return new SpriteActorPreview(anim, offset, offset.Y + offset.Z + 1, p, rs.Scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WithParachute : UpgradableTrait<WithParachuteInfo>, IRender
|
||||||
|
{
|
||||||
|
readonly Animation shadow;
|
||||||
|
readonly AnimationWithOffset anim;
|
||||||
|
readonly WithParachuteInfo info;
|
||||||
|
|
||||||
|
bool renderProlonged = false;
|
||||||
|
|
||||||
|
public WithParachute(Actor self, WithParachuteInfo info)
|
||||||
|
: base(info)
|
||||||
|
{
|
||||||
|
this.info = info;
|
||||||
|
|
||||||
|
if (info.ShadowImage != null)
|
||||||
|
{
|
||||||
|
shadow = new Animation(self.World, info.ShadowImage);
|
||||||
|
shadow.PlayRepeating(info.ShadowSequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.Image == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// For this, info.Image must not be null
|
||||||
|
var overlay = new Animation(self.World, info.Image);
|
||||||
|
var body = self.Trait<IBodyOrientation>();
|
||||||
|
anim = new AnimationWithOffset(overlay,
|
||||||
|
() => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))),
|
||||||
|
() => IsTraitDisabled && !renderProlonged,
|
||||||
|
() => false,
|
||||||
|
p => WithTurret.ZOffsetFromCenter(self, p, 1));
|
||||||
|
|
||||||
|
var rs = self.Trait<RenderSprites>();
|
||||||
|
rs.Add(anim, info.Palette);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpgradeEnabled(Actor self)
|
||||||
|
{
|
||||||
|
if (info.Image == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
anim.Animation.PlayThen(info.OpeningSequence, () => anim.Animation.PlayRepeating(info.Sequence));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpgradeDisabled(Actor self)
|
||||||
|
{
|
||||||
|
if (info.Image == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
renderProlonged = true;
|
||||||
|
if (!string.IsNullOrEmpty(info.ClosingSequence))
|
||||||
|
anim.Animation.PlayThen(info.ClosingSequence, () => renderProlonged = false);
|
||||||
|
else
|
||||||
|
anim.Animation.PlayBackwardsThen(info.OpeningSequence, () => renderProlonged = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
|
||||||
|
{
|
||||||
|
if (info.ShadowImage == null)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
if (IsTraitDisabled)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
if (self.IsDead || !self.IsInWorld)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
if (self.World.FogObscures(self))
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
shadow.Tick();
|
||||||
|
var pos = self.CenterPosition - new WVec(0, 0, self.CenterPosition.Z);
|
||||||
|
yield return new SpriteRenderable(shadow.Image, pos, info.ShadowOffset, info.ShadowZOffset, wr.Palette(info.ShadowPalette), 1, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1524,6 +1524,89 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (engineVersion < 20150714)
|
||||||
|
{
|
||||||
|
// Move certain properties from Parachutable to new WithParachute trait
|
||||||
|
// Add dependency traits to actors implementing Parachutable
|
||||||
|
// Make otherwise targetable parachuting actors untargetable
|
||||||
|
var par = node.Value.Nodes.FirstOrDefault(n => n.Key == "Parachutable");
|
||||||
|
if (par != null)
|
||||||
|
{
|
||||||
|
var withParachute = new MiniYamlNode("WithParachute", null, new List<MiniYamlNode>
|
||||||
|
{
|
||||||
|
new MiniYamlNode("UpgradeTypes", "parachute"),
|
||||||
|
new MiniYamlNode("UpgradeMinEnabledLevel", "1")
|
||||||
|
});
|
||||||
|
|
||||||
|
var copyProp = new Action<string, string, string>((srcName, dstName, defValue) =>
|
||||||
|
{
|
||||||
|
var prop = par.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith(srcName));
|
||||||
|
if (prop != null && prop.Value.Value != defValue)
|
||||||
|
withParachute.Value.Nodes.Add(new MiniYamlNode(dstName, prop.Value.Value));
|
||||||
|
});
|
||||||
|
|
||||||
|
var moveProp = new Action<string, string, string>((srcName, dstName, defValue) =>
|
||||||
|
{
|
||||||
|
copyProp(srcName, dstName, defValue);
|
||||||
|
par.Value.Nodes.RemoveAll(n => n.Key.StartsWith(srcName));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (par.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("ShadowSequence")) != null)
|
||||||
|
{
|
||||||
|
moveProp("ShadowSequence", "ShadowImage", null);
|
||||||
|
copyProp("ParachuteIdleSequence", "ShadowSequence", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
moveProp("ParachuteSequence", "Image", null);
|
||||||
|
moveProp("ParachuteIdleSequence", "Sequence", null);
|
||||||
|
|
||||||
|
moveProp("ParachuteOpenSequence", "OpeningSequence", null);
|
||||||
|
|
||||||
|
moveProp("ParachutePalette", "Palette", "player");
|
||||||
|
moveProp("ShadowPalette", "ShadowPalette", "player");
|
||||||
|
|
||||||
|
moveProp("ParachuteOffset", "Offset", "player");
|
||||||
|
|
||||||
|
par.Value.Nodes.RemoveAll(n => n.Key.StartsWith("ParachuteShadowPalette"));
|
||||||
|
|
||||||
|
node.Value.Nodes.Add(withParachute);
|
||||||
|
|
||||||
|
var otherNodes = nodes;
|
||||||
|
var inherits = new Func<string, bool>(traitName => node.Value.Nodes.Where(n => n.Key.StartsWith("Inherits"))
|
||||||
|
.Any(inh => otherNodes.First(n => n.Key.StartsWith(inh.Value.Value)).Value.Nodes.Any(n => n.Key.StartsWith(traitName))));
|
||||||
|
|
||||||
|
// For actors that have or inherit a TargetableUnit, disable the trait while parachuting
|
||||||
|
var tu = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("TargetableUnit"));
|
||||||
|
if (tu != null)
|
||||||
|
{
|
||||||
|
tu.Value.Nodes.Add(new MiniYamlNode("UpgradeTypes", "parachute"));
|
||||||
|
tu.Value.Nodes.Add(new MiniYamlNode("UpgradeMaxEnabledLevel", "0"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (inherits("TargetableUnit"))
|
||||||
|
{
|
||||||
|
node.Value.Nodes.Add(new MiniYamlNode("TargetableUnit", null, new List<MiniYamlNode>
|
||||||
|
{
|
||||||
|
new MiniYamlNode("UpgradeTypes", "parachute"),
|
||||||
|
new MiniYamlNode("UpgradeMaxEnabledLevel", "0")
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var has = new Func<string, bool>(traitName => node.Value.Nodes.Any(n => n.Key.StartsWith(traitName)));
|
||||||
|
|
||||||
|
// If actor does not have nor inherits an UpgradeManager, add one
|
||||||
|
if (!has("UpgradeManager") && !inherits("UpgradeManager"))
|
||||||
|
node.Value.Nodes.Add(new MiniYamlNode("UpgradeManager", ""));
|
||||||
|
|
||||||
|
// If actor does not have nor inherits a BodyOrientation, add one
|
||||||
|
if (!has("BodyOrientation") && !inherits("BodyOrientation"))
|
||||||
|
node.Value.Nodes.Add(new MiniYamlNode("BodyOrientation", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
|
UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user