Add drop pod reinforcements.

This commit is contained in:
Matthias Mailänder
2020-06-20 20:59:04 +02:00
committed by tovl
parent dc3dbf6d85
commit c7ba359688
10 changed files with 395 additions and 4 deletions

View File

@@ -0,0 +1,78 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 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.GameRules;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Effects
{
public class DropPodImpact : IProjectile
{
readonly Target target;
readonly Animation entryAnimation;
readonly Player firedBy;
readonly string entryPalette;
readonly WeaponInfo weapon;
readonly WPos launchPos;
int weaponDelay;
bool impacted = false;
public DropPodImpact(Player firedBy, WeaponInfo weapon, World world, WPos launchPos, Target target,
int delay, string entryEffect, string entrySequence, string entryPalette)
{
this.target = target;
this.firedBy = firedBy;
this.weapon = weapon;
this.entryPalette = entryPalette;
weaponDelay = delay;
this.launchPos = launchPos;
entryAnimation = new Animation(world, entryEffect);
entryAnimation.PlayThen(entrySequence, () => Finish(world));
if (weapon.Report != null && weapon.Report.Any())
Game.Sound.Play(SoundType.World, weapon.Report, world, launchPos);
}
public void Tick(World world)
{
entryAnimation.Tick();
if (!impacted && weaponDelay-- <= 0)
{
var warheadArgs = new WarheadArgs
{
Weapon = weapon,
Source = target.CenterPosition,
SourceActor = firedBy.PlayerActor,
WeaponTarget = target
};
weapon.Impact(target, warheadArgs);
impacted = true;
}
}
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
return entryAnimation.Render(launchPos, wr.Palette(entryPalette));
}
void Finish(World world)
{
world.AddFrameEndTask(w => w.Remove(this));
}
}
}

View File

@@ -0,0 +1,159 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 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.GameRules;
using OpenRA.Mods.Cnc.Effects;
using OpenRA.Mods.Common;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Traits
{
public class DropPodsPowerInfo : SupportPowerInfo, IRulesetLoaded
{
[FieldLoader.Require]
[Desc("Drop pod unit")]
[ActorReference(typeof(AircraftInfo), typeof(FallsToEarthInfo))]
public readonly string[] UnitTypes = null;
[Desc("Number of drop pods spawned.")]
public readonly int2 Drops = new int2(5, 8);
[Desc("Sets the approach direction.")]
public readonly int PodFacing = 32;
[Desc("Maximum offset from targetLocation")]
public readonly int PodScatter = 3;
[Desc("Effect sequence sprite image")]
public readonly string EntryEffect = "podring";
[Desc("Effect sequence to display in the air.")]
[SequenceReference("Effect")]
public readonly string EntryEffectSequence = "idle";
[PaletteReference]
public readonly string EntryEffectPalette = "effect";
[ActorReference]
[Desc("Actor to spawn when the attack starts")]
public readonly string CameraActor = null;
[Desc("Number of ticks to keep the camera alive")]
public readonly int CameraRemoveDelay = 25;
[Desc("Which weapon to fire")]
[WeaponReference]
public readonly string Weapon = "Vulcan2";
public WeaponInfo WeaponInfo { get; private set; }
[Desc("Apply the weapon impact this many ticks into the effect")]
public readonly int WeaponDelay = 0;
public override object Create(ActorInitializer init) { return new DropPodsPower(init.Self, this); }
public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
{
WeaponInfo weapon;
var weaponToLower = (Weapon ?? string.Empty).ToLowerInvariant();
if (!rules.Weapons.TryGetValue(weaponToLower, out weapon))
throw new YamlException("Weapons Ruleset does not contain an entry '{0}'".F(weaponToLower));
WeaponInfo = weapon;
base.RulesetLoaded(rules, ai);
}
}
public class DropPodsPower : SupportPower
{
readonly DropPodsPowerInfo info;
public DropPodsPower(Actor self, DropPodsPowerInfo info)
: base(self, info)
{
this.info = info;
}
public override void Activate(Actor self, Order order, SupportPowerManager manager)
{
base.Activate(self, order, manager);
SendDropPods(self, order, info.PodFacing);
}
public void SendDropPods(Actor self, Order order, int podFacing)
{
var actorInfo = self.World.Map.Rules.Actors[info.UnitTypes.First().ToLowerInvariant()];
var aircraftInfo = actorInfo.TraitInfo<AircraftInfo>();
var altitude = aircraftInfo.CruiseAltitude.Length;
var approachRotation = WRot.FromFacing(podFacing);
var fallsToEarthInfo = actorInfo.TraitInfo<FallsToEarthInfo>();
var delta = new WVec(0, -altitude * aircraftInfo.Speed / fallsToEarthInfo.Velocity.Length, 0).Rotate(approachRotation);
self.World.AddFrameEndTask(w =>
{
var target = order.Target.CenterPosition;
var targetCell = self.World.Map.CellContaining(target);
var podLocations = self.World.Map.FindTilesInCircle(targetCell, info.PodScatter)
.Where(c => aircraftInfo.LandableTerrainTypes.Contains(w.Map.GetTerrainInfo(c).Type)
&& !self.World.ActorMap.GetActorsAt(c).Any());
if (!podLocations.Any())
return;
if (info.CameraActor != null)
{
var camera = w.CreateActor(info.CameraActor, new TypeDictionary
{
new LocationInit(targetCell),
new OwnerInit(self.Owner),
});
camera.QueueActivity(new Wait(info.CameraRemoveDelay));
camera.QueueActivity(new RemoveSelf());
}
PlayLaunchSounds();
var drops = self.World.SharedRandom.Next(info.Drops.X, info.Drops.Y);
for (var i = 0; i < drops; i++)
{
var unitType = info.UnitTypes.Random(self.World.SharedRandom);
var podLocation = podLocations.Random(self.World.SharedRandom);
var podTarget = Target.FromCell(w, podLocation);
var location = self.World.Map.CenterOfCell(podLocation) - delta + new WVec(0, 0, altitude);
var pod = w.CreateActor(false, unitType, new TypeDictionary
{
new CenterPositionInit(location),
new OwnerInit(self.Owner),
new FacingInit(podFacing)
});
var aircraft = pod.Trait<Aircraft>();
if (!aircraft.CanLand(podLocation))
pod.Dispose();
else
{
w.Add(new DropPodImpact(self.Owner, info.WeaponInfo, w, location, podTarget, info.WeaponDelay,
info.EntryEffect, info.EntryEffectSequence, info.EntryEffectPalette));
w.Add(pod);
}
}
});
}
}
}

View File

@@ -35,6 +35,52 @@ DPOD:
EmptySequence: pip-ammoempty
Palette: pips
DPOD2:
Inherits@2: ^ExistsInWorld
Valued:
Cost: 10
Tooltip:
Name: Drop Pod
Health:
HP: 6000
Armor:
Type: Light
Aircraft:
TurnSpeed: 5
Speed: 300
CruiseAltitude: 16c0
MaximumPitch: 110
LandableTerrainTypes: Clear, Road, Rail, DirtRoad, Rough
HiddenUnderFog:
Type: CenterPosition
BodyOrientation:
UseClassicPerspectiveFudge: False
RenderSprites:
Image: pod
WithFacingSpriteBody:
QuantizeFacingsFromSequence:
HitShape:
Interactable:
WithShadow:
SmokeTrailWhenDamaged:
Sprite: largesmoke
MinDamage: Undamaged
FallsToEarth:
Explosion: DropPodExplode
Moves: true
Velocity: 768
MaximumSpinSpeed: 0
DPOD2E1:
Inherits: DPOD2
SpawnActorOnDeath:
Actor: E1R3
DPOD2E2:
Inherits: DPOD2
SpawnActorOnDeath:
Actor: E2R3
DSHP:
Inherits: ^Aircraft
Inherits@CARGOPIPS: ^CargoPips

