Merge pull request #4036 from pchote/aircraft-polish

C&C aircraft polish.
This commit is contained in:
Matthias Mailänder
2013-11-01 14:17:23 -07:00
19 changed files with 231 additions and 126 deletions

View File

@@ -172,6 +172,9 @@ namespace OpenRA.GameRules
if (target.Type == TargetType.Terrain)
{
var cell = target.CenterPosition.ToCPos();
if (!world.Map.IsInMap(cell))
return false;
if (ValidTargets.Contains("Ground") && world.GetTerrainType(cell) != "Water")
return true;

View File

@@ -124,6 +124,15 @@ namespace OpenRA
r.Next(w.Map.Bounds.Top, w.Map.Bounds.Bottom));
}
public static WRange DistanceToMapEdge(this World w, WPos pos, WVec dir)
{
var tl = w.Map.Bounds.TopLeftAsCPos().TopLeft;
var br = w.Map.Bounds.BottomRightAsCPos().BottomRight;
var x = dir.X == 0 ? int.MaxValue : ((dir.X < 0 ? tl.X : br.X) - pos.X) / dir.X;
var y = dir.Y == 0 ? int.MaxValue : ((dir.Y < 0 ? tl.Y : br.Y) - pos.Y) / dir.Y;
return new WRange(Math.Min(x, y) * dir.Length);
}
public static bool HasVoices(this Actor a)
{
var selectable = a.Info.Traits.GetOrDefault<SelectableInfo>();

View File

@@ -57,13 +57,17 @@ namespace OpenRA.Mods.RA.Air
if (init.Contains<LocationInit>())
SetPosition(self, init.Get<LocationInit, CPos>());
this.Facing = init.Contains<FacingInit>() ? init.Get<FacingInit, int>() : info.InitialFacing;
if (init.Contains<AltitudeInit>())
{
var z = init.Get<AltitudeInit, int>() * 1024 / Game.CellSize;
SetPosition(self, CenterPosition + new WVec(0, 0, z - CenterPosition.Z));
}
if (init.Contains<CenterPositionInit>())
SetPosition(self, init.Get<CenterPositionInit, WPos>());
this.Facing = init.Contains<FacingInit>() ? init.Get<FacingInit, int>() : info.InitialFacing;
}
public Actor GetActorBelow()

View File

@@ -101,7 +101,8 @@ namespace OpenRA.Mods.RA
// The world coordinate model uses Actor.Orientation
public void CheckFire(Actor self, AttackBase attack, IFacing facing, Target target)
{
if (FireDelay > 0) return;
if (FireDelay > 0)
return;
var limitedAmmo = self.TraitOrDefault<LimitedAmmo>();
if (limitedAmmo != null && !limitedAmmo.HasAmmo())
@@ -113,7 +114,7 @@ namespace OpenRA.Mods.RA
if (!target.IsInRange(self.CenterPosition, range))
return;
if (target.IsInRange(self.CenterPosition, minRange))
if (minRange != WRange.Zero && target.IsInRange(self.CenterPosition, minRange))
return;
if (!Weapon.IsValidAgainst(target, self.World))

View File

@@ -0,0 +1,85 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.GameRules;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
class AttackBomberInfo : AttackBaseInfo
{
[Desc("Armament name")]
public readonly string Bombs = "primary";
[Desc("Armament name")]
public readonly string Guns = "secondary";
public readonly int FacingTolerance = 2;
public override object Create(ActorInitializer init) { return new AttackBomber(init.self, this); }
}
class AttackBomber : AttackBase, ISync
{
AttackBomberInfo info;
[Sync] Target target;
public AttackBomber(Actor self, AttackBomberInfo info)
: base(self)
{
this.info = info;
}
public override void Tick(Actor self)
{
base.Tick(self);
var facing = self.TraitOrDefault<IFacing>();
var cp = self.CenterPosition;
var bombTarget = Target.FromPos(cp - new WVec(0, 0, cp.Z));
// Bombs drop anywhere in range
foreach (var a in Armaments.Where(a => a.Info.Name == info.Bombs))
{
var range = new WRange((int)(1024 * a.Weapon.Range));
if (!target.IsInRange(self.CenterPosition, range))
continue;
a.CheckFire(self, this, facing, bombTarget);
}
// Guns only fire when approaching the target
var facingToTarget = Util.GetFacing(target.CenterPosition - self.CenterPosition, facing.Facing);
if (Math.Abs(facingToTarget - facing.Facing) % 256 > info.FacingTolerance)
return;
foreach (var a in Armaments.Where(a => a.Info.Name == info.Guns))
{
var range = new WRange((int)(1024 * a.Weapon.Range));
if (!target.IsInRange(self.CenterPosition, range))
continue;
var t = Target.FromPos(cp - new WVec(0, range.Range / 2, cp.Z).Rotate(WRot.FromFacing(facing.Facing)));
a.CheckFire(self, this, facing, t);
}
}
public void SetTarget(WPos pos) { target = Target.FromPos(pos); }
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
{
// TODO: Player controlled units want this too!
throw new NotImplementedException("CarpetBomb requires a scripted target");
}
}
}

