diff --git a/OpenRA.Mods.RA/Crate.cs b/OpenRA.Mods.RA/Crate.cs index df94f6de89..cf45a6ef05 100644 --- a/OpenRA.Mods.RA/Crate.cs +++ b/OpenRA.Mods.RA/Crate.cs @@ -18,8 +18,11 @@ namespace OpenRA.Mods.RA { class CrateInfo : ITraitInfo, IOccupySpaceInfo, Requires { - 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); } } diff --git a/OpenRA.Mods.RA/CrateSpawner.cs b/OpenRA.Mods.RA/CrateSpawner.cs index 938c089f40..bd95dc0abe 100644 --- a/OpenRA.Mods.RA/CrateSpawner.cs +++ b/OpenRA.Mods.RA/CrateSpawner.cs @@ -99,7 +99,7 @@ namespace OpenRA.Mods.RA plane.CancelActivity(); plane.QueueActivity(new FlyAttack(Target.FromCell(w, p))); - plane.Trait().SetLZ(p); + plane.Trait().SetLZ(p, true); plane.Trait().Load(plane, crate); } else diff --git a/OpenRA.Mods.RA/Effects/Parachute.cs b/OpenRA.Mods.RA/Effects/Parachute.cs index f05adcc035..db9fbd2bbb 100644 --- a/OpenRA.Mods.RA/Effects/Parachute.cs +++ b/OpenRA.Mods.RA/Effects/Parachute.cs @@ -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(); - paraAnim = new Animation(cargo.World, pai != null ? pai.ParachuteSprite : "parach"); - paraAnim.PlayThen("open", () => paraAnim.PlayRepeating("idle")); + parachutableInfo = cargo.Info.Traits.GetOrDefault(); - 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().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; } } } diff --git a/OpenRA.Mods.RA/EjectOnDeath.cs b/OpenRA.Mods.RA/EjectOnDeath.cs index 31cf09fe9d..94e501ff14 100644 --- a/OpenRA.Mods.RA/EjectOnDeath.cs +++ b/OpenRA.Mods.RA/EjectOnDeath.cs @@ -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) { diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 613ee4bcba..60f4dac608 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -279,7 +279,6 @@ - @@ -520,6 +519,7 @@ + diff --git a/OpenRA.Mods.RA/ParaDrop.cs b/OpenRA.Mods.RA/ParaDrop.cs index 08a7ca1540..ab6af54edb 100644 --- a/OpenRA.Mods.RA/ParaDrop.cs +++ b/OpenRA.Mods.RA/ParaDrop.cs @@ -24,13 +24,15 @@ namespace OpenRA.Mods.RA public class ParaDrop : ITick { + bool checkForSuitableCell; readonly List droppedAt = new List(); 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 diff --git a/OpenRA.Mods.RA/Parachutable.cs b/OpenRA.Mods.RA/Parachutable.cs new file mode 100644 index 0000000000..2391db913e --- /dev/null +++ b/OpenRA.Mods.RA/Parachutable.cs @@ -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(); + } + + 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); + } + } +} diff --git a/OpenRA.Mods.RA/ParachuteAttachment.cs b/OpenRA.Mods.RA/ParachuteAttachment.cs deleted file mode 100644 index f6850ebc9e..0000000000 --- a/OpenRA.Mods.RA/ParachuteAttachment.cs +++ /dev/null @@ -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 - { - public readonly string ParachuteSprite = "parach"; - public readonly WVec Offset = WVec.Zero; - } - - class ParachuteAttachment {} -} \ No newline at end of file diff --git a/OpenRA.Mods.RA/Scripting/Properties/TransportProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/TransportProperties.cs index 8819cf94d2..c4ce0967bb 100644 --- a/OpenRA.Mods.RA/Scripting/Properties/TransportProperties.cs +++ b/OpenRA.Mods.RA/Scripting/Properties/TransportProperties.cs @@ -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))); } } diff --git a/OpenRA.Mods.RA/SupportPowers/ParatroopersPower.cs b/OpenRA.Mods.RA/SupportPowers/ParatroopersPower.cs index 24bb609e82..46f13bc778 100644 --- a/OpenRA.Mods.RA/SupportPowers/ParatroopersPower.cs +++ b/OpenRA.Mods.RA/SupportPowers/ParatroopersPower.cs @@ -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().SetLZ(order.TargetLocation); + a.Trait().SetLZ(order.TargetLocation, !info.AllowImpassableCells); var cargo = a.Trait(); foreach (var i in items) diff --git a/OpenRA.Utility/UpgradeRules.cs b/OpenRA.Utility/UpgradeRules.cs index 5d707c7f00..12551fb493 100644 --- a/OpenRA.Utility/UpgradeRules.cs +++ b/OpenRA.Utility/UpgradeRules.cs @@ -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); } } diff --git a/mods/d2k/bits/parach.shp b/mods/d2k/bits/parach.shp deleted file mode 100644 index 6ab15cd9ca..0000000000 Binary files a/mods/d2k/bits/parach.shp and /dev/null differ diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml index 621aaac7ce..08863b5172 100644 --- a/mods/d2k/rules/defaults.yaml +++ b/mods/d2k/rules/defaults.yaml @@ -196,6 +196,8 @@ LuaScriptEvents: ScriptTriggers: DeathSounds: + Parachutable: + FallRate: 130 ^Plane: AppearsOnRadar: diff --git a/mods/ra/bits/parach-shadow.shp b/mods/ra/bits/parach-shadow.shp new file mode 100644 index 0000000000..7d848c88a4 Binary files /dev/null and b/mods/ra/bits/parach-shadow.shp differ diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index 950d309d75..0f5d0eac3d 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -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 diff --git a/mods/ra/rules/misc.yaml b/mods/ra/rules/misc.yaml index ba8c2109f7..93051c793f 100644 --- a/mods/ra/rules/misc.yaml +++ b/mods/ra/rules/misc.yaml @@ -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 diff --git a/mods/ra/rules/structures.yaml b/mods/ra/rules/structures.yaml index c2f5f9d5c8..82ad7a130a 100644 --- a/mods/ra/rules/structures.yaml +++ b/mods/ra/rules/structures.yaml @@ -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: diff --git a/mods/ra/sequences/misc.yaml b/mods/ra/sequences/misc.yaml index 1c46d17d88..1092fbf7ae 100644 --- a/mods/ra/sequences/misc.yaml +++ b/mods/ra/sequences/misc.yaml @@ -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