Add Railgun projectile

This commit is contained in:
Forcecore
2017-06-05 20:42:22 -05:00
committed by Paul Chote
parent 9d84f4a845
commit 0573f52a37
4 changed files with 334 additions and 7 deletions

View File

@@ -0,0 +1,87 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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.Drawing;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Projectiles;
namespace OpenRA.Mods.Common.Graphics
{
public struct RailgunHelixRenderable : IRenderable, IFinalizedRenderable
{
readonly WPos pos;
readonly int zOffset;
readonly Railgun railgun;
readonly RailgunInfo info;
readonly WDist helixRadius;
readonly int alpha;
readonly int ticks;
WAngle angle;
public RailgunHelixRenderable(WPos pos, int zOffset, Railgun railgun, RailgunInfo railgunInfo, int ticks)
{
this.pos = pos;
this.zOffset = zOffset;
this.railgun = railgun;
this.info = railgunInfo;
this.ticks = ticks;
helixRadius = info.HelixRadius + new WDist(ticks * info.HelixRadiusDeltaPerTick);
alpha = (railgun.HelixColor.A + ticks * info.HelixAlphaDeltaPerTick).Clamp(0, 255);
angle = new WAngle(ticks * info.HelixAngleDeltaPerTick.Angle);
}
public WPos Pos { get { return pos; } }
public PaletteReference Palette { get { return null; } }
public int ZOffset { get { return zOffset; } }
public bool IsDecoration { get { return true; } }
public IRenderable WithPalette(PaletteReference newPalette) { return new RailgunHelixRenderable(pos, zOffset, railgun, info, ticks); }
public IRenderable WithZOffset(int newOffset) { return new RailgunHelixRenderable(pos, newOffset, railgun, info, ticks); }
public IRenderable OffsetBy(WVec vec) { return new RailgunHelixRenderable(pos + vec, zOffset, railgun, info, ticks); }
public IRenderable AsDecoration() { return this; }
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
public void Render(WorldRenderer wr)
{
if (railgun.ForwardStep == WVec.Zero)
return;
var screenWidth = wr.ScreenVector(new WVec(info.HelixThickness.Length, 0, 0))[0];
// Move forward from self to target to draw helix
var centerPos = this.pos;
var points = new float3[railgun.CycleCount * info.QuantizationCount];
for (var i = points.Length - 1; i >= 0; i--)
{
// Make it narrower near the end.
var rad = i < info.QuantizationCount ? helixRadius / 4 :
i < 2 * info.QuantizationCount ? helixRadius / 2 :
helixRadius;
// Note: WAngle.Sin(x) = 1024 * Math.Sin(2pi/1024 * x)
var u = rad.Length * angle.Cos() * railgun.LeftVector / (1024 * 1024)
+ rad.Length * angle.Sin() * railgun.UpVector / (1024 * 1024);
points[i] = wr.Screen3DPosition(centerPos + u);
centerPos += railgun.ForwardStep;
angle += railgun.AngleStep;
}
Game.Renderer.WorldRgbaColorRenderer.DrawLine(points, screenWidth, Color.FromArgb(alpha, railgun.HelixColor));
}
public void RenderDebugGeometry(WorldRenderer wr) { }
public Rectangle ScreenBounds(WorldRenderer wr) { return Rectangle.Empty; }
}
}

View File

@@ -145,6 +145,7 @@
<Compile Include="Effects\RallyPointIndicator.cs" />
<Compile Include="Effects\RepairIndicator.cs" />
<Compile Include="Effects\SpriteEffect.cs" />
<Compile Include="Graphics\RailgunRenderable.cs" />
<Compile Include="Projectiles\AreaBeam.cs" />
<Compile Include="Projectiles\Bullet.cs" />
<Compile Include="Projectiles\InstantHit.cs" />
@@ -187,6 +188,7 @@
<Compile Include="Lint\LintExts.cs" />
<Compile Include="LoadScreens\ModContentLoadScreen.cs" />
<Compile Include="ActorInitializer.cs" />
<Compile Include="Projectiles\Railgun.cs" />
<Compile Include="Scripting\Properties\AmmoPoolProperties.cs" />
<Compile Include="ShroudExts.cs" />
<Compile Include="Orders\BeaconOrderGenerator.cs" />

View File