View File

@@ -33,6 +33,15 @@ E2:
ProducibleWithLevel:
Prerequisites: barracks.upgraded
E2R3:
Inherits: E2
RenderSprites:
Image: E2
ProducibleWithLevel:
Prerequisites: techlevel.low
InitialLevels: 3
-Buildable:
MEDIC:
Inherits: ^Soldier
Inherits@EXPERIENCE: ^GainsExperience

View File

@@ -481,6 +481,18 @@ GAPLUG:
SelectTargetSpeechNotification: SelectTarget
DisplayRadarPing: True
CameraActor: camera
DropPodsPower:
Cursor: ioncannon
PauseOnCondition: disabled || empdisable
RequiresCondition: plug.droppoda || plug.droppodb
Icon: droppods
Description: Drop Pods
LongDesc: Drop Pod reinforcements.\nSmall team of elite soldiers orbital drops\nto target location.
SelectTargetSpeechNotification: SelectTarget
DisplayRadarPing: true
ChargeInterval: 10000
UnitTypes: DPOD2E1, DPOD2E2
CameraActor: camera
ProduceActorPower:
PauseOnCondition: disabled || empdisable
RequiresCondition: plug.hunterseekera || plug.hunterseekerb
@@ -504,17 +516,23 @@ GAPLUG:
Power@hunterseeker:
RequiresCondition: plug.hunterseekera || plug.hunterseekerb
Amount: -50
Power@droppod:
RequiresCondition: plug.droppoda || plug.droppodb
Amount: -20
Pluggable@pluga:
Offset: 0,2
Conditions:
plug.ioncannon: plug.ioncannona
plug.hunterseeker: plug.hunterseekera
plug.droppod: plug.droppoda
Requirements:
plug.ioncannon: !build-incomplete && !plug.ioncannonb && !plug.ioncannona && !plug.hunterseekera
plug.hunterseeker: !build-incomplete && !plug.hunterseekerb && !plug.ioncannona && !plug.hunterseekera
plug.ioncannon: !build-incomplete && !plug.ioncannonb && !plug.ioncannona && !plug.hunterseekera && !plug.droppoda
plug.hunterseeker: !build-incomplete && !plug.hunterseekerb && !plug.ioncannona && !plug.hunterseekera && !plug.droppoda
plug.droppod: !build-incomplete && !plug.droppodb && !plug.ioncannona && !plug.hunterseekera && !plug.droppoda
EditorOptions:
plug.ioncannon: Ion Cannon
plug.hunterseeker: Hunter Seeker
plug.droppod: Drop Pod Reinforcements
WithIdleOverlay@ioncannona:
RequiresCondition: !build-incomplete && plug.ioncannona
PauseOnCondition: disabled
@@ -523,17 +541,24 @@ GAPLUG:
RequiresCondition: !build-incomplete && plug.hunterseekera
PauseOnCondition: disabled
Sequence: idle-hunterseekera
WithIdleOverlay@droppoda:
RequiresCondition: !build-incomplete && plug.droppoda
PauseOnCondition: disabled
Sequence: idle-droppoda
Pluggable@plugb:
Offset: 1,2
Conditions:
plug.ioncannon: plug.ioncannonb
plug.hunterseeker: plug.hunterseekerb
plug.droppod: plug.droppodb
Requirements:
plug.ioncannon: !build-incomplete && !plug.ioncannona && !plug.ioncannonb && !plug.hunterseekerb
plug.hunterseeker: !build-incomplete && !plug.hunterseekera && !plug.ioncannonb && !plug.hunterseekerb
plug.ioncannon: !build-incomplete && !plug.ioncannona && !plug.ioncannonb && !plug.hunterseekerb && !plug.droppodb
plug.hunterseeker: !build-incomplete && !plug.hunterseekera && !plug.ioncannonb && !plug.hunterseekerb && !plug.droppodb
plug.droppod: !build-incomplete && !plug.droppoda && !plug.ioncannonb && !plug.hunterseekerb && !plug.droppodb
EditorOptions:
plug.ioncannon: Ion Cannon
plug.hunterseeker: Hunter Seeker
plug.droppod: Drop Pod Reinforcements
WithIdleOverlay@ioncannonb:
RequiresCondition: !build-incomplete && plug.ioncannonb
PauseOnCondition: disabled
@@ -542,6 +567,10 @@ GAPLUG:
RequiresCondition: !build-incomplete && plug.hunterseekerb
PauseOnCondition: disabled
Sequence: idle-hunterseekerb
WithIdleOverlay@droppodb:
RequiresCondition: plug.droppodb
PauseOnCondition: disabled
Sequence: idle-droppodb
ProvidesPrerequisite@buildingname:
ProvidesPrerequisite@pluggableion:
RequiresCondition: !plug.ioncannona && !plug.ioncannonb
@@ -595,3 +624,19 @@ GAPLUG3:
Type: plug.ioncannon
Power:
Amount: -100
GAPLUG4:
Inherits: ^BuildingPlug
Valued:
Cost: 1000
Tooltip:
Name: Drop Pod Node
Buildable:
Queue: Building
BuildPaletteOrder: 180
Prerequisites: gaplug, gatech, ~structures.gdi, ~techlevel.superweapons
Description: Enables use of the Drop Pod Reinforcements.
Plug:
Type: plug.droppod
Power:
Amount: -20

View File

@@ -38,6 +38,15 @@ E1:
RevealsShroud:
Range: 5c0
E1R3:
Inherits: E1
RenderSprites:
Image: e1.gdi
ProducibleWithLevel:
Prerequisites: techlevel.low
InitialLevels: 3
-Buildable:
ENGINEER:
Inherits: ^Soldier
Inherits@selection: ^SelectableSupportUnit

View File

@@ -30,3 +30,9 @@ apache:
orcatran:
Inherits: ^VehicleOverlays
icon: crryicon
pod:
Inherits: ^VehicleOverlays
idle:
Facings: 1
Length: 1

View File

@@ -211,6 +211,18 @@ explosion:
small_grey_explosion: xgrysml2
medium_grey_explosion: xgrymed1
large_grey_explosion: xgrymed2
droppod_explosion: droppod
Length: 8
Tick: 360
droppod2_explosion: droppod2
Length: 8
Tick: 360
droppody_explosion: droppody
Length: 8
Tick: 360
droppody2_explosion: droppody2
Length: 8
Tick: 360
discus:
idle:
@@ -440,6 +452,7 @@ icon:
ioncannon: ioncicon
hunterseeker: detnicon
emp: pulsicon
droppods: podsicon
clustermissile:
up: mltimisl-placeholder # TODO: use voxel
@@ -655,3 +668,8 @@ typeglyphs:
Start: 3
structure:
Start: 4
podring:
idle:
Frames: 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
Length: *

View File

@@ -1977,6 +1977,13 @@ gaplug:
Length: 15
Reverses: true
Tick: 120
idle-droppoda: gaplug_d
Length: 15
Tick: 120
Offset: -12, -42, 30
idle-droppodb: gaplug_d
Length: 15
Tick: 120
make: gtplugmk
Length: 17
ShadowStart: 17
@@ -2009,3 +2016,12 @@ gaplug3:
Length: 14
Tick: 120
icon: rad3icon
gaplug4:
place: gtplug_d
Offset: 24,-48, 48
UseTilesetCode: true
Reverses: true
Length: 14
Tick: 120
icon: rad1icon

View File

@@ -66,3 +66,8 @@ Demolish:
Explosions: large_twlt
ExplosionPalette: effect-ignore-lighting-alpha75
ImpactSounds: expnew09.aud
DropPodExplode:
Warhead@1Eff: CreateEffect
Explosions: droppod_explosion, droppod2_explosion, droppody_explosion, droppody2_explosion
ExplosionPalette: effect-ignore-lighting-alpha75