Merge pull request #5695 from Mailaender/parachute-dangers

Added the possibility for paratroopers to die on impact
This commit is contained in:
Paul Chote
2014-07-05 19:49:15 +12:00
18 changed files with 200 additions and 63 deletions

View File

@@ -18,8 +18,11 @@ namespace OpenRA.Mods.RA
{
class CrateInfo : ITraitInfo, IOccupySpaceInfo, Requires<RenderSpritesInfo>
{
public readonly int Lifetime = 5; // Seconds
[Desc("Seconds")]
public readonly int Lifetime = 5;
public readonly string[] TerrainTypes = { };
public object Create(ActorInitializer init) { return new Crate(init, this); }
}

View File

@@ -99,7 +99,7 @@ namespace OpenRA.Mods.RA
plane.CancelActivity();
plane.QueueActivity(new FlyAttack(Target.FromCell(w, p)));
plane.Trait<ParaDrop>().SetLZ(p);
plane.Trait<ParaDrop>().SetLZ(p, true);
plane.Trait<Cargo>().Load(plane, crate);
}
else

View File

@@ -18,22 +18,39 @@ namespace OpenRA.Mods.RA.Effects
{
public class Parachute : IEffect
{
readonly Animation paraAnim;
readonly ParachutableInfo parachutableInfo;
readonly Animation parachute;
readonly Animation shadow;
readonly WVec parachuteOffset;
readonly Actor cargo;
WPos pos;
WVec fallRate = new WVec(0, 0, 13);
WVec fallVector;
public Parachute(Actor cargo, WPos dropPosition)
{
this.cargo = cargo;
var pai = cargo.Info.Traits.GetOrDefault<ParachuteAttachmentInfo>();
paraAnim = new Animation(cargo.World, pai != null ? pai.ParachuteSprite : "parach");
paraAnim.PlayThen("open", () => paraAnim.PlayRepeating("idle"));
parachutableInfo = cargo.Info.Traits.GetOrDefault<ParachutableInfo>();
if (pai != null)
parachuteOffset = pai.Offset;
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("open", () => parachute.PlayRepeating("idle"));
}
var shadowSprite = parachutableInfo != null ? parachutableInfo.ShadowSequence : null;
if (shadowSprite != null)
{
shadow = new Animation(cargo.World, shadowSprite);
shadow.PlayRepeating("idle");
}
if (parachutableInfo != null)
parachuteOffset = parachutableInfo.ParachuteOffset;
// Adjust x,y to match the target subcell
cargo.Trait<IPositionable>().SetPosition(cargo, cargo.World.Map.CellContaining(dropPosition));
@@ -43,9 +60,13 @@ namespace OpenRA.Mods.RA.Effects
public void Tick(World world)
{
paraAnim.Tick();
if (parachute != null)
parachute.Tick();
pos -= fallRate;
if (shadow != null)
shadow.Tick();
pos -= fallVector;
if (pos.Z <= 0)
{
@@ -69,17 +90,24 @@ namespace OpenRA.Mods.RA.Effects
if (!rc.Any())
yield break;
var shadow = wr.Palette("shadow");
var parachuteShadowPalette = wr.Palette(parachutableInfo.ParachuteShadowPalette);
foreach (var c in rc)
{
if (!c.IsDecoration)
yield return c.WithPalette(shadow).WithZOffset(c.ZOffset - 1).AsDecoration();
if (!c.IsDecoration && shadow == null)
yield return c.WithPalette(parachuteShadowPalette).WithZOffset(c.ZOffset - 1).AsDecoration();
yield return c.OffsetBy(pos - c.Pos);
}
foreach (var r in paraAnim.Render(pos, parachuteOffset, 1, rc.First().Palette, 1f))
yield return r;
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;
}
}
}

View File

@@ -24,6 +24,9 @@ namespace OpenRA.Mods.RA
public readonly string ChuteSound = "chute1.aud";
public readonly bool EjectInAir = false;
public readonly bool EjectOnGround = false;
[Desc("Risks stuck units when they don't have the Paratrooper trait.")]
public readonly bool AllowUnsuitableCell = false;
}
public class EjectOnDeath : INotifyKilled
@@ -47,7 +50,7 @@ namespace OpenRA.Mods.RA
new TypeDictionary { new OwnerInit(self.Owner), new LocationInit(self.Location) });
if (IsSuitableCell(self, pilot))
if (info.AllowUnsuitableCell || IsSuitableCell(self, pilot))
{
if (cp.Z > 0)
{

View File

@@ -279,7 +279,6 @@
<Compile Include="PaletteFromFile.cs" />
<Compile Include="PaletteFromRGBA.cs" />
<Compile Include="ParaDrop.cs" />
<Compile Include="ParachuteAttachment.cs" />
<Compile Include="Passenger.cs" />
<Compile Include="Player\PlayerStatistics.cs" />
<Compile Include="Player\ActorGroupProxy.cs" />
@@ -520,6 +519,7 @@
<Compile Include="Lint\CheckActors.cs" />
<Compile Include="Lint\CheckMapCordon.cs" />
<Compile Include="World\ResourceLayer.cs" />
<Compile Include="Parachutable.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj">

View File

@@ -24,13 +24,15 @@ namespace OpenRA.Mods.RA
public class ParaDrop : ITick
{
bool checkForSuitableCell;
readonly List<CPos> droppedAt = new List<CPos>();
CPos lz;
public void SetLZ(CPos lz)
public void SetLZ(CPos lz, bool checkLandingCell)
{
this.lz = lz;
droppedAt.Clear();
checkForSuitableCell = checkLandingCell;
}
public void Tick(Actor self)
@@ -45,7 +47,7 @@ namespace OpenRA.Mods.RA
FinishedDropping(self);
else
{
if (!IsSuitableCell(cargo.Peek(self), self.Location))
if (checkForSuitableCell && !IsSuitableCell(cargo.Peek(self), self.Location))
return;
// unload a dude here

View File

@@ -0,0 +1,82 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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.Mods.RA.Effects;
using OpenRA.Mods.RA.Render;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
class ParachutableInfo : ITraitInfo
{
public readonly bool KilledOnImpassableTerrain = true;
public readonly string GroundImpactSound = "squishy2.aud";
public readonly string GroundCorpseSequence = "corpse";
public readonly string GroundCorpsePalette = "effect";
public readonly string WaterImpactSound = "splash9.aud";
public readonly string WaterCorpseSequence = "small_splash";
public readonly string WaterCorpsePalette = "effect";
[Desc("Requires the sub-sequences \"open\" and \"idle\".")]
public readonly string ParachuteSequence = 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;
[Desc("Alternative to ParachuteShadowPalette which disables it and allows to set a custom sprite sequence instead.")]
public readonly string ShadowSequence = null;
[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); }
}
class Parachutable : INotifyParachuteLanded
{
readonly Actor self;
readonly ParachutableInfo info;
readonly IPositionable positionable;
public Parachutable(ActorInitializer init, ParachutableInfo info)
{
this.self = init.self;
this.info = info;
positionable = self.TraitOrDefault<IPositionable>();
}
public void OnLanded()
{
if (!info.KilledOnImpassableTerrain)
return;
if (positionable.CanEnterCell(self.Location))
return;
var terrain = self.World.Map.GetTerrainInfo(self.Location);
var sound = terrain.IsWater ? info.WaterImpactSound : info.GroundImpactSound;
Sound.Play(sound, self.CenterPosition);
var sequence = terrain.IsWater ? info.WaterCorpseSequence : info.GroundCorpseSequence;
var palette = terrain.IsWater ? info.WaterCorpsePalette : info.GroundCorpsePalette;
self.World.AddFrameEndTask(w => w.Add(new Explosion(w, self.OccupiesSpace.CenterPosition, sequence, palette)));
self.Kill(self);
}
}
}

View File

@@ -1,22 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 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.Traits;
namespace OpenRA.Mods.RA
{
class ParachuteAttachmentInfo : TraitInfo<ParachuteAttachment>
{
public readonly string ParachuteSprite = "parach";
public readonly WVec Offset = WVec.Zero;
}
class ParachuteAttachment {}
}

View File

@@ -56,7 +56,7 @@ namespace OpenRA.Mods.RA.Scripting
[Desc("Command transport to paradrop passengers near the target cell.")]
public void Paradrop(CPos cell)
{
paradrop.SetLZ(cell);
paradrop.SetLZ(cell, true);
self.QueueActivity(new FlyAttack(Target.FromCell(self.World, cell)));
}
}

View File

@@ -24,7 +24,11 @@ namespace OpenRA.Mods.RA
[ActorReference]
public string FlareType = "flare";
public readonly int FlareTime = 25 * 60 * 2; // 2 minutes
[Desc("In game ticks. Default value equates to 2 minutes.")]
public readonly int FlareTime = 25 * 60 * 2;
[Desc("Risks stuck units when they don't have the Paratrooper trait.")]
public readonly bool AllowImpassableCells = false;
public override object Create(ActorInitializer init) { return new ParatroopersPower(init.self, this); }
}
@@ -45,8 +49,8 @@ namespace OpenRA.Mods.RA
{
var flare = info.FlareType != null ? w.CreateActor(info.FlareType, new TypeDictionary
{
new LocationInit( order.TargetLocation ),
new OwnerInit( self.Owner ),
new LocationInit(order.TargetLocation),
new OwnerInit(self.Owner),
}) : null;
if (flare != null)
@@ -65,7 +69,7 @@ namespace OpenRA.Mods.RA
a.CancelActivity();
a.QueueActivity(new FlyAttack(Target.FromOrder(self.World, order)));
a.Trait<ParaDrop>().SetLZ(order.TargetLocation);
a.Trait<ParaDrop>().SetLZ(order.TargetLocation, !info.AllowImpassableCells);
var cargo = a.Trait<Cargo>();
foreach (var i in items)

View File

@@ -252,6 +252,22 @@ namespace OpenRA.Utility
node.Key = "StoresResources";
}
// ParachuteAttachment was merged into Parachutable
if (engineVersion < 20140701)
{
if (depth == 1 && node.Key == "ParachuteAttachment")
{
node.Key = "Parachutable";
foreach (var subnode in node.Value.Nodes)
if (subnode.Key == "Offset")
subnode.Key = "ParachuteOffset";
}
if (depth == 2 && node.Key == "ParachuteSprite")
node.Key = "ParachuteSequence";
}
UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
}
}