View File

@@ -1,77 +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 System.Linq;
using OpenRA.GameRules;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
class CarpetBombInfo : ITraitInfo
{
[WeaponReference]
public readonly string Weapon = null;
public readonly int Range = 3;
public object Create(ActorInitializer init) { return new CarpetBomb(this); }
}
// TODO: maybe integrate this better with the normal weapons system?
class CarpetBomb : ITick, ISync
{
CarpetBombInfo info;
Target target;
[Sync] int dropDelay;
[Sync] WRange range;
public CarpetBomb(CarpetBombInfo info)
{
this.info = info;
// TODO: Push this conversion into the yaml
range = WRange.FromCells(info.Range);
}
public void SetTarget(CPos targetCell) { target = Target.FromCell(targetCell); }
public void Tick(Actor self)
{
if (!target.IsInRange(self.CenterPosition, range))
return;
var limitedAmmo = self.TraitOrDefault<LimitedAmmo>();
if (limitedAmmo != null && !limitedAmmo.HasAmmo())
return;
if (--dropDelay <= 0)
{
var weapon = Rules.Weapons[info.Weapon.ToLowerInvariant()];
dropDelay = weapon.ROF;
var pos = self.CenterPosition;
var args = new ProjectileArgs
{
Weapon = weapon,
Facing = self.Trait<IFacing>().Facing,
Source = pos,
SourceActor = self,
PassiveTarget = pos - new WVec(0, 0, pos.Z)
};
self.World.Add(args.Weapon.Projectile.Create(args));
if (args.Weapon.Report != null && args.Weapon.Report.Any())
Sound.Play(args.Weapon.Report.Random(self.World.SharedRandom), self.CenterPosition);
}
}
}
}

View File

@@ -54,8 +54,9 @@ namespace OpenRA.Mods.RA.Effects
if (pos.Z <= args.PassiveTarget.Z)
{
pos += new WVec(0, 0, args.PassiveTarget.Z - pos.Z);
world.AddFrameEndTask(w => w.Remove(this));
Combat.DoImpacts(args.PassiveTarget, args.SourceActor, args.Weapon, args.FirepowerModifier);
Combat.DoImpacts(pos, args.SourceActor, args.Weapon, args.FirepowerModifier);
}
anim.Tick();

View File

@@ -98,7 +98,7 @@ namespace OpenRA.Mods.RA.Missions
new FacingInit(Util.GetFacing(location - entry, 0)),
new AltitudeInit(Rules.Info["badr.bomber"].Traits.Get<PlaneInfo>().CruiseAltitude),
});
badger.Trait<CarpetBomb>().SetTarget(location);
badger.Trait<AttackBomber>().SetTarget(location.CenterPosition);
badger.QueueActivity(Fly.ToCell(location));
badger.QueueActivity(new FlyOffMap());
badger.QueueActivity(new RemoveSelf());

View File

@@ -179,7 +179,6 @@
<Compile Include="Capturable.cs" />
<Compile Include="ExternalCaptures.cs" />
<Compile Include="Cargo.cs" />
<Compile Include="CarpetBomb.cs" />
<Compile Include="CashTrickler.cs" />
<Compile Include="ChronoshiftDeploy.cs" />
<Compile Include="ChronoshiftPaletteEffect.cs" />
@@ -472,6 +471,7 @@
<Compile Include="World\PathfinderDebugOverlay.cs" />
<Compile Include="Effects\ContrailFader.cs" />
<Compile Include="Widgets\Logic\SettingsLogic.cs" />
<Compile Include="AttackBomber.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2013 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,
@@ -8,6 +8,7 @@
*/
#endregion
using System;
using OpenRA.FileFormats;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Air;
@@ -19,59 +20,81 @@ namespace OpenRA.Mods.RA
{
[ActorReference]
public readonly string UnitType = "badr.bomber";
public readonly int SquadSize = 1;
public readonly WVec SquadOffset = new WVec(-1536, 1536, 0);
public readonly int QuantizedFacings = 32;
public readonly WRange Cordon = new WRange(5120);
[ActorReference]
public readonly string FlareType = null;
public readonly int FlareTime = 25 * 60 * 2; // 2 minutes
public readonly int FlareTime = 3000; // 2 minutes
public override object Create(ActorInitializer init) { return new AirstrikePower(init.self, this); }
}
class AirstrikePower : SupportPower
{
public AirstrikePower(Actor self, AirstrikePowerInfo info) : base(self, info) { }
public AirstrikePower(Actor self, AirstrikePowerInfo info)
: base(self, info) { }
public override void Activate(Actor self, Order order)
{
var startPos = self.World.ChooseRandomEdgeCell();
var info = Info as AirstrikePowerInfo;
var attackFacing = Util.QuantizeFacing(self.World.SharedRandom.Next(256), info.QuantizedFacings) * (256 / info.QuantizedFacings);
var attackRotation = WRot.FromFacing(attackFacing);
var delta = new WVec(0, -1024, 0).Rotate(attackRotation);
var altitude = Rules.Info[info.UnitType].Traits.Get<PlaneInfo>().CruiseAltitude * 1024 / Game.CellSize;
var target = order.TargetLocation.CenterPosition + new WVec(0, 0, altitude);
var startEdge = target - (self.World.DistanceToMapEdge(target, -delta) + info.Cordon).Range * delta / 1024;
var finishEdge = target + (self.World.DistanceToMapEdge(target, delta) + info.Cordon).Range * delta / 1024;
self.World.AddFrameEndTask(w =>
{
var info = (Info as AirstrikePowerInfo);
var notification = self.Owner.IsAlliedWith(self.World.RenderPlayer) ? Info.LaunchSound : Info.IncomingSound;
Sound.Play(notification);
if (self.Owner.IsAlliedWith(self.World.RenderPlayer))
Sound.Play(Info.LaunchSound);
else
Sound.Play(Info.IncomingSound);
var flare = info.FlareType != null ? w.CreateActor(info.FlareType, new TypeDictionary
Actor flare = null;
if (info.FlareType != null)
{
new LocationInit( order.TargetLocation ),
new OwnerInit( self.Owner ),
}) : null;
flare = w.CreateActor(info.FlareType, new TypeDictionary
{
new LocationInit(order.TargetLocation),
new OwnerInit(self.Owner),
});
if (flare != null)
{
flare.QueueActivity(new Wait(info.FlareTime));
flare.QueueActivity(new RemoveSelf());
}
var a = w.CreateActor(info.UnitType, new TypeDictionary
for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++)
{
new LocationInit( startPos ),
new OwnerInit( self.Owner ),
new FacingInit( Util.GetFacing(order.TargetLocation - startPos, 0) ),
new AltitudeInit( Rules.Info[info.UnitType].Traits.Get<PlaneInfo>().CruiseAltitude ),
});
a.Trait<CarpetBomb>().SetTarget(order.TargetLocation);
// Even-sized squads skip the lead plane
if (i == 0 && (info.SquadSize & 1) == 0)
continue;
a.CancelActivity();
a.QueueActivity(Fly.ToCell(order.TargetLocation));
// Includes the 90 degree rotation between body and world coordinates
var so = info.SquadOffset;
var spawnOffset = new WVec(i*so.Y, -Math.Abs(i)*so.X, 0).Rotate(attackRotation);
var targetOffset = new WVec(i*so.Y, 0, 0).Rotate(attackRotation);
if (flare != null)
a.QueueActivity(new CallFunc(() => flare.Destroy()));
var a = w.CreateActor(info.UnitType, new TypeDictionary
{
new CenterPositionInit(startEdge + spawnOffset),
new OwnerInit(self.Owner),
new FacingInit(attackFacing),
});
a.QueueActivity(new FlyOffMap());
a.QueueActivity(new RemoveSelf());
a.Trait<AttackBomber>().SetTarget(target + targetOffset);
if (flare != null)
a.QueueActivity(new CallFunc(() => flare.Destroy()));
a.QueueActivity(Fly.ToPos(finishEdge + spawnOffset));
a.QueueActivity(new RemoveSelf());
}
});
}
}

