Introduce world offsets for turrets & armaments.

This commit is contained in:
Paul Chote
2013-03-25 20:33:32 +13:00
parent 27d852a425
commit bb6a1ed6e0
4 changed files with 163 additions and 38 deletions

View File

@@ -18,11 +18,18 @@ using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
public enum CoordinateModel { Legacy, World };
public class Barrel
{
// Legacy coordinates
public PVecInt TurretSpaceOffset; // position in turret space
public PVecInt ScreenSpaceOffset; // screen-space hack to make things line up good.
public int Facing; // deviation from turret facing
// World coordinates
public WVec Offset;
public WAngle Yaw;
}
[Desc("Allows you to attach weapons to the unit (use @IdentifierSuffix for > 1)")]
@@ -40,6 +47,16 @@ namespace OpenRA.Mods.RA
public readonly float LegacyRecoilRecovery = 0.2f;
public readonly int[] LegacyLocalOffset = { };
public readonly CoordinateModel OffsetModel = CoordinateModel.Legacy;
[Desc("Muzzle position relative to turret or body. (forward, right, up) triples")]
public readonly WRange[] LocalOffset = {};
[Desc("Muzzle yaw relative to turret or body.")]
public readonly WAngle[] LocalYaw = {};
[Desc("Move the turret backwards when firing.")]
public readonly WRange Recoil = WRange.Zero;
[Desc("Recoil recovery per-frame")]
public readonly WRange RecoilRecovery = new WRange(9);
public object Create(ActorInitializer init) { return new Armament(init.self, this); }
}
@@ -49,8 +66,10 @@ namespace OpenRA.Mods.RA
public readonly WeaponInfo Weapon;
public readonly Barrel[] Barrels;
Lazy<Turreted> Turret;
Lazy<ILocalCoordinatesModel> Coords;
public float Recoil { get; private set; }
public WRange Recoil;
public float LegacyRecoil { get; private set; }
public int FireDelay { get; private set; }
public int Burst { get; private set; }
@@ -58,26 +77,45 @@ namespace OpenRA.Mods.RA
{
Info = info;
// We can't soft-depend on TraitInfo, so we have to wait
// until runtime to cache this
Turret = Lazy.New(() => self.TraitsImplementing<Turreted>().FirstOrDefault(t => t.info.Turret == info.Turret));
// We can't resolve these until runtime
Turret = Lazy.New(() => self.TraitsImplementing<Turreted>().FirstOrDefault(t => t.Name == info.Turret));
Coords = Lazy.New(() => self.Trait<ILocalCoordinatesModel>());
Weapon = Rules.Weapons[info.Weapon.ToLowerInvariant()];
Burst = Weapon.Burst;
var barrels = new List<Barrel>();
for (var i = 0; i < info.LegacyLocalOffset.Length / 5; i++)
barrels.Add(new Barrel
if (Info.OffsetModel == CoordinateModel.Legacy)
{
for (var i = 0; i < info.LocalOffset.Length / 5; i++)
barrels.Add(new Barrel
{
TurretSpaceOffset = new PVecInt(info.LegacyLocalOffset[5 * i], info.LegacyLocalOffset[5 * i + 1]),
ScreenSpaceOffset = new PVecInt(info.LegacyLocalOffset[5 * i + 2], info.LegacyLocalOffset[5 * i + 3]),
Facing = info.LegacyLocalOffset[5 * i + 4],
});
// if no barrels specified, the default is "turret position; turret facing".
if (barrels.Count == 0)
barrels.Add(new Barrel { TurretSpaceOffset = PVecInt.Zero, ScreenSpaceOffset = PVecInt.Zero, Facing = 0 });
}
else
{
if (info.LocalOffset.Length % 3 != 0)
throw new InvalidOperationException("Invalid LocalOffset array length");
for (var i = 0; i < info.LocalOffset.Length / 3; i++)
{
TurretSpaceOffset = new PVecInt(info.LegacyLocalOffset[5 * i], info.LegacyLocalOffset[5 * i + 1]),
ScreenSpaceOffset = new PVecInt(info.LegacyLocalOffset[5 * i + 2], info.LegacyLocalOffset[5 * i + 3]),
Facing = info.LegacyLocalOffset[5 * i + 4],
});
// if no barrels specified, the default is "turret position; turret facing".
if (barrels.Count == 0)
barrels.Add(new Barrel { TurretSpaceOffset = PVecInt.Zero, ScreenSpaceOffset = PVecInt.Zero, Facing = 0 });
barrels.Add(new Barrel
{
Offset = new WVec(info.LocalOffset[3*i], info.LocalOffset[3*i + 1], info.LocalOffset[3*i + 2]),
Yaw = info.LocalYaw.Length > i ? info.LocalYaw[i] : WAngle.Zero
});
}
if (barrels.Count == 0)
barrels.Add(new Barrel { Offset = WVec.Zero, Yaw = WAngle.Zero });
}
Barrels = barrels.ToArray();
}
@@ -85,9 +123,12 @@ namespace OpenRA.Mods.RA
{
if (FireDelay > 0)
--FireDelay;
Recoil = Math.Max(0f, Recoil - Info.LegacyRecoilRecovery);
LegacyRecoil = Math.Max(0f, LegacyRecoil - Info.LegacyRecoilRecovery);
Recoil = new WRange(Math.Max(0, Recoil.Range - Info.RecoilRecovery.Range));
}
// Note: facing is only used by the legacy positioning code
// The world coordinate model uses Actor.Orientation
public void CheckFire(Actor self, AttackBase attack, IMove move, IFacing facing, Target target)
{
if (FireDelay > 0) return;
@@ -103,20 +144,32 @@ namespace OpenRA.Mods.RA
var barrel = Barrels[Burst % Barrels.Length];
var destMove = target.IsActor ? target.Actor.TraitOrDefault<IMove>() : null;
var legacyMuzzlePosition = self.CenterLocation + (PVecInt)MuzzlePxOffset(self, facing, barrel).ToInt2();
var legacyMuzzleAltitude = move != null ? move.Altitude : 0;
var legacyFacing = barrel.Facing + (Turret.Value != null ? Turret.Value.turretFacing :
facing != null ? facing.Facing : Util.GetFacing(target.CenterLocation - self.CenterLocation, 0));
if (Info.OffsetModel == CoordinateModel.World)
{
var muzzlePosition = self.CenterPosition + MuzzleOffset(self, barrel);
legacyMuzzlePosition = PPos.FromWPos(muzzlePosition);
legacyMuzzleAltitude = Game.CellSize*muzzlePosition.Z/1024;
legacyFacing = MuzzleOrientation(self, barrel).Yaw.Angle / 4;
}
var args = new ProjectileArgs
{
weapon = Weapon,
firedBy = self,
target = target,
src = legacyMuzzlePosition,
srcAltitude = legacyMuzzleAltitude,
src = (self.CenterLocation + (PVecInt)MuzzlePxPosition(self, facing, barrel).ToInt2()),
srcAltitude = move != null ? move.Altitude : 0,
dest = target.CenterLocation,
destAltitude = destMove != null ? destMove.Altitude : 0,
facing = barrel.Facing +
(Turret.Value != null ? Turret.Value.turretFacing :
facing != null ? facing.Facing : Util.GetFacing(target.CenterLocation - self.CenterLocation, 0)),
facing = legacyFacing,
firepowerModifier = self.TraitsImplementing<IFirepowerModifier>()
.Select(a => a.GetFirepowerModifier())
@@ -139,7 +192,8 @@ namespace OpenRA.Mods.RA
foreach (var na in self.TraitsImplementing<INotifyAttack>())
na.Attacking(self, target);
Recoil = Info.LegacyRecoil;
LegacyRecoil = Info.LegacyRecoil;
Recoil = Info.Recoil;
if (--Burst > 0)
FireDelay = Weapon.BurstDelay;
@@ -169,8 +223,13 @@ namespace OpenRA.Mods.RA
return (PVecFloat)Util.RotateVectorByFacing(b.TurretSpaceOffset.ToFloat2(), turretFacing, .7f);
}
public PVecFloat MuzzlePxPosition(Actor self, IFacing facing, Barrel b)
// Note: facing is only used by the legacy positioning code
public PVecFloat MuzzlePxOffset(Actor self, IFacing facing, Barrel b)
{
// Hack for external code unaware of world coordinates
if (Info.OffsetModel == CoordinateModel.World)
return (PVecFloat)PPos.FromWPosHackZ(WPos.Zero + MuzzleOffset(self, b)).ToFloat2();
PVecFloat pos = b.ScreenSpaceOffset;
// local facing offset doesn't make sense for actors that don't rotate
@@ -192,10 +251,29 @@ namespace OpenRA.Mods.RA
return pos;
}
public PVecFloat RecoilPxOffset(Actor self, int facing)
public WVec MuzzleOffset(Actor self, Barrel b)
{
var localRecoil = new float2(0, Recoil);
return (PVecFloat)Util.RotateVectorByFacing(localRecoil, facing, .7f);
if (Info.OffsetModel != CoordinateModel.World)
throw new InvalidOperationException("Armament.MuzzlePosition requires a world coordinate offset");
var bodyOrientation = Coords.Value.QuantizeOrientation(self, self.Orientation);
var localOffset = b.Offset + new WVec(-Recoil, WRange.Zero, WRange.Zero);
if (Turret.Value != null)
{
var turretOrientation = Coords.Value.QuantizeOrientation(self, Turret.Value.LocalOrientation(self));
localOffset = localOffset.Rotate(turretOrientation);
localOffset += Turret.Value.Offset;
}
return Coords.Value.LocalToWorld(localOffset.Rotate(bodyOrientation));
}
public WRot MuzzleOrientation(Actor self, Barrel b)
{
var orientation = self.Orientation + WRot.FromYaw(b.Yaw);
if (Turret.Value != null)
orientation += Turret.Value.LocalOrientation(self);
return orientation;
}
}
}