Binary file not shown.

View File

@@ -196,6 +196,8 @@
LuaScriptEvents:
ScriptTriggers:
DeathSounds:
Parachutable:
FallRate: 130
^Plane:
AppearsOnRadar:

Binary file not shown.

View File

@@ -41,8 +41,9 @@
EjectOnDeath:
PilotActor: e1
SuccessRate: 20
EjectOnGround: yes
EjectInAir: no
EjectOnGround: true
EjectInAir: false
AllowUnsuitableCell: false
Huntable:
LuaScriptEvents:
Demolishable:
@@ -97,8 +98,9 @@
EjectOnDeath:
PilotActor: e1
SuccessRate: 20
EjectOnGround: yes
EjectInAir: no
EjectOnGround: true
EjectInAir: false
AllowUnsuitableCell: false
Huntable:
LuaScriptEvents:
Demolishable:
@@ -149,8 +151,6 @@
GivesBounty:
GpsDot:
String: Infantry
ParachuteAttachment:
Offset: 0,0,427
CrushableInfantry:
CrushSound: squishy2.aud
UpdatesPlayerStatistics:
@@ -175,6 +175,11 @@
DeathSounds@ZAPPED:
DeathSound: Zapped
InfDeaths: 6
Parachutable:
ParachuteOffset: 0,0,427
KilledOnImpassableTerrain: true
ParachuteSequence: parach
ShadowSequence: parach-shadow
^Ship:
AppearsOnRadar:
@@ -211,7 +216,7 @@
^Plane:
AppearsOnRadar:
UseLocation: yes
UseLocation: true
SelectionDecorations:
Selectable:
Voice: GenericVoice
@@ -232,8 +237,9 @@
EjectOnDeath:
PilotActor: E1
SuccessRate: 50
EjectOnGround: no
EjectInAir: yes
EjectOnGround: false
EjectInAir: true
AllowUnsuitableCell: true
GivesBounty:
GpsDot:
String: Plane
@@ -277,7 +283,7 @@
GivesExperience:
CaptureNotification:
EditorAppearance:
RelativeToTopLeft: yes
RelativeToTopLeft: true
ShakeOnDeath:
ProximityCaptor:
Types: Building
@@ -322,7 +328,7 @@
Palette: terrain
GivesExperience:
EditorAppearance:
RelativeToTopLeft: yes
RelativeToTopLeft: true
UseTerrainPalette: true
AutoTargetIgnore:
ProximityCaptor:
@@ -425,7 +431,7 @@
RadarColorFromTerrain:
Terrain: Tree
EditorAppearance:
RelativeToTopLeft: yes
RelativeToTopLeft: true
UseTerrainPalette: true
ProximityCaptor:
Types: Tree
@@ -460,11 +466,11 @@
LuaScriptEvents:
TargetableUnit:
TargetTypes: Ground
RequiresForceFire: yes
RequiresForceFire: true
AutoTargetIgnore:
Capturable:
Type: husk
AllowAllies: yes
AllowAllies: true
CaptureThreshold: 1.0
TransformOnCapture:
ForceHealthPercentage: 25
@@ -501,7 +507,7 @@
BelowUnits:
TargetableBuilding:
TargetTypes: Ground, Water
RequiresForceFire: yes
RequiresForceFire: true
Building:
Footprint: ____ ____
Dimensions: 4,2
@@ -528,7 +534,7 @@
RadarColorFromTerrain:
Terrain: Tree
EditorAppearance:
RelativeToTopLeft: yes
RelativeToTopLeft: true
UseTerrainPalette: true
ProximityCaptor:
Types: Tree

View File

@@ -60,6 +60,9 @@ CRATE:
Crate:
Lifetime: 120
TerrainTypes: Clear, Rough, Road, Water, Ore, Beach
Parachutable:
KilledOnImpassableTerrain: false
ParachuteSequence: parach
GiveCashCrateAction:
Amount: 1000
SelectionShares: 50

View File

@@ -905,6 +905,7 @@ AFLD:
LongDesc: A Badger drops a squad of infantry\nanywhere on the map.
DropItems: E1,E1,E1,E3,E3
SelectTargetSound: slcttgt1.aud
AllowImpassableCells: false
ProductionBar:
SupportPowerChargeBar:
PrimaryBuilding:

View File

@@ -72,6 +72,10 @@ explosion:
small_napalm: napalm1
Start: 0
Length: *
corpse: corpse1
Start: 0
Length: 6
Tick: 1600
pips:
groups:
@@ -366,6 +370,11 @@ parach:
Start: 5
Length: 11
parach-shadow:
idle:
Start: 0
Length: *
atomicup:
idle:
Start: 0