View File

@@ -166,6 +166,18 @@ C17:
-GainsExperience:
FlyAwayOnIdle:
RejectsOrders:
Contrail@1:
Offset: -261,-650,0
TrailLength: 15
Contrail@2:
Offset: -85,-384,0
TrailLength: 16
Contrail@3:
Offset: -85,384,0
TrailLength: 16
Contrail@4:
Offset: -261,650,0
TrailLength: 15
A10:
Inherits: ^Plane
@@ -185,15 +197,29 @@ A10:
Range: 12
RenderUnit:
WithShadow:
LimitedAmmo:
Ammo: 10
CarpetBomb:
AttackBomber:
Guns: gun
Bombs: bombs
Armament@GUNS:
Name: gun
Weapon: Vulcan
LocalOffset: 1024,0,-85
WithMuzzleFlash@SECONDARY:
Armament: gun
Armament@BOMBS:
Name: bombs
Weapon: Napalm
Range: 3
LocalOffset: 0,-256,-43, 0,256,-43
-Selectable:
-GainsExperience:
FlyAwayOnIdle:
RejectsOrders:
Contrail@1:
Offset: -640,171,0
TrailLength: 15
Contrail@2:
Offset: -640,-171,0
TrailLength: 15
TRAN.Husk:
Inherits: ^HelicopterHusk

View File

@@ -375,6 +375,8 @@ HQ:
AirstrikePower:
Icon: airstrike
ChargeTime: 180
SquadSize: 3
QuantizedFacings: 8
Description: Air Strike
LongDesc: Deploy an aerial napalm strike.\nBurns buildings and infantry along a line.
EndChargeSound: airredy1.aud

View File

@@ -60,5 +60,9 @@ a10:
idle:
Start: 0
Facings: 32
muzzle: minigun
Start: 0
Length: 6
Facings: 8
icon: a10icnh
Start: 0

View File

@@ -725,8 +725,30 @@ TowerMissle:
SmudgeType: Crater
Damage: 45
Napalm:
Vulcan:
ValidTargets: Ground, Water
ROF: 2
Range: 6
Report: gun5.aud
Projectile: Bullet
Speed: 100
Warhead:
Damage: 75
Spread: 10
Versus:
None: 100%
Wood: 50%
Light: 100%
Heavy: 25%
InfDeath: 2
Explosion: piffs
Napalm:
ValidTargets: Ground, Water
ROF: 4
Range: 2
Burst: 2
BurstDelay: 2
Projectile: GravityBomb
Image: BOMBLET
Warhead:
@@ -738,9 +760,10 @@ Napalm:
Heavy: 55%
InfDeath: 5
Explosion: med_napalm
WaterExplosion: med_napalm
ImpactSound: flamer2.aud
SmudgeType: Scorch
Damage: 130
Damage: 50
Napalm.Crate:
Warhead:

View File

@@ -94,8 +94,8 @@ ORNI:
HuskActor: ORNI.Husk
ORNI.bomber:
CarpetBomb:
Range: 3
AttackBomber:
Armament:
Weapon: Napalm
Inherits: ^Plane
Health:

View File

@@ -482,6 +482,7 @@ ParaBomb:
Napalm:
ROF: 2
Range: 3
Projectile: GravityBomb
Image: BOMBS
Warhead:

View File

@@ -1002,8 +1002,8 @@ Rules:
Armor:
Type: Concrete
BADR.Bomber:
CarpetBomb:
Range: 3
AttackBomber:
Armament:
Weapon: ParaBomb
Inherits: ^Plane
Health:

View File

@@ -36,8 +36,8 @@ BADR:
RejectsOrders:
BADR.Bomber:
CarpetBomb:
Range: 3
AttackBomber:
Armament:
Weapon: ParaBomb
Inherits: ^Plane
Health:

View File

@@ -898,7 +898,7 @@ DepthCharge:
ParaBomb:
ROF: 10
Range: 4.5
Range: 3
Report: CHUTE1.AUD
Projectile: GravityBomb
Image: PARABOMB