Merge pull request #9357 from UberWaffe/LineImpactProjectile
Added a LineImpactProjectile (Attempt 2)
This commit is contained in:
@@ -24,6 +24,7 @@ namespace OpenRA.GameRules
|
||||
public int[] RangeModifiers;
|
||||
public int Facing;
|
||||
public WPos Source;
|
||||
public Func<WPos> CurrentSource;
|
||||
public Actor SourceActor;
|
||||
public WPos PassiveTarget;
|
||||
public Target GuidedTarget;
|
||||
|
||||
@@ -40,6 +40,20 @@ namespace OpenRA
|
||||
/// </summary>
|
||||
public static WPos Lerp(WPos a, WPos b, int mul, int div) { return a + (b - a) * mul / div; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the linear interpolation between points 'a' and 'b'
|
||||
/// </summary>
|
||||
public static WPos Lerp(WPos a, WPos b, long mul, long div)
|
||||
{
|
||||
// The intermediate variables may need more precision than
|
||||
// an int can provide, so we can't use WPos.
|
||||
var x = (int)(a.X + (b.X - a.X) * mul / div);
|
||||
var y = (int)(a.Y + (b.Y - a.Y) * mul / div);
|
||||
var z = (int)(a.Z + (b.Z - a.Z) * mul / div);
|
||||
|
||||
return new WPos(x, y, z);
|
||||
}
|
||||
|
||||
public static WPos LerpQuadratic(WPos a, WPos b, WAngle pitch, int mul, int div)
|
||||
{
|
||||
// Start with a linear lerp between the points
|
||||
|
||||
217
OpenRA.Mods.Common/Effects/AreaBeam.cs
Normal file
217
OpenRA.Mods.Common/Effects/AreaBeam.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 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.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Graphics;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Effects
|
||||
{
|
||||
public class AreaBeamInfo : IProjectileInfo
|
||||
{
|
||||
[Desc("Projectile speed in WDist / tick, two values indicate a randomly picked velocity per beam.")]
|
||||
public readonly WDist[] Speed = { new WDist(128) };
|
||||
|
||||
[Desc("The maximum duration (in ticks) of each beam burst.")]
|
||||
public readonly int Duration = 10;
|
||||
|
||||
[Desc("The number of ticks between the beam causing warhead impacts in its area of effect.")]
|
||||
public readonly int DamageInterval = 3;
|
||||
|
||||
[Desc("The width of the beam.")]
|
||||
public readonly WDist Width = new WDist(512);
|
||||
|
||||
[Desc("How far beyond the target the projectile keeps on travelling.")]
|
||||
public readonly WDist BeyondTargetRange = new WDist(0);
|
||||
|
||||
[Desc("Damage modifier applied at each range step.")]
|
||||
public readonly int[] Falloff = { 100, 100 };
|
||||
|
||||
[Desc("Ranges at which each Falloff step is defined.")]
|
||||
public readonly WDist[] Range = { WDist.Zero, new WDist(int.MaxValue) };
|
||||
|
||||
[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("Extra search radius beyond beam width. Required to ensure affecting actors with large health radius.")]
|
||||
public readonly WDist TargetExtraSearchRadius = new WDist(2048);
|
||||
|
||||
[Desc("Should the beam be visuall rendered? False = Beam is invisible.")]
|
||||
public readonly bool RenderBeam = true;
|
||||
|
||||
[Desc("Color of the beam.")]
|
||||
public readonly Color Color = Color.Red;
|
||||
|
||||
[Desc("Beam color is the player's color.")]
|
||||
public readonly bool UsePlayerColor = false;
|
||||
|
||||
public IEffect Create(ProjectileArgs args)
|
||||
{
|
||||
var c = UsePlayerColor ? args.SourceActor.Owner.Color.RGB : Color;
|
||||
return new AreaBeam(this, args, c);
|
||||
}
|
||||
}
|
||||
|
||||
public class AreaBeam : IEffect, ISync
|
||||
{
|
||||
readonly AreaBeamInfo info;
|
||||
readonly ProjectileArgs args;
|
||||
readonly AttackBase actorAttackBase;
|
||||
readonly Color color;
|
||||
readonly WDist speed;
|
||||
|
||||
[Sync] WPos headPos;
|
||||
[Sync] WPos tailPos;
|
||||
[Sync] WPos target;
|
||||
int length;
|
||||
int towardsTargetFacing;
|
||||
int headTicks;
|
||||
int tailTicks;
|
||||
bool isHeadTravelling = true;
|
||||
bool isTailTravelling;
|
||||
|
||||
bool IsBeamComplete { get { return !isHeadTravelling && headTicks >= length &&
|
||||
!isTailTravelling && tailTicks >= length; } }
|
||||
|
||||
public AreaBeam(AreaBeamInfo info, ProjectileArgs args, Color color)
|
||||
{
|
||||
this.info = info;
|
||||
this.args = args;
|
||||
this.color = color;
|
||||
actorAttackBase = args.SourceActor.Trait<AttackBase>();
|
||||
|
||||
var world = args.SourceActor.World;
|
||||
if (info.Speed.Length > 1)
|
||||
speed = new WDist(world.SharedRandom.Next(info.Speed[0].Length, info.Speed[1].Length));
|
||||
else
|
||||
speed = info.Speed[0];
|
||||
|
||||
// Both the head and tail start at the source actor, but initially only the head is travelling.
|
||||
headPos = args.Source;
|
||||
tailPos = headPos;
|
||||
|
||||
target = args.PassiveTarget;
|
||||
if (info.Inaccuracy.Length > 0)
|
||||
{
|
||||
var inaccuracy = OpenRA.Traits.Util.ApplyPercentageModifiers(info.Inaccuracy.Length, args.InaccuracyModifiers);
|
||||
var maxOffset = inaccuracy * (target - headPos).Length / args.Weapon.Range.Length;
|
||||
target += WVec.FromPDF(world.SharedRandom, 2) * maxOffset / 1024;
|
||||
}
|
||||
|
||||
towardsTargetFacing = OpenRA.Traits.Util.GetFacing(target - headPos, 0);
|
||||
|
||||
// Update the target position with the range we shoot beyond the target by
|
||||
// I.e. we can deliberately overshoot, so aim for that position
|
||||
var dir = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(towardsTargetFacing));
|
||||
target += dir * info.BeyondTargetRange.Length / 1024;
|
||||
|
||||
length = Math.Max((target - headPos).Length / speed.Length, 1);
|
||||
}
|
||||
|
||||
public void Tick(World world)
|
||||
{
|
||||
if (++headTicks >= length)
|
||||
{
|
||||
headPos = target;
|
||||
isHeadTravelling = false;
|
||||
}
|
||||
else if (isHeadTravelling)
|
||||
headPos = WPos.LerpQuadratic(args.Source, target, WAngle.Zero, headTicks, length);
|
||||
|
||||
if (tailTicks <= 0 && args.SourceActor.IsInWorld && !args.SourceActor.IsDead)
|
||||
{
|
||||
args.Source = args.CurrentSource();
|
||||
tailPos = args.Source;
|
||||
}
|
||||
|
||||
// Allow for 1 cell (1024) leniency to avoid edge case stuttering (start firing and immediately stop again).
|
||||
var outOfWeaponRange = args.Weapon.Range.Length + 1024 < (args.PassiveTarget - args.Source).Length;
|
||||
|
||||
// While the head is travelling, the tail must start to follow Duration ticks later.
|
||||
// Alternatively, also stop emitting the beam if source actor dies or is ordered to stop.
|
||||
if ((headTicks >= info.Duration && !isTailTravelling) || args.SourceActor.IsDead ||
|
||||
!actorAttackBase.IsAttacking || outOfWeaponRange)
|
||||
isTailTravelling = true;
|
||||
|
||||
if (isTailTravelling)
|
||||
{
|
||||
if (++tailTicks >= length)
|
||||
{
|
||||
tailPos = target;
|
||||
isTailTravelling = false;
|
||||
}
|
||||
else
|
||||
tailPos = WPos.LerpQuadratic(args.Source, target, WAngle.Zero, tailTicks, length);
|
||||
}
|
||||
|
||||
// Check for blocking actors
|
||||
WPos blockedPos;
|
||||
if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, tailPos, headPos,
|
||||
info.Width, info.TargetExtraSearchRadius, out blockedPos))
|
||||
{
|
||||
headPos = blockedPos;
|
||||
target = headPos;
|
||||
length = Math.Min(headTicks, length);
|
||||
}
|
||||
|
||||
// Damage is applied to intersected actors every DamageInterval ticks
|
||||
if (headTicks % info.DamageInterval == 0)
|
||||
{
|
||||
var actors = world.FindActorsOnLine(tailPos, headPos, info.Width, info.TargetExtraSearchRadius);
|
||||
foreach (var a in actors)
|
||||
{
|
||||
var adjustedModifiers = args.DamageModifiers.Append(GetFalloff((args.Source - a.CenterPosition).Length));
|
||||
args.Weapon.Impact(Target.FromActor(a), args.SourceActor, adjustedModifiers);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsBeamComplete)
|
||||
world.AddFrameEndTask(w => w.Remove(this));
|
||||
}
|
||||
|
||||
public IEnumerable<IRenderable> Render(WorldRenderer wr)
|
||||
{
|
||||
if (!IsBeamComplete && info.RenderBeam && !(wr.World.FogObscures(tailPos) && wr.World.FogObscures(headPos)))
|
||||
{
|
||||
float width, y, z;
|
||||
wr.ScreenVectorComponents(new WVec(info.Width, WDist.Zero, WDist.Zero), out width, out y, out z);
|
||||
var beamRender = new BeamRenderable(headPos, 0, tailPos - headPos, width, color);
|
||||
return new[] { (IRenderable)beamRender };
|
||||
}
|
||||
|
||||
return SpriteRenderable.None;
|
||||
}
|
||||
|
||||
int GetFalloff(int distance)
|
||||
{
|
||||
var inner = info.Range[0].Length;
|
||||
for (var i = 1; i < info.Range.Length; i++)
|
||||
{
|
||||
var outer = info.Range[i].Length;
|
||||
if (outer > distance)
|
||||
return int2.Lerp(info.Falloff[i - 1], info.Falloff[i], distance - inner, outer - inner);
|
||||
|
||||
inner = outer;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,6 +157,7 @@
|
||||
<Compile Include="Effects\FloatingText.cs" />
|
||||
<Compile Include="Effects\GravityBomb.cs" />
|
||||
<Compile Include="Effects\LaserZap.cs" />
|
||||
<Compile Include="Effects\AreaBeam.cs" />
|
||||
<Compile Include="Effects\Missile.cs" />
|
||||
<Compile Include="Effects\NukeLaunch.cs" />
|
||||
<Compile Include="Effects\PowerdownIndicator.cs" />
|
||||
@@ -534,6 +535,7 @@
|
||||
<Compile Include="UtilityCommands\ExtractLanguageStringsCommand.cs" />
|
||||
<Compile Include="UtilityCommands\ExtractLuaDocsCommand.cs" />
|
||||
<Compile Include="UtilityCommands\ExtractTraitDocsCommand.cs" />
|
||||
<Compile Include="WorldExtensions.cs" />
|
||||
<Compile Include="UtilityCommands\GenerateMinimapCommand.cs" />
|
||||
<Compile Include="UtilityCommands\GetMapHashCommand.cs" />
|
||||
<Compile Include="UtilityCommands\Glob.cs" />
|
||||
|
||||
@@ -187,7 +187,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return null;
|
||||
|
||||
var barrel = Barrels[Burst % Barrels.Length];
|
||||
var muzzlePosition = self.CenterPosition + MuzzleOffset(self, barrel);
|
||||
Func<WPos> muzzlePosition = () => self.CenterPosition + MuzzleOffset(self, barrel);
|
||||
var legacyFacing = MuzzleOrientation(self, barrel).Yaw.Angle / 4;
|
||||
|
||||
var args = new ProjectileArgs
|
||||
@@ -204,7 +204,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
RangeModifiers = self.TraitsImplementing<IRangeModifier>()
|
||||
.Select(a => a.GetRangeModifier()).ToArray(),
|
||||
|
||||
Source = muzzlePosition,
|
||||
Source = muzzlePosition(),
|
||||
CurrentSource = muzzlePosition,
|
||||
SourceActor = self,
|
||||
PassiveTarget = target.CenterPosition,
|
||||
GuidedTarget = target
|
||||
|
||||
@@ -34,5 +34,31 @@ namespace OpenRA.Mods.Common.Traits
|
||||
.Where(t => t.Info.Height.Length >= dat.Length)
|
||||
.Any(Exts.IsTraitEnabled));
|
||||
}
|
||||
|
||||
public static bool AnyBlockingActorsBetween(World world, WPos start, WPos end, WDist width, WDist overscan, out WPos hit)
|
||||
{
|
||||
var actors = world.FindActorsOnLine(start, end, width, overscan);
|
||||
var length = (end - start).Length;
|
||||
|
||||
foreach (var a in actors)
|
||||
{
|
||||
var blockers = a.TraitsImplementing<BlocksProjectiles>()
|
||||
.Where(Exts.IsTraitEnabled).ToList();
|
||||
|
||||
if (!blockers.Any())
|
||||
continue;
|
||||
|
||||
var hitPos = WorldExtensions.MinimumPointLineProjection(start, end, a.CenterPosition);
|
||||
var dat = world.Map.DistanceAboveTerrain(hitPos);
|
||||
if ((hitPos - start).Length < length && blockers.Any(t => t.Info.Height.Length >= dat.Length))
|
||||
{
|
||||
hit = hitPos;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
hit = WPos.Zero;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
.Select(a => a.GetRangeModifier()).ToArray(),
|
||||
|
||||
Source = self.CenterPosition,
|
||||
CurrentSource = () => self.CenterPosition,
|
||||
SourceActor = self,
|
||||
PassiveTarget = self.CenterPosition + new WVec(range, 0, 0).Rotate(rotation)
|
||||
};
|
||||
|
||||
99
OpenRA.Mods.Common/WorldExtensions.cs
Normal file
99
OpenRA.Mods.Common/WorldExtensions.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 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 OpenRA.Mods.Common.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common
|
||||
{
|
||||
public static class WorldExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Finds all the actors of which their health radius is intersected by a line (with a definable width) between two points.
|
||||
/// </summary>
|
||||
/// <param name="world">The engine world the line intersection is to be done in.</param>
|
||||
/// <param name="lineStart">The position the line should start at</param>
|
||||
/// <param name="lineEnd">The position the line should end at</param>
|
||||
/// <param name="lineWidth">How close an actor's health radius needs to be to the line to be considered 'intersected' by the line</param>
|
||||
/// <returns>A list of all the actors intersected by the line</returns>
|
||||
public static IEnumerable<Actor> FindActorsOnLine(this World world, WPos lineStart, WPos lineEnd, WDist lineWidth, WDist targetExtraSearchRadius)
|
||||
{
|
||||
// This line intersection check is done by first just finding all actors within a square that starts at the source, and ends at the target.
|
||||
// Then we iterate over this list, and find all actors for which their health radius is at least within lineWidth of the line.
|
||||
// For actors without a health radius, we simply check their center point.
|
||||
// The square in which we select all actors must be large enough to encompass the entire line's width.
|
||||
var xDir = Math.Sign(lineEnd.X - lineStart.X);
|
||||
var yDir = Math.Sign(lineEnd.Y - lineStart.Y);
|
||||
|
||||
var dir = new WVec(xDir, yDir, 0);
|
||||
var overselect = dir * (1024 + lineWidth.Length + targetExtraSearchRadius.Length);
|
||||
var finalTarget = lineEnd + overselect;
|
||||
var finalSource = lineStart - overselect;
|
||||
|
||||
var actorsInSquare = world.ActorMap.ActorsInBox(finalTarget, finalSource);
|
||||
var intersectedActors = new List<Actor>();
|
||||
|
||||
foreach (var currActor in actorsInSquare)
|
||||
{
|
||||
var actorWidth = 0;
|
||||
var healthInfo = currActor.Info.TraitInfoOrDefault<HealthInfo>();
|
||||
if (healthInfo != null)
|
||||
actorWidth = healthInfo.Radius.Length;
|
||||
|
||||
var projection = MinimumPointLineProjection(lineStart, lineEnd, currActor.CenterPosition);
|
||||
var distance = (currActor.CenterPosition - projection).HorizontalLength;
|
||||
var maxReach = actorWidth + lineWidth.Length;
|
||||
|
||||
if (distance <= maxReach)
|
||||
intersectedActors.Add(currActor);
|
||||
}
|
||||
|
||||
return intersectedActors;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the point (D) on a line (A-B) that is closest to the target point (C).
|
||||
/// </summary>
|
||||
/// <param name="lineStart">The source point (tail) of the line</param>
|
||||
/// <param name="lineEnd">The target point (head) of the line</param>
|
||||
/// <param name="point">The target point that the minimum distance should be found to</param>
|
||||
/// <returns>The WPos that is the point on the line that is closest to the target point</returns>
|
||||
public static WPos MinimumPointLineProjection(WPos lineStart, WPos lineEnd, WPos point)
|
||||
{
|
||||
var squaredLength = (lineEnd - lineStart).HorizontalLengthSquared;
|
||||
|
||||
// Line has zero length, so just use the lineEnd position as the closest position.
|
||||
if (squaredLength == 0)
|
||||
return lineEnd;
|
||||
|
||||
// Consider the line extending the segment, parameterized as target + t (source - target).
|
||||
// We find projection of point onto the line.
|
||||
// It falls where t = [(point - target) . (source - target)] / |source - target|^2
|
||||
// The normal DotProduct math would be (xDiff + yDiff) / dist, where dist = (target - source).LengthSquared;
|
||||
// But in order to avoid floating points, we do not divide here, but rather work with the large numbers as far as possible.
|
||||
// We then later divide by dist, only AFTER we have multiplied by the dotproduct.
|
||||
var xDiff = ((long)point.X - lineEnd.X) * (lineStart.X - lineEnd.X);
|
||||
var yDiff = ((long)point.Y - lineEnd.Y) * (lineStart.Y - lineEnd.Y);
|
||||
var t = xDiff + yDiff;
|
||||
|
||||
// Beyond the 'target' end of the segment
|
||||
if (t < 0)
|
||||
return lineEnd;
|
||||
|
||||
// Beyond the 'source' end of the segment
|
||||
if (t > squaredLength)
|
||||
return lineStart;
|
||||
|
||||
// Projection falls on the segment
|
||||
return WPos.Lerp(lineEnd, lineStart, t, squaredLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,6 +136,7 @@ namespace OpenRA.Mods.D2k.Traits
|
||||
.Select(a => a.GetInaccuracyModifier()).ToArray(),
|
||||
|
||||
Source = self.CenterPosition,
|
||||
CurrentSource = () => self.CenterPosition,
|
||||
SourceActor = self,
|
||||
PassiveTarget = self.World.Map.CenterOfCell(cell.Value)
|
||||
};
|
||||
|
||||
@@ -290,6 +290,8 @@ sonic_tank:
|
||||
Actor: sonic_tank.husk
|
||||
AttractsWorms:
|
||||
Intensity: 600
|
||||
Targetable:
|
||||
TargetTypes: Ground, Vehicle, C4, Sonictank
|
||||
|
||||
devastator:
|
||||
Inherits: ^Tank
|
||||
|
||||
@@ -530,14 +530,36 @@ Sound:
|
||||
ReloadDelay: 90
|
||||
Range: 5c0
|
||||
Report: SONIC1.WAV
|
||||
Projectile: LaserZap
|
||||
BeamWidth: 10
|
||||
BeamDuration: 8
|
||||
UsePlayerColor: true
|
||||
Projectile: AreaBeam
|
||||
Speed: 0c128
|
||||
Duration: 4 # Has a length of 0c512
|
||||
DamageInterval: 3 # Travels 0c384 between impacts, will hit a target roughly three times
|
||||
Width: 0c512
|
||||
Falloff: 100, 100, 50
|
||||
Range: 0, 6c0, 11c0
|
||||
BeyondTargetRange: 1c0
|
||||
Color: 00FFFFC8
|
||||
Warhead@1Dam: SpreadDamage
|
||||
Spread: 256
|
||||
Falloff: 100, 100, 0
|
||||
Damage: 500 #80 D2k but damages through all in path
|
||||
Range: 0, 32
|
||||
Falloff: 100, 100
|
||||
Damage: 150
|
||||
AffectsParent: false
|
||||
ValidStances: Neutral, Enemy
|
||||
Versus:
|
||||
wall: 50
|
||||
building: 60
|
||||
heavy: 60
|
||||
invulnerable: 0
|
||||
cy: 20
|
||||
harvester: 50
|
||||
DamageTypes: Prone50Percent, TriggerProne, SoundDeath
|
||||
Warhead@2Dam: SpreadDamage
|
||||
Range: 0, 32
|
||||
Falloff: 50, 50 # Only does half damage to friendly units
|
||||
Damage: 150
|
||||
InvalidTargets: Sonictank # Does not affect friendly sonic tanks at all
|
||||
AffectsParent: false
|
||||
ValidStances: Ally
|
||||
Versus:
|
||||
wall: 50
|
||||
building: 60
|
||||
@@ -546,9 +568,6 @@ Sound:
|
||||
cy: 20
|
||||
harvester: 50
|
||||
DamageTypes: Prone50Percent, TriggerProne, SoundDeath
|
||||
Warhead@3Eff: CreateEffect
|
||||
ImpactSound: SONIC3.WAV
|
||||
Delay: 10
|
||||
|
||||
Heal:
|
||||
ReloadDelay: 160
|
||||
|
||||
@@ -207,6 +207,8 @@ SONIC:
|
||||
Queue: Vehicle
|
||||
BuildPaletteOrder: 70
|
||||
Prerequisites: ~gaweap, gatech
|
||||
Targetable:
|
||||
TargetTypes: Ground, Vehicle, Repair, Disruptor
|
||||
Mobile:
|
||||
ROT: 4
|
||||
Speed: 56
|
||||
|
||||
@@ -2,14 +2,35 @@ LtRail:
|
||||
ReloadDelay: 60
|
||||
Range: 6c0
|
||||
Report: BIGGGUN1.AUD
|
||||
Projectile: LaserZap
|
||||
Speed: 1c682
|
||||
BeamWidth: 1
|
||||
BeamDuration: 10
|
||||
Projectile: AreaBeam
|
||||
Speed: 20c0
|
||||
Duration: 3
|
||||
DamageInterval: 2
|
||||
Width: 0c128
|
||||
BeyondTargetRange: 0c64
|
||||
Blockable: true
|
||||
Color: 0080FFC8
|
||||
Warhead@1Dam: SpreadDamage
|
||||
Spread: 42
|
||||
Range: 0, 32
|
||||
Falloff: 100, 100
|
||||
Damage: 150
|
||||
InfDeath: 6
|
||||
AffectsParent: false
|
||||
ValidStances: Neutral, Enemy
|
||||
Versus:
|
||||
None: 100
|
||||
Wood: 130
|
||||
Light: 150
|
||||
Heavy: 110
|
||||
Concrete: 5
|
||||
DamageTypes: Prone100Percent, TriggerProne, SmallExplosionDeath
|
||||
Warhead@2Dam: SpreadDamage
|
||||
Range: 0, 32
|
||||
Falloff: 50, 50 # Only does half damage to friendly units
|
||||
Damage: 150
|
||||
InfDeath: 6
|
||||
AffectsParent: false
|
||||
ValidStances: Ally
|
||||
Versus:
|
||||
None: 100
|
||||
Wood: 130
|
||||
@@ -24,9 +45,14 @@ MechRailgun:
|
||||
Burst: 2 # for alternating muzzle offsets, dmg/s identical to original
|
||||
BurstDelay: 60
|
||||
Report: RAILUSE5.AUD
|
||||
Projectile: LaserZap
|
||||
Projectile: AreaBeam
|
||||
Speed: 20c0
|
||||
Duration: 3
|
||||
DamageInterval: 2
|
||||
Width: 0c128
|
||||
BeyondTargetRange: 0c64
|
||||
Blockable: true
|
||||
Color: 00FFFFC8
|
||||
BeamWidth: 3
|
||||
Warhead@1Dam: SpreadDamage
|
||||
Spread: 42
|
||||
Damage: 200
|
||||
@@ -39,21 +65,39 @@ MechRailgun:
|
||||
DamageTypes: Prone100Percent, TriggerProne, FireDeath
|
||||
|
||||
SonicZap:
|
||||
ReloadDelay: 120
|
||||
ReloadDelay: 180
|
||||
Range: 6c0
|
||||
Charges: yes
|
||||
Report: SONIC4.AUD
|
||||
Projectile: LaserZap
|
||||
Projectile: AreaBeam
|
||||
Speed: 0c128
|
||||
Duration: 90
|
||||
DamageInterval: 5 # Roughly 18 impacts.
|
||||
Width: 0c384
|
||||
BeyondTargetRange: 0c256
|
||||
Blockable: true
|
||||
Color: 00FFFFC8
|
||||
BeamWidth: 12
|
||||
BeamDuration: 50
|
||||
Warhead@1Dam: SpreadDamage
|
||||
Spread: 42
|
||||
Damage: 100
|
||||
Range: 0, 32
|
||||
Falloff: 100, 100
|
||||
Damage: 8
|
||||
AffectsParent: false
|
||||
ValidStances: Neutral, Enemy
|
||||
Versus:
|
||||
Heavy: 80
|
||||
Concrete: 60
|
||||
DamageTypes: Prone50Percent, TriggerProne, FireDeath
|
||||
DamageTypes: Prone50Percent, TriggerProne, ExplosionDeath
|
||||
Warhead@2Dam: SpreadDamage
|
||||
Range: 0, 32
|
||||
Falloff: 50, 50
|
||||
Damage: 8
|
||||
InvalidTargets: Disruptor # Does not affect friendly disruptors at all
|
||||
AffectsParent: false
|
||||
ValidStances: Ally
|
||||
Versus:
|
||||
Heavy: 80
|
||||
Concrete: 60
|
||||
DamageTypes: Prone50Percent, TriggerProne, ExplosionDeath
|
||||
|
||||
CyCannon:
|
||||
ReloadDelay: 50
|
||||
|
||||
Reference in New Issue
Block a user