@@ -0,0 +1,239 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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.Drawing;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Projectiles
{
[Desc("Laser effect with helix coiling around.")]
public class RailgunInfo : IProjectileInfo
{
[Desc("Damage all units hit by the beam instead of just the target?")]
public readonly bool DamageActorsInLine = false;
[Desc("Maximum offset at the maximum range.")]
public readonly WDist Inaccuracy = WDist.Zero;
[Desc("Can this projectile be blocked when hitting actors with an IBlocksProjectiles trait.")]
public readonly bool Blockable = false;
[Desc("Duration of the beam and helix")]
public readonly int Duration = 15;
[Desc("Equivalent to sequence ZOffset. Controls Z sorting.")]
public readonly int ZOffset = 0;
[Desc("The width of the main trajectory. (\"beam\").")]
public readonly WDist BeamWidth = new WDist(86);
[Desc("The shape of the beam. Accepts values Cylindrical or Flat.")]
public readonly BeamRenderableShape BeamShape = BeamRenderableShape.Cylindrical;
[Desc("Beam color in (A),R,G,B.")]
public readonly Color BeamColor = Color.FromArgb(128, 255, 255, 255);
[Desc("When true, this will override BeamColor parameter and draw the laser with player color."
+ " (Still uses BeamColor's alpha information)")]
public readonly bool BeamPlayerColor = false;
[Desc("Beam alpha gets + this value per tick during drawing; hence negative value makes it fade over time.")]
public readonly int BeamAlphaDeltaPerTick = -8;
[Desc("Thickness of the helix")]
public readonly WDist HelixThickness = new WDist(32);
[Desc("The radius of the spiral effect. (WDist)")]
public readonly WDist HelixRadius = new WDist(64);
[Desc("Height of one complete helix turn, measured parallel to the axis of the helix (WDist)")]
public readonly WDist HelixPitch = new WDist(512);
[Desc("Helix radius gets + this value per tick during drawing")]
public readonly int HelixRadiusDeltaPerTick = 8;
[Desc("Helix alpha gets + this value per tick during drawing; hence negative value makes it fade over time.")]
public readonly int HelixAlphaDeltaPerTick = -8;
[Desc("Helix spins by this much over time each tick.")]
public readonly WAngle HelixAngleDeltaPerTick = new WAngle(16);
[Desc("Draw each cycle of helix with this many quantization steps")]
public readonly int QuantizationCount = 16;
[Desc("Helix color in (A),R,G,B.")]
public readonly Color HelixColor = Color.FromArgb(128, 255, 255, 255);
[Desc("Draw helix in PlayerColor? Overrides RGB part of the HelixColor. (Still uses HelixColor's alpha information)")]
public readonly bool HelixPlayerColor = false;
[Desc("Impact animation.")]
public readonly string HitAnim = null;
[Desc("Sequence of impact animation to use.")]
[SequenceReference("HitAnim")]
public readonly string HitAnimSequence = "idle";
[PaletteReference]
public readonly string HitAnimPalette = "effect";
[Desc("Scan radius for actors damaged by beam. If set to zero (default), it will automatically scale to the largest health shape.",
"Only set custom values if you know what you're doing.")]
public WDist AreaVictimScanRadius = WDist.Zero;
[Desc("Scan radius for actors with projectile-blocking trait. If set to zero (default), it will automatically scale",
"to the blocker with the largest health shape. Only set custom values if you know what you're doing.")]
public WDist BlockerScanRadius = WDist.Zero;
public IProjectile Create(ProjectileArgs args)
{
var bc = BeamPlayerColor ? Color.FromArgb(BeamColor.A, args.SourceActor.Owner.Color.RGB) : BeamColor;
var hc = HelixPlayerColor ? Color.FromArgb(HelixColor.A, args.SourceActor.Owner.Color.RGB) : HelixColor;
return new Railgun(args, this, bc, hc);
}
public void RulesetLoaded(Ruleset rules, WeaponInfo wi)
{
if (BlockerScanRadius == WDist.Zero)
BlockerScanRadius = Util.MinimumRequiredBlockerScanRadius(rules);
if (AreaVictimScanRadius == WDist.Zero)
AreaVictimScanRadius = Util.MinimumRequiredVictimScanRadius(rules);
}
}
public class Railgun : IProjectile
{
readonly ProjectileArgs args;
readonly RailgunInfo info;
readonly Animation hitanim;
public readonly Color BeamColor;
public readonly Color HelixColor;
int ticks = 0;
bool animationComplete;
WPos target;
// Computing these in Railgun instead of RailgunRenderable saves Info.Duration ticks of computation.
// Fortunately, railguns don't track the target.
public int CycleCount { get; private set; }
public WVec SourceToTarget { get; private set; }
public WVec ForwardStep { get; private set; }
public WVec LeftVector { get; private set; }
public WVec UpVector { get; private set; }
public WAngle AngleStep { get; private set; }
public Railgun(ProjectileArgs args, RailgunInfo info, Color beamColor, Color helixColor)
{
this.args = args;
this.info = info;
target = args.PassiveTarget;
this.BeamColor = beamColor;
this.HelixColor = helixColor;
if (!string.IsNullOrEmpty(info.HitAnim))
hitanim = new Animation(args.SourceActor.World, info.HitAnim);
CalculateVectors();
}
void CalculateVectors()
{
// Check for blocking actors
WPos blockedPos;
if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(args.SourceActor.World, target, args.Source,
info.BeamWidth, info.BlockerScanRadius, out blockedPos))
target = blockedPos;
// Note: WAngle.Sin(x) = 1024 * Math.Sin(2pi/1024 * x)
AngleStep = new WAngle(1024 / info.QuantizationCount);
SourceToTarget = target - args.Source;
// Forward step, pointing from src to target.
// QuantizationCont * forwardStep == One cycle of beam in src2target direction.
ForwardStep = (info.HelixPitch.Length * SourceToTarget) / (info.QuantizationCount * SourceToTarget.Length);
// An easy vector to find which is perpendicular vector to forwardStep, with 0 Z component
LeftVector = new WVec(ForwardStep.Y, -ForwardStep.X, 0);
LeftVector = 1024 * LeftVector / LeftVector.Length;
// Vector that is pointing upwards from the ground
UpVector = new WVec(
-ForwardStep.X * ForwardStep.Z,
-ForwardStep.Z * ForwardStep.Y,
ForwardStep.X * ForwardStep.X + ForwardStep.Y * ForwardStep.Y);
UpVector = 1024 * UpVector / UpVector.Length;
//// LeftVector and UpVector are unit vectors of size 1024.
CycleCount = SourceToTarget.Length / info.HelixPitch.Length;
if (SourceToTarget.Length % info.HelixPitch.Length != 0)
CycleCount += 1; // math.ceil, int version.
// Using ForwardStep * CycleCount, the helix and the main beam gets "out of sync"
// if drawn from source to target. Instead, the main beam is drawn from source to end point of helix.
// Trade-off between computation vs Railgun weapon range.
// Modders must not have too large range for railgun weapons.
SourceToTarget = info.QuantizationCount * CycleCount * ForwardStep;
}
public void Tick(World world)
{
if (ticks == 0)
{
if (hitanim != null)
hitanim.PlayThen(info.HitAnimSequence, () => animationComplete = true);
else
animationComplete = true;
if (!info.DamageActorsInLine)
args.Weapon.Impact(Target.FromPos(target), args.SourceActor, args.DamageModifiers);
else
{
var actors = world.FindActorsOnLine(args.Source, target, info.BeamWidth, info.AreaVictimScanRadius);
foreach (var a in actors)
args.Weapon.Impact(Target.FromActor(a), args.SourceActor, args.DamageModifiers);
}
}
if (hitanim != null)
hitanim.Tick();
if (ticks++ > info.Duration && animationComplete)
world.AddFrameEndTask(w => w.Remove(this));
}
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
if (wr.World.FogObscures(target) &&
wr.World.FogObscures(args.Source))
yield break;
if (ticks < info.Duration)
{
yield return new RailgunHelixRenderable(args.Source, info.ZOffset, this, info, ticks);
yield return new BeamRenderable(args.Source, info.ZOffset, SourceToTarget, info.BeamShape, info.BeamWidth,
Color.FromArgb(BeamColor.A + info.BeamAlphaDeltaPerTick * ticks, BeamColor));
}
if (hitanim != null)
foreach (var r in hitanim.Render(target, wr.Palette(info.HitAnimPalette)))
yield return r;
}
}
}

View File

@@ -2,14 +2,13 @@
ReloadDelay: 60
Range: 6c0
Report: bigggun1.aud
Projectile: AreaBeam
Projectile: Railgun
Speed: 20c0
Duration: 3
DamageInterval: 2
Duration: 15
Width: 80
BeyondTargetRange: 0c64
Blockable: true
Color: 0080FFC8
DamageActorsInLine: true
BeamColor: 0080FFC8
Warhead@1Dam: SpreadDamage
Range: 0, 32
Falloff: 100, 100
@@ -48,8 +47,8 @@ MechRailgun:
Burst: 2 # for alternating muzzle offsets, dmg/s identical to original
BurstDelay: 60
Report: railuse5.aud
Projectile: AreaBeam
Color: 00FFFFC8
Projectile: Railgun
BeamColor: 00FFFFC8
Warhead@1Dam: SpreadDamage
Damage: 200
Versus: