Merge pull request #3592 from pchote/world-projectiles

Convert projectiles to world coordinates.
This commit is contained in:
Chris Forbes
2013-08-01 01:36:44 -07:00
28 changed files with 520 additions and 961 deletions

View File

@@ -67,6 +67,42 @@ namespace OpenRA
return new WAngle(Angle - 512).Tan(); return new WAngle(Angle - 512).Tan();
} }
public static WAngle ArcTan(int y, int x) { return ArcTan(y, x, 1); }
public static WAngle ArcTan(int y, int x, int stride)
{
if (y == 0)
return new WAngle(x >= 0 ? 0 : 512);
if (x == 0)
return new WAngle(Math.Sign(y)*256);
var ay = Math.Abs(y);
var ax = Math.Abs(x);
// Find the closest angle that satisfies y = x*tan(theta)
var bestVal = int.MaxValue;
var bestAngle = 0;
for (var i = 0; i < 256; i+= stride)
{
var val = Math.Abs(1024*ay - ax*TanTable[i]);
if (val < bestVal)
{
bestVal = val;
bestAngle = i;
}
}
// Calculate quadrant
if (x < 0 && y > 0)
bestAngle = 512 - bestAngle;
else if (x < 0 && y < 0)
bestAngle = 512 + bestAngle;
else if (x > 0 && y < 0)
bestAngle = 1024 - bestAngle;
return new WAngle(bestAngle);
}
// Must not be used outside rendering code // Must not be used outside rendering code
public float RendererRadians() { return (float)(Angle * Math.PI / 512f); } public float RendererRadians() { return (float)(Angle * Math.PI / 512f); }
public float RendererDegrees() { return Angle * 0.3515625f; } public float RendererDegrees() { return Angle * 0.3515625f; }

View File

@@ -10,6 +10,7 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Linq;
namespace OpenRA namespace OpenRA
{ {
@@ -34,6 +35,17 @@ namespace OpenRA
public static bool operator ==(WRange me, WRange other) { return (me.Range == other.Range); } public static bool operator ==(WRange me, WRange other) { return (me.Range == other.Range); }
public static bool operator !=(WRange me, WRange other) { return !(me == other); } public static bool operator !=(WRange me, WRange other) { return !(me == other); }
// Sampled a N-sample probability density function in the range [-1024..1024]
// 1 sample produces a rectangular probability
// 2 samples produces a triangular probability
// ...
// N samples approximates a true gaussian
public static WRange FromPDF(Thirdparty.Random r, int samples)
{
return new WRange(Exts.MakeArray(samples, _ => r.Next(-1024, 1024))
.Sum() / samples);
}
public static bool TryParse(string s, out WRange result) public static bool TryParse(string s, out WRange result)
{ {
s = s.ToLowerInvariant(); s = s.ToLowerInvariant();

View File

@@ -69,6 +69,16 @@ namespace OpenRA
return new WVec(ret.X, ret.Y, ret.Z + offset); return new WVec(ret.X, ret.Y, ret.Z + offset);
} }
// Sampled a N-sample probability density function in the range [-1024..1024, -1024..1024]
// 1 sample produces a rectangular probability
// 2 samples produces a triangular probability
// ...
// N samples approximates a true gaussian
public static WVec FromPDF(Thirdparty.Random r, int samples)
{
return new WVec(WRange.FromPDF(r, samples), WRange.FromPDF(r, samples), WRange.Zero);
}
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); } public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); }
public override bool Equals(object obj) public override bool Equals(object obj)

View File

@@ -72,8 +72,9 @@ namespace OpenRA
public static CPos BottomRightAsCPos(this Rectangle r) { return new CPos(r.Right, r.Bottom); } public static CPos BottomRightAsCPos(this Rectangle r) { return new CPos(r.Right, r.Bottom); }
} }
public static class WPosExtensions public static class WorldCoordinateExtensions
{ {
public static CPos ToCPos(this WPos a) { return new CPos(a.X / 1024, a.Y / 1024); } public static CPos ToCPos(this WPos a) { return new CPos(a.X / 1024, a.Y / 1024); }
public static CVec ToCVec(this WVec a) { return new CVec(a.X / 1024, a.Y / 1024); }
} }
} }

View File

@@ -74,8 +74,6 @@ namespace OpenRA.GameRules
} }
} }
public enum DamageModel public enum DamageModel
{ {
Normal, // classic RA damage model: point actors, distance-based falloff Normal, // classic RA damage model: point actors, distance-based falloff
@@ -85,14 +83,12 @@ namespace OpenRA.GameRules
public class ProjectileArgs public class ProjectileArgs
{ {
public WeaponInfo weapon; public WeaponInfo weapon;
public Actor firedBy;
public PPos src;
public int srcAltitude;
public int facing;
public Target target;
public PPos dest;
public int destAltitude;
public float firepowerModifier = 1.0f; public float firepowerModifier = 1.0f;
public int facing;
public WPos source;
public Actor sourceActor;
public WPos passiveTarget;
public Target guidedTarget;
} }
public interface IProjectileInfo { IEffect Create(ProjectileArgs args); } public interface IProjectileInfo { IEffect Create(ProjectileArgs args); }

View File

@@ -86,8 +86,6 @@
<Compile Include="ActorMap.cs" /> <Compile Include="ActorMap.cs" />
<Compile Include="ActorReference.cs" /> <Compile Include="ActorReference.cs" />
<Compile Include="Graphics\QuadRenderer.cs" /> <Compile Include="Graphics\QuadRenderer.cs" />
<Compile Include="PSubVec.cs" />
<Compile Include="PSubPos.cs" />
<Compile Include="PVecInt.cs" /> <Compile Include="PVecInt.cs" />
<Compile Include="PPos.cs" /> <Compile Include="PPos.cs" />
<Compile Include="Download.cs" /> <Compile Include="Download.cs" />

View File

@@ -77,7 +77,6 @@ namespace OpenRA
public float2 ToFloat2() { return new float2(X, Y); } public float2 ToFloat2() { return new float2(X, Y); }
public int2 ToInt2() { return new int2(X, Y); } public int2 ToInt2() { return new int2(X, Y); }
public CPos ToCPos() { return new CPos((int)(1f / Game.CellSize * X), (int)(1f / Game.CellSize * Y)); } public CPos ToCPos() { return new CPos((int)(1f / Game.CellSize * X), (int)(1f / Game.CellSize * Y)); }
public PSubPos ToPSubPos() { return new PSubPos(X * PSubPos.PerPx, Y * PSubPos.PerPx); }
public PPos Clamp(Rectangle r) public PPos Clamp(Rectangle r)
{ {

View File

@@ -1,72 +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;
using System.Drawing;
namespace OpenRA
{
/// <summary>
/// Sub-pixel coordinate position in the world (very fine).
/// </summary>
public struct PSubPos
{
public readonly int X, Y;
public PSubPos(int x, int y) { X = x; Y = y; }
public const int PerPx = 1024;
public static readonly PSubPos Zero = new PSubPos(0, 0);
public static explicit operator PSubPos(int2 a) { return new PSubPos(a.X, a.Y); }
public static explicit operator PSubVec(PSubPos a) { return new PSubVec(a.X, a.Y); }
public static PSubPos operator +(PSubPos a, PSubVec b) { return new PSubPos(a.X + b.X, a.Y + b.Y); }
public static PSubVec operator -(PSubPos a, PSubPos b) { return new PSubVec(a.X - b.X, a.Y - b.Y); }
public static PSubPos operator -(PSubPos a, PSubVec b) { return new PSubPos(a.X - b.X, a.Y - b.Y); }
public static bool operator ==(PSubPos me, PSubPos other) { return (me.X == other.X && me.Y == other.Y); }
public static bool operator !=(PSubPos me, PSubPos other) { return !(me == other); }
public static PSubPos Max(PSubPos a, PSubPos b) { return new PSubPos(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)); }
public static PSubPos Min(PSubPos a, PSubPos b) { return new PSubPos(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y)); }
public static PSubPos Lerp(PSubPos a, PSubPos b, int mul, int div)
{
return a + ((PSubVec)(b - a) * mul / div);
}
public float2 ToFloat2() { return new float2(X, Y); }
public int2 ToInt2() { return new int2(X, Y); }
public PPos ToPPos() { return new PPos(X / PerPx, Y / PerPx); }
public CPos ToCPos() { return ToPPos().ToCPos(); }
public PSubPos Clamp(Rectangle r)
{
return new PSubPos(Math.Min(r.Right, Math.Max(X, r.Left)),
Math.Min(r.Bottom, Math.Max(Y, r.Top)));
}
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); }
public override bool Equals(object obj)
{
if (obj == null)
return false;
PSubPos o = (PSubPos)obj;
return o == this;
}
public override string ToString() { return "{0},{1}".F(X, Y); }
}
}

View File

@@ -1,98 +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;
using System.Drawing;
namespace OpenRA
{
/// <summary>
/// Sub-pixel coordinate vector (very fine)
/// </summary>
public struct PSubVec
{
public readonly int X, Y;
public PSubVec(int x, int y) { X = x; Y = y; }
public PSubVec(Size p) { X = p.Width; Y = p.Height; }
public static readonly PSubVec Zero = new PSubVec(0, 0);
public static PSubVec OneCell { get { return new PSubVec(Game.CellSize, Game.CellSize); } }
public static explicit operator PSubVec(int2 a) { return new PSubVec(a.X, a.Y); }
public static explicit operator PSubVec(float2 a) { return new PSubVec((int)a.X, (int)a.Y); }
public static PSubVec FromRadius(int r) { return new PSubVec(r, r); }
public static PSubVec operator +(PSubVec a, PSubVec b) { return new PSubVec(a.X + b.X, a.Y + b.Y); }
public static PSubVec operator -(PSubVec a, PSubVec b) { return new PSubVec(a.X - b.X, a.Y - b.Y); }
public static PSubVec operator *(int a, PSubVec b) { return new PSubVec(a * b.X, a * b.Y); }
public static PSubVec operator *(PSubVec b, int a) { return new PSubVec(a * b.X, a * b.Y); }
public static PSubVec operator /(PSubVec a, int b) { return new PSubVec(a.X / b, a.Y / b); }
public static PSubVec operator -(PSubVec a) { return new PSubVec(-a.X, -a.Y); }
public static bool operator ==(PSubVec me, PSubVec other) { return (me.X == other.X && me.Y == other.Y); }
public static bool operator !=(PSubVec me, PSubVec other) { return !(me == other); }
public static PSubVec Max(PSubVec a, PSubVec b) { return new PSubVec(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)); }
public static PSubVec Min(PSubVec a, PSubVec b) { return new PSubVec(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y)); }
public static int Dot(PSubVec a, PSubVec b) { return a.X * b.X + a.Y * b.Y; }
public PSubVec Sign() { return new PSubVec(Math.Sign(X), Math.Sign(Y)); }
public PSubVec Abs() { return new PSubVec(Math.Abs(X), Math.Abs(Y)); }
public int LengthSquared { get { return X * X + Y * Y; } }
public int Length { get { return (int)Math.Sqrt(LengthSquared); } }
public float2 ToFloat2() { return new float2(X, Y); }
public int2 ToInt2() { return new int2(X, Y); }
public PVecInt ToPVecInt() { return new PVecInt(X / PSubPos.PerPx, Y / PSubPos.PerPx); }
public PSubVec Clamp(Rectangle r)
{
return new PSubVec(
Math.Min(r.Right, Math.Max(X, r.Left)),
Math.Min(r.Bottom, Math.Max(Y, r.Top))
);
}
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); }
public override bool Equals(object obj)
{
if (obj == null)
return false;
PSubVec o = (PSubVec)obj;
return o == this;
}
public override string ToString() { return "{0},{1}".F(X, Y); }
}
public static class PSubVecExtensions
{
/// <summary>
/// Scales the float2 vector up to a subpixel vector.
/// </summary>
/// <param name="vec"></param>
/// <returns></returns>
public static PSubVec ToPSubVec(this float2 vec)
{
return new PSubVec((int)(vec.X * PSubPos.PerPx), (int)(vec.Y * PSubPos.PerPx));
}
public static PSubVec ToPSubVec(this PVecInt vec)
{
return new PSubVec((vec.X * PSubPos.PerPx), (vec.Y * PSubPos.PerPx));
}
}
}

View File

@@ -37,8 +37,6 @@ namespace OpenRA
{typeof(CVec), ((Func<CVec, int>)hash_CVec).Method}, {typeof(CVec), ((Func<CVec, int>)hash_CVec).Method},
{typeof(PPos), ((Func<PPos, int>)hash_PPos).Method}, {typeof(PPos), ((Func<PPos, int>)hash_PPos).Method},
{typeof(PVecInt), ((Func<PVecInt, int>)hash_PVecInt).Method}, {typeof(PVecInt), ((Func<PVecInt, int>)hash_PVecInt).Method},
{typeof(PSubPos), ((Func<PSubPos, int>)hash_PSubPos).Method},
{typeof(PSubVec), ((Func<PSubVec, int>)hash_PSubVec).Method},
{typeof(WRange), ((Func<WRange, int>)hash<WRange>).Method}, {typeof(WRange), ((Func<WRange, int>)hash<WRange>).Method},
{typeof(WPos), ((Func<WPos, int>)hash<WPos>).Method}, {typeof(WPos), ((Func<WPos, int>)hash<WPos>).Method},
{typeof(WVec), ((Func<WVec, int>)hash<WVec>).Method}, {typeof(WVec), ((Func<WVec, int>)hash<WVec>).Method},
@@ -126,16 +124,6 @@ namespace OpenRA
return ((i2.X * 5) ^ (i2.Y * 3)) / 4; return ((i2.X * 5) ^ (i2.Y * 3)) / 4;
} }
public static int hash_PSubPos(PSubPos i2)
{
return ((i2.X * 5) ^ (i2.Y * 3)) / 4;
}
public static int hash_PSubVec(PSubVec i2)
{
return ((i2.X * 5) ^ (i2.Y * 3)) / 4;
}
public static int hash_tdict(TypeDictionary d) public static int hash_tdict(TypeDictionary d)
{ {
int ret = 0; int ret = 0;

View File

@@ -25,7 +25,6 @@ namespace OpenRA.Traits
int generation; int generation;
public static Target FromPos(WPos p) { return new Target { pos = p, valid = true }; } public static Target FromPos(WPos p) { return new Target { pos = p, valid = true }; }
public static Target FromPos(PPos p) { return new Target { pos = p.ToWPos(0), valid = true }; }
public static Target FromCell(CPos c) { return new Target { pos = c.CenterPosition, valid = true }; } public static Target FromCell(CPos c) { return new Target { pos = c.CenterPosition, valid = true }; }
public static Target FromOrder(Order o) public static Target FromOrder(Order o)
{ {
@@ -45,7 +44,6 @@ namespace OpenRA.Traits
} }
public bool IsValid { get { return valid && (actor == null || (actor.IsInWorld && !actor.IsDead() && actor.Generation == generation)); } } public bool IsValid { get { return valid && (actor == null || (actor.IsInWorld && !actor.IsDead() && actor.Generation == generation)); } }
public PPos CenterLocation { get { return IsActor ? actor.CenterLocation : PPos.FromWPos(pos); } }
public Actor Actor { get { return IsActor ? actor : null; } } public Actor Actor { get { return IsActor ? actor : null; } }
// TODO: This should return true even if the actor is destroyed // TODO: This should return true even if the actor is destroyed

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #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 * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -32,38 +32,19 @@ namespace OpenRA.Traits
public static int GetFacing(WVec d, int currentFacing) public static int GetFacing(WVec d, int currentFacing)
{ {
return GetFacing(new int2(d.X, d.Y), currentFacing); if (d.LengthSquared == 0)
} return currentFacing;
public static int GetFacing(PVecInt d, int currentFacing) // OpenRA defines north as -y, so invert
{ var angle = WAngle.ArcTan(-d.Y, d.X, 4).Angle;
return GetFacing(d.ToInt2(), currentFacing);
// Convert back to a facing
return (angle / 4 - 0x40) & 0xFF;
} }
public static int GetFacing(CVec d, int currentFacing) public static int GetFacing(CVec d, int currentFacing)
{ {
return GetFacing(d.ToInt2(), currentFacing); return GetFacing(d.ToWVec(), currentFacing);
}
public static int GetFacing(int2 d, int currentFacing)
{
if (d == int2.Zero)
return currentFacing;
int highest = -1;
int highestDot = -1;
for( int i = 0 ; i < fvecs.Length ; i++ )
{
int dot = int2.Dot( fvecs[ i ], d );
if( dot > highestDot )
{
highestDot = dot;
highest = i;
}
}
return highest * 8;
} }
public static int GetNearestFacing(int facing, int desiredFacing) public static int GetNearestFacing(int facing, int desiredFacing)
@@ -84,31 +65,11 @@ namespace OpenRA.Traits
return a / step; return a / step;
} }
public static float2 RotateVectorByFacing(float2 v, int facing, float ecc)
{
var angle = (facing / 256f) * (2 * (float)Math.PI);
var sinAngle = (float)Math.Sin(angle);
var cosAngle = (float)Math.Cos(angle);
return new float2(
(cosAngle * v.X + sinAngle * v.Y),
ecc * (cosAngle * v.Y - sinAngle * v.X));
}
public static PPos CenterOfCell(CPos loc)
{
return loc.ToPPos() + new PVecInt(Game.CellSize / 2, Game.CellSize / 2);
}
public static WPos BetweenCells(CPos from, CPos to) public static WPos BetweenCells(CPos from, CPos to)
{ {
return WPos.Lerp(from.CenterPosition, to.CenterPosition, 1, 2); return WPos.Lerp(from.CenterPosition, to.CenterPosition, 1, 2);
} }
public static int2 AsInt2(this int[] xs) { return new int2(xs[0], xs[1]); }
public static float2 RelOffset(this int[] offset) { return new float2(offset[0], offset[1]); }
public static float2 AbsOffset(this int[] offset) { return new float2(offset.ElementAtOrDefault(2), offset.ElementAtOrDefault(3)); }
public static Activity SequenceActivities(params Activity[] acts) public static Activity SequenceActivities(params Activity[] acts)
{ {
return acts.Reverse().Aggregate( return acts.Reverse().Aggregate(
@@ -130,14 +91,10 @@ namespace OpenRA.Traits
if (prev == act) if (prev == act)
break; break;
} }
return act; return act;
} }
public static Color ArrayToColor(int[] x) { return Color.FromArgb(x[0], x[1], x[2]); }
[Obsolete("Use ToCPos() method", true)]
public static int2 CellContaining(float2 pos) { return (1f / Game.CellSize * pos).ToInt2(); }
/* pretty crap */ /* pretty crap */
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> ts, Thirdparty.Random random) public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> ts, Thirdparty.Random random)
{ {
@@ -186,302 +143,5 @@ namespace OpenRA.Traits
return Util.ExpandFootprint(cells, true); return Util.ExpandFootprint(cells, true);
} }
static int2[] fvecs =
{
new int2( 0, -1331 ),
new int2( -199, -1305 ),
new int2( -391, -1229 ),
new int2( -568, -1106 ),
new int2( -724, -941 ),
new int2( -851, -739 ),
new int2( -946, -509 ),
new int2( -1004, -259 ),
new int2( -1024, 0 ),
new int2( -1004, 259 ),
new int2( -946, 509 ),
new int2( -851, 739 ),
new int2( -724, 941 ),
new int2( -568, 1106 ),
new int2( -391, 1229 ),
new int2( -199, 1305 ),
new int2( 0, 1331 ),
new int2( 199, 1305 ),
new int2( 391, 1229 ),
new int2( 568, 1106 ),
new int2( 724, 941 ),
new int2( 851, 739 ),
new int2( 946, 509 ),
new int2( 1004, 259 ),
new int2( 1024, 0 ),
new int2( 1004, -259 ),
new int2( 946, -509 ),
new int2( 851, -739 ),
new int2( 724, -941 ),
new int2( 568, -1106 ),
new int2( 391, -1229 ),
new int2( 199, -1305 )
};
public static readonly PSubVec[] SubPxVector =
{
new PSubVec( 0, 1024 ),
new PSubVec( 25, 1023 ),
new PSubVec( 50, 1022 ),
new PSubVec( 75, 1021 ),
new PSubVec( 100, 1019 ),
new PSubVec( 125, 1016 ),
new PSubVec( 150, 1012 ),
new PSubVec( 175, 1008 ),
new PSubVec( 199, 1004 ),
new PSubVec( 224, 999 ),
new PSubVec( 248, 993 ),
new PSubVec( 273, 986 ),
new PSubVec( 297, 979 ),
new PSubVec( 321, 972 ),
new PSubVec( 344, 964 ),
new PSubVec( 368, 955 ),
new PSubVec( 391, 946 ),
new PSubVec( 414, 936 ),
new PSubVec( 437, 925 ),
new PSubVec( 460, 914 ),
new PSubVec( 482, 903 ),
new PSubVec( 504, 890 ),
new PSubVec( 526, 878 ),
new PSubVec( 547, 865 ),
new PSubVec( 568, 851 ),
new PSubVec( 589, 837 ),
new PSubVec( 609, 822 ),
new PSubVec( 629, 807 ),
new PSubVec( 649, 791 ),
new PSubVec( 668, 775 ),
new PSubVec( 687, 758 ),
new PSubVec( 706, 741 ),
new PSubVec( 724, 724 ),
new PSubVec( 741, 706 ),
new PSubVec( 758, 687 ),
new PSubVec( 775, 668 ),
new PSubVec( 791, 649 ),
new PSubVec( 807, 629 ),
new PSubVec( 822, 609 ),
new PSubVec( 837, 589 ),
new PSubVec( 851, 568 ),
new PSubVec( 865, 547 ),
new PSubVec( 878, 526 ),
new PSubVec( 890, 504 ),
new PSubVec( 903, 482 ),
new PSubVec( 914, 460 ),
new PSubVec( 925, 437 ),
new PSubVec( 936, 414 ),
new PSubVec( 946, 391 ),
new PSubVec( 955, 368 ),
new PSubVec( 964, 344 ),
new PSubVec( 972, 321 ),
new PSubVec( 979, 297 ),
new PSubVec( 986, 273 ),
new PSubVec( 993, 248 ),
new PSubVec( 999, 224 ),
new PSubVec( 1004, 199 ),
new PSubVec( 1008, 175 ),
new PSubVec( 1012, 150 ),
new PSubVec( 1016, 125 ),
new PSubVec( 1019, 100 ),
new PSubVec( 1021, 75 ),
new PSubVec( 1022, 50 ),
new PSubVec( 1023, 25 ),
new PSubVec( 1024, 0 ),
new PSubVec( 1023, -25 ),
new PSubVec( 1022, -50 ),
new PSubVec( 1021, -75 ),
new PSubVec( 1019, -100 ),
new PSubVec( 1016, -125 ),
new PSubVec( 1012, -150 ),
new PSubVec( 1008, -175 ),
new PSubVec( 1004, -199 ),
new PSubVec( 999, -224 ),
new PSubVec( 993, -248 ),
new PSubVec( 986, -273 ),
new PSubVec( 979, -297 ),
new PSubVec( 972, -321 ),
new PSubVec( 964, -344 ),
new PSubVec( 955, -368 ),
new PSubVec( 946, -391 ),
new PSubVec( 936, -414 ),
new PSubVec( 925, -437 ),
new PSubVec( 914, -460 ),
new PSubVec( 903, -482 ),
new PSubVec( 890, -504 ),
new PSubVec( 878, -526 ),
new PSubVec( 865, -547 ),
new PSubVec( 851, -568 ),
new PSubVec( 837, -589 ),
new PSubVec( 822, -609 ),
new PSubVec( 807, -629 ),
new PSubVec( 791, -649 ),
new PSubVec( 775, -668 ),
new PSubVec( 758, -687 ),
new PSubVec( 741, -706 ),
new PSubVec( 724, -724 ),
new PSubVec( 706, -741 ),
new PSubVec( 687, -758 ),
new PSubVec( 668, -775 ),
new PSubVec( 649, -791 ),
new PSubVec( 629, -807 ),
new PSubVec( 609, -822 ),
new PSubVec( 589, -837 ),
new PSubVec( 568, -851 ),
new PSubVec( 547, -865 ),
new PSubVec( 526, -878 ),
new PSubVec( 504, -890 ),
new PSubVec( 482, -903 ),
new PSubVec( 460, -914 ),
new PSubVec( 437, -925 ),
new PSubVec( 414, -936 ),
new PSubVec( 391, -946 ),
new PSubVec( 368, -955 ),
new PSubVec( 344, -964 ),
new PSubVec( 321, -972 ),
new PSubVec( 297, -979 ),
new PSubVec( 273, -986 ),
new PSubVec( 248, -993 ),
new PSubVec( 224, -999 ),
new PSubVec( 199, -1004 ),
new PSubVec( 175, -1008 ),
new PSubVec( 150, -1012 ),
new PSubVec( 125, -1016 ),
new PSubVec( 100, -1019 ),
new PSubVec( 75, -1021 ),
new PSubVec( 50, -1022 ),
new PSubVec( 25, -1023 ),
new PSubVec( 0, -1024 ),
new PSubVec( -25, -1023 ),
new PSubVec( -50, -1022 ),
new PSubVec( -75, -1021 ),
new PSubVec( -100, -1019 ),
new PSubVec( -125, -1016 ),
new PSubVec( -150, -1012 ),
new PSubVec( -175, -1008 ),
new PSubVec( -199, -1004 ),
new PSubVec( -224, -999 ),
new PSubVec( -248, -993 ),
new PSubVec( -273, -986 ),
new PSubVec( -297, -979 ),
new PSubVec( -321, -972 ),
new PSubVec( -344, -964 ),
new PSubVec( -368, -955 ),
new PSubVec( -391, -946 ),
new PSubVec( -414, -936 ),
new PSubVec( -437, -925 ),
new PSubVec( -460, -914 ),
new PSubVec( -482, -903 ),
new PSubVec( -504, -890 ),
new PSubVec( -526, -878 ),
new PSubVec( -547, -865 ),
new PSubVec( -568, -851 ),
new PSubVec( -589, -837 ),
new PSubVec( -609, -822 ),
new PSubVec( -629, -807 ),
new PSubVec( -649, -791 ),
new PSubVec( -668, -775 ),
new PSubVec( -687, -758 ),
new PSubVec( -706, -741 ),
new PSubVec( -724, -724 ),
new PSubVec( -741, -706 ),
new PSubVec( -758, -687 ),
new PSubVec( -775, -668 ),
new PSubVec( -791, -649 ),
new PSubVec( -807, -629 ),
new PSubVec( -822, -609 ),
new PSubVec( -837, -589 ),
new PSubVec( -851, -568 ),
new PSubVec( -865, -547 ),
new PSubVec( -878, -526 ),
new PSubVec( -890, -504 ),
new PSubVec( -903, -482 ),
new PSubVec( -914, -460 ),
new PSubVec( -925, -437 ),
new PSubVec( -936, -414 ),
new PSubVec( -946, -391 ),
new PSubVec( -955, -368 ),
new PSubVec( -964, -344 ),
new PSubVec( -972, -321 ),
new PSubVec( -979, -297 ),
new PSubVec( -986, -273 ),
new PSubVec( -993, -248 ),
new PSubVec( -999, -224 ),
new PSubVec( -1004, -199 ),
new PSubVec( -1008, -175 ),
new PSubVec( -1012, -150 ),
new PSubVec( -1016, -125 ),
new PSubVec( -1019, -100 ),
new PSubVec( -1021, -75 ),
new PSubVec( -1022, -50 ),
new PSubVec( -1023, -25 ),
new PSubVec( -1024, 0 ),
new PSubVec( -1023, 25 ),
new PSubVec( -1022, 50 ),
new PSubVec( -1021, 75 ),
new PSubVec( -1019, 100 ),
new PSubVec( -1016, 125 ),
new PSubVec( -1012, 150 ),
new PSubVec( -1008, 175 ),
new PSubVec( -1004, 199 ),
new PSubVec( -999, 224 ),
new PSubVec( -993, 248 ),
new PSubVec( -986, 273 ),
new PSubVec( -979, 297 ),
new PSubVec( -972, 321 ),
new PSubVec( -964, 344 ),
new PSubVec( -955, 368 ),
new PSubVec( -946, 391 ),
new PSubVec( -936, 414 ),
new PSubVec( -925, 437 ),
new PSubVec( -914, 460 ),
new PSubVec( -903, 482 ),
new PSubVec( -890, 504 ),
new PSubVec( -878, 526 ),
new PSubVec( -865, 547 ),
new PSubVec( -851, 568 ),
new PSubVec( -837, 589 ),
new PSubVec( -822, 609 ),
new PSubVec( -807, 629 ),
new PSubVec( -791, 649 ),
new PSubVec( -775, 668 ),
new PSubVec( -758, 687 ),
new PSubVec( -741, 706 ),
new PSubVec( -724, 724 ),
new PSubVec( -706, 741 ),
new PSubVec( -687, 758 ),
new PSubVec( -668, 775 ),
new PSubVec( -649, 791 ),
new PSubVec( -629, 807 ),
new PSubVec( -609, 822 ),
new PSubVec( -589, 837 ),
new PSubVec( -568, 851 ),
new PSubVec( -547, 865 ),
new PSubVec( -526, 878 ),
new PSubVec( -504, 890 ),
new PSubVec( -482, 903 ),
new PSubVec( -460, 914 ),
new PSubVec( -437, 925 ),
new PSubVec( -414, 936 ),
new PSubVec( -391, 946 ),
new PSubVec( -368, 955 ),
new PSubVec( -344, 964 ),
new PSubVec( -321, 972 ),
new PSubVec( -297, 979 ),
new PSubVec( -273, 986 ),
new PSubVec( -248, 993 ),
new PSubVec( -224, 999 ),
new PSubVec( -199, 1004 ),
new PSubVec( -175, 1008 ),
new PSubVec( -150, 1012 ),
new PSubVec( -125, 1016 ),
new PSubVec( -100, 1019 ),
new PSubVec( -75, 1021 ),
new PSubVec( -50, 1022 ),
new PSubVec( -25, 1023 )
};
} }
} }

View File

@@ -138,19 +138,6 @@ namespace OpenRA
r.Next(w.Map.Bounds.Top, w.Map.Bounds.Bottom)); r.Next(w.Map.Bounds.Top, w.Map.Bounds.Bottom));
} }
public static float Gauss1D(this Thirdparty.Random r, int samples)
{
return Exts.MakeArray(samples, _ => r.NextFloat() * 2 - 1f)
.Sum() / samples;
}
// Returns a random offset in the range [-1..1,-1..1] with a separable
// Gauss distribution with 'samples' values taken for each axis
public static float2 Gauss2D(this Thirdparty.Random r, int samples)
{
return new float2(Gauss1D(r, samples), Gauss1D(r, samples));
}
public static bool HasVoices(this Actor a) public static bool HasVoices(this Actor a)
{ {
var selectable = a.Info.Traits.GetOrDefault<SelectableInfo>(); var selectable = a.Info.Traits.GetOrDefault<SelectableInfo>();

View File

@@ -1,4 +1,4 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS) * Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * This file is part of OpenRA, which is free software. It is made
@@ -121,26 +121,20 @@ namespace OpenRA.Mods.RA
var barrel = Barrels[Burst % Barrels.Length]; var barrel = Barrels[Burst % Barrels.Length];
var muzzlePosition = self.CenterPosition + MuzzleOffset(self, barrel); var muzzlePosition = self.CenterPosition + MuzzleOffset(self, barrel);
var legacyMuzzlePosition = PPos.FromWPos(muzzlePosition);
var legacyMuzzleAltitude = Game.CellSize*muzzlePosition.Z/1024;
var legacyFacing = MuzzleOrientation(self, barrel).Yaw.Angle / 4; var legacyFacing = MuzzleOrientation(self, barrel).Yaw.Angle / 4;
var args = new ProjectileArgs var args = new ProjectileArgs
{ {
weapon = Weapon, weapon = Weapon,
firedBy = self,
target = target,
src = legacyMuzzlePosition,
srcAltitude = legacyMuzzleAltitude,
dest = PPos.FromWPos(target.CenterPosition),
destAltitude = target.CenterPosition.Z * Game.CellSize / 1024,
facing = legacyFacing, facing = legacyFacing,
firepowerModifier = self.TraitsImplementing<IFirepowerModifier>() firepowerModifier = self.TraitsImplementing<IFirepowerModifier>()
.Select(a => a.GetFirepowerModifier()) .Select(a => a.GetFirepowerModifier())
.Product() .Product(),
source = muzzlePosition,
sourceActor = self,
passiveTarget = target.CenterPosition,
guidedTarget = target
}; };
attack.ScheduleDelayedAction(Info.FireDelay, () => attack.ScheduleDelayedAction(Info.FireDelay, () =>

View File

@@ -31,8 +31,8 @@ namespace OpenRA.Mods.RA
public void TickIdle(Actor self) public void TickIdle(Actor self)
{ {
var target = (Util.SubPxVector[self.World.SharedRandom.Next(255)] * Info.MoveRadius).ToPVecInt().ToCVec() + self.Location; var offset = new WVec(0, -1024*Info.MoveRadius, 0).Rotate(WRot.FromFacing(self.World.SharedRandom.Next(255))).ToCVec();
self.Trait<AttackMove>().ResolveOrder(self, new Order("AttackMove", self, false) { TargetLocation = target }); self.Trait<AttackMove>().ResolveOrder(self, new Order("AttackMove", self, false) { TargetLocation = self.Location + offset });
} }
} }
} }

View File

@@ -56,17 +56,15 @@ namespace OpenRA.Mods.RA
var weapon = Rules.Weapons[info.Weapon.ToLowerInvariant()]; var weapon = Rules.Weapons[info.Weapon.ToLowerInvariant()];
dropDelay = weapon.ROF; dropDelay = weapon.ROF;
var centerLocation = PPos.FromWPos(self.CenterPosition); var pos = self.CenterPosition;
var altitude = self.CenterPosition.Z * Game.CellSize / 1024;
var args = new ProjectileArgs var args = new ProjectileArgs
{ {
srcAltitude = altitude, weapon = weapon,
destAltitude = 0,
src = centerLocation,
dest = centerLocation,
facing = self.Trait<IFacing>().Facing, facing = self.Trait<IFacing>().Facing,
firedBy = self,
weapon = weapon source = pos,
sourceActor = self,
passiveTarget = pos - new WVec(0, 0, pos.Z)
}; };
self.World.Add(args.weapon.Projectile.Create(args)); self.World.Add(args.weapon.Projectile.Create(args));

View File

@@ -32,23 +32,21 @@ namespace OpenRA.Mods.RA
return null; return null;
} }
public static void DoImpact(WarheadInfo warhead, ProjectileArgs args) public static void DoImpact(WPos pos, WarheadInfo warhead, WeaponInfo weapon, Actor firedBy, float firepowerModifier)
{ {
var world = args.firedBy.World; var world = firedBy.World;
var targetTile = args.dest.ToCPos(); var targetTile = pos.ToCPos();
if (!world.Map.IsInMap(targetTile)) if (!world.Map.IsInMap(targetTile))
return; return;
var isWater = args.destAltitude == 0 && world.GetTerrainInfo(targetTile).IsWater; var isWater = pos.Z == 0 && world.GetTerrainInfo(targetTile).IsWater;
var explosionType = isWater ? warhead.WaterExplosion : warhead.Explosion; var explosionType = isWater ? warhead.WaterExplosion : warhead.Explosion;
var dest = args.dest.ToWPos(args.destAltitude);
if (explosionType != null) if (explosionType != null)
world.AddFrameEndTask( world.AddFrameEndTask(w => w.Add(new Explosion(w, pos, explosionType)));
w => w.Add(new Explosion(w, dest, explosionType)));
Sound.Play(GetImpactSound(warhead, isWater), dest); Sound.Play(GetImpactSound(warhead, isWater), pos);
var smudgeLayers = world.WorldActor.TraitsImplementing<SmudgeLayer>().ToDictionary(x => x.Info.Type); var smudgeLayers = world.WorldActor.TraitsImplementing<SmudgeLayer>().ToDictionary(x => x.Info.Type);
@@ -106,12 +104,12 @@ namespace OpenRA.Mods.RA
{ {
var maxSpread = warhead.Spread * (float)Math.Log(Math.Abs(warhead.Damage), 2); var maxSpread = warhead.Spread * (float)Math.Log(Math.Abs(warhead.Damage), 2);
var range = new WRange((int)maxSpread * 1024 / Game.CellSize); var range = new WRange((int)maxSpread * 1024 / Game.CellSize);
var hitActors = world.FindActorsInCircle(args.dest.ToWPos(0), range); var hitActors = world.FindActorsInCircle(pos, range);
foreach (var victim in hitActors) foreach (var victim in hitActors)
{ {
var damage = (int)GetDamageToInflict(victim, args, warhead, args.firepowerModifier); var damage = (int)GetDamageToInflict(pos, victim, warhead, weapon, firepowerModifier);
victim.InflictDamage(args.firedBy, damage, warhead); victim.InflictDamage(firedBy, damage, warhead);
} }
} break; } break;
@@ -119,23 +117,22 @@ namespace OpenRA.Mods.RA
{ {
foreach (var t in world.FindTilesInCircle(targetTile, warhead.Size[0])) foreach (var t in world.FindTilesInCircle(targetTile, warhead.Size[0]))
foreach (var unit in world.FindActorsInBox(t, t)) foreach (var unit in world.FindActorsInBox(t, t))
unit.InflictDamage(args.firedBy, unit.InflictDamage(firedBy,
(int)(warhead.Damage * warhead.EffectivenessAgainst(unit)), warhead); (int)(warhead.Damage * warhead.EffectivenessAgainst(unit)), warhead);
} break; } break;
} }
} }
public static void DoImpacts(ProjectileArgs args) public static void DoImpacts(WPos pos, Actor firedBy, WeaponInfo weapon, float damageModifier)
{ {
foreach (var warhead in args.weapon.Warheads) foreach (var wh in weapon.Warheads)
{ {
// NOTE(jsd): Fixed access to modified closure bug! var warhead = wh;
var warheadClosed = warhead; Action a = () => DoImpact(pos, warhead, weapon, firedBy, damageModifier);
Action a = () => DoImpact(warheadClosed, args);
if (warhead.Delay > 0) if (warhead.Delay > 0)
args.firedBy.World.AddFrameEndTask( firedBy.World.AddFrameEndTask(
w => w.Add(new DelayedAction(warheadClosed.Delay, a))); w => w.Add(new DelayedAction(warhead.Delay, a)));
else else
a(); a();
} }
@@ -143,24 +140,11 @@ namespace OpenRA.Mods.RA
public static void DoExplosion(Actor attacker, string weapontype, WPos pos) public static void DoExplosion(Actor attacker, string weapontype, WPos pos)
{ {
var pxPos = PPos.FromWPos(pos); var weapon = Rules.Weapons[weapontype.ToLowerInvariant()];
var altitude = pos.Z * Game.CellSize / 1024; if (weapon.Report != null && weapon.Report.Any())
var args = new ProjectileArgs Sound.Play(weapon.Report.Random(attacker.World.SharedRandom), pos);
{
src = pxPos,
dest = pxPos,
srcAltitude = altitude,
destAltitude = altitude,
firedBy = attacker,
target = Target.FromPos(pos),
weapon = Rules.Weapons[weapontype.ToLowerInvariant()],
facing = 0
};
if (args.weapon.Report != null && args.weapon.Report.Any()) DoImpacts(pos, attacker, weapon, 1f);
Sound.Play(args.weapon.Report.Random(attacker.World.SharedRandom), pos);
DoImpacts(args);
} }
static readonly float[] falloff = static readonly float[] falloff =
@@ -177,16 +161,16 @@ namespace OpenRA.Mods.RA
return (falloff[u] * (1 - t)) + (falloff[u + 1] * t); return (falloff[u] * (1 - t)) + (falloff[u + 1] * t);
} }
static float GetDamageToInflict(Actor target, ProjectileArgs args, WarheadInfo warhead, float modifier) static float GetDamageToInflict(WPos pos, Actor target, WarheadInfo warhead, WeaponInfo weapon, float modifier)
{ {
// don't hit air units with splash from ground explosions, etc // don't hit air units with splash from ground explosions, etc
if (!args.weapon.IsValidAgainst(target)) if (!weapon.IsValidAgainst(target))
return 0f; return 0f;
var health = target.Info.Traits.GetOrDefault<HealthInfo>(); var health = target.Info.Traits.GetOrDefault<HealthInfo>();
if( health == null ) return 0f; if( health == null ) return 0f;
var distance = (int)Math.Max(0, (target.CenterLocation - args.dest).Length - health.Radius); var distance = (int)Math.Max(0, (target.CenterPosition - pos).Length * Game.CellSize / 1024 - health.Radius);
var falloff = (float)GetDamageFalloff(distance / warhead.Spread); var falloff = (float)GetDamageFalloff(distance / warhead.Spread);
var rawDamage = (float)(warhead.Damage * modifier * falloff); var rawDamage = (float)(warhead.Damage * modifier * falloff);
var multiplier = (float)warhead.EffectivenessAgainst(target); var multiplier = (float)warhead.EffectivenessAgainst(target);

View File

@@ -1,4 +1,4 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS) * Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * This file is part of OpenRA, which is free software. It is made
@@ -8,6 +8,7 @@
*/ */
#endregion #endregion
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
@@ -28,12 +29,10 @@ namespace OpenRA.Mods.RA.Effects
public readonly string Image = null; public readonly string Image = null;
[Desc("Check for whether an actor with Wall: trait blocks fire")] [Desc("Check for whether an actor with Wall: trait blocks fire")]
public readonly bool High = false; public readonly bool High = false;
public readonly int RangeLimit = 0;
public readonly int Arm = 0;
public readonly bool Shadow = false; public readonly bool Shadow = false;
public readonly bool Proximity = false;
public readonly float Angle = 0; public readonly float Angle = 0;
public readonly int TrailInterval = 2; public readonly int TrailInterval = 2;
public readonly int TrailDelay = 1;
public readonly int ContrailLength = 0; public readonly int ContrailLength = 0;
public readonly Color ContrailColor = Color.White; public readonly Color ContrailColor = Color.White;
public readonly bool ContrailUsePlayerColor = false; public readonly bool ContrailUsePlayerColor = false;
@@ -44,147 +43,131 @@ namespace OpenRA.Mods.RA.Effects
public class Bullet : IEffect public class Bullet : IEffect
{ {
readonly BulletInfo Info; readonly BulletInfo info;
readonly ProjectileArgs Args; readonly ProjectileArgs args;
int t = 0; ContrailRenderable trail;
Animation anim; Animation anim;
WAngle angle;
const int BaseBulletSpeed = 100; /* pixels / 40ms frame */ WPos pos, target;
ContrailRenderable Trail; int length;
int facing;
int ticks, smokeTicks;
public Bullet(BulletInfo info, ProjectileArgs args) public Bullet(BulletInfo info, ProjectileArgs args)
{ {
Info = info; this.info = info;
Args = args; this.args = args;
this.pos = args.source;
// Convert ProjectileArg definitions to world coordinates
// TODO: Change the yaml definitions so we don't need this
var range = new WRange((int)(1024 * args.weapon.Range)); // Range in world units
var inaccuracy = new WRange((int)(info.Inaccuracy * 1024 / Game.CellSize)); // Offset in world units at max range
var speed = (int)(info.Speed * 4 * 1024 / (10 * Game.CellSize)); // Speed in world units per tick
angle = WAngle.ArcTan((int)(info.Angle * 4 * 1024), 1024); // Angle in world angle
target = args.passiveTarget;
if (info.Inaccuracy > 0) if (info.Inaccuracy > 0)
{ {
var factor = ((Args.dest - Args.src).ToCVec().Length) / args.weapon.Range; var maxOffset = inaccuracy.Range * (target - args.source).Length / range.Range;
Args.dest += (PVecInt) (info.Inaccuracy * factor * args.firedBy.World.SharedRandom.Gauss2D(2)).ToInt2(); target += WVec.FromPDF(args.sourceActor.World.SharedRandom, 2) * maxOffset / 1024;
Log.Write("debug", "Bullet with Inaccuracy; factor: #{0}; Projectile dest: {1}", factor, Args.dest);
} }
if (Info.Image != null) facing = Traits.Util.GetFacing(target - args.source, 0);
length = Math.Max((target - args.source).Length / speed, 1);
if (info.Image != null)
{ {
anim = new Animation(Info.Image, GetEffectiveFacing); anim = new Animation(info.Image, GetEffectiveFacing);
anim.PlayRepeating("idle"); anim.PlayRepeating("idle");
} }
if (Info.ContrailLength > 0) if (info.ContrailLength > 0)
{ {
var color = Info.ContrailUsePlayerColor ? ContrailRenderable.ChooseColor(args.firedBy) : Info.ContrailColor; var color = info.ContrailUsePlayerColor ? ContrailRenderable.ChooseColor(args.sourceActor) : info.ContrailColor;
Trail = new ContrailRenderable(args.firedBy.World, color, Info.ContrailLength, Info.ContrailDelay, 0); trail = new ContrailRenderable(args.sourceActor.World, color, info.ContrailLength, info.ContrailDelay, 0);
}
} }
int TotalTime() { return (Args.dest - Args.src).Length * BaseBulletSpeed / Info.Speed; } smokeTicks = info.TrailDelay;
float GetAltitude()
{
var at = (float)t / TotalTime();
return (Args.dest - Args.src).Length * Info.Angle * 4 * at * (1 - at);
} }
int GetEffectiveFacing() int GetEffectiveFacing()
{ {
var at = (float)t / TotalTime(); var at = (float)ticks / (length - 1);
var attitude = Info.Angle * (1 - 2 * at); var attitude = angle.Tan() * (1 - 2 * at) / (4 * 1024);
var rawFacing = Traits.Util.GetFacing(Args.dest - Args.src, 0); var u = (facing % 128) / 128f;
var u = (rawFacing % 128) / 128f;
var scale = 512 * u * (1 - u); var scale = 512 * u * (1 - u);
return (int)(rawFacing < 128 return (int)(facing < 128
? rawFacing - scale * attitude ? facing - scale * attitude
: rawFacing + scale * attitude); : facing + scale * attitude);
} }
int ticksToNextSmoke;
public void Tick(World world) public void Tick(World world)
{ {
t += 40; // Fade the trail out gradually
if (ticks > length && info.ContrailLength > 0)
if (anim != null) anim.Tick();
if (t > TotalTime()) Explode( world );
{ {
var at = (float)t / TotalTime(); trail.Update(pos);
var altitude = float2.Lerp(Args.srcAltitude, Args.destAltitude, at); return;
var pos = float2.Lerp(Args.src.ToFloat2(), Args.dest.ToFloat2(), at) - new float2(0, altitude);
var highPos = (Info.High || Info.Angle > 0)
? (pos - new float2(0, GetAltitude()))
: pos;
if (Info.Trail != null && --ticksToNextSmoke < 0)
{
world.AddFrameEndTask(w => w.Add(
new Smoke(w, ((PPos)highPos.ToInt2()).ToWPos(0), Info.Trail)));
ticksToNextSmoke = Info.TrailInterval;
} }
if (Info.ContrailLength > 0) if (anim != null)
anim.Tick();
pos = WPos.LerpQuadratic(args.source, target, angle, ticks, length);
if (info.Trail != null && --smokeTicks < 0)
{ {
var alt = (Info.High || Info.Angle > 0) ? GetAltitude() : 0; var delayedPos = WPos.LerpQuadratic(args.source, target, angle, ticks - info.TrailDelay, length);
Trail.Update(new PPos((int)pos.X, (int)pos.Y).ToWPos((int)alt)); world.AddFrameEndTask(w => w.Add(new Smoke(w, delayedPos, info.Trail)));
} smokeTicks = info.TrailInterval;
} }
if (!Info.High) // check for hitting a wall if (info.ContrailLength > 0)
{ trail.Update(pos);
var at = (float)t / TotalTime();
var pos = float2.Lerp(Args.src.ToFloat2(), Args.dest.ToFloat2(), at);
var cell = ((PPos) pos.ToInt2()).ToCPos();
if (world.ActorMap.GetUnitsAt(cell).Any( if (ticks++ >= length || (!info.High && world.ActorMap
a => a.HasTrait<IBlocksBullets>())) .GetUnitsAt(pos.ToCPos()).Any(a => a.HasTrait<IBlocksBullets>())))
{ {
Args.dest = (PPos) pos.ToInt2();
Explode(world); Explode(world);
} }
} }
}
const float height = .1f;
public IEnumerable<IRenderable> Render(WorldRenderer wr) public IEnumerable<IRenderable> Render(WorldRenderer wr)
{ {
if (Info.ContrailLength > 0) if (info.ContrailLength > 0)
yield return Trail; yield return trail;
if (anim != null) if (anim == null || ticks >= length)
yield break;
var cell = pos.ToCPos();
if (!args.sourceActor.World.FogObscures(cell))
{ {
var at = (float)t / TotalTime(); if (info.Shadow)
var altitude = float2.Lerp(Args.srcAltitude, Args.destAltitude, at);
var pos = float2.Lerp(Args.src.ToFloat2(), Args.dest.ToFloat2(), at) - new float2(0, altitude);
var cell = ((PPos)pos.ToInt2()).ToCPos();
if (!Args.firedBy.World.FogObscures(cell))
{ {
if (Info.High || Info.Angle > 0) var shadowPos = pos - new WVec(0, 0, pos.Z);
{ foreach (var r in anim.Render(shadowPos, wr.Palette("shadow")))
if (Info.Shadow) yield return r;
yield return new SpriteRenderable(anim.Image, pos, wr.Palette("shadow"), (int)pos.Y);
var highPos = pos - new float2(0, GetAltitude());
yield return new SpriteRenderable(anim.Image, highPos, wr.Palette("effect"), (int)pos.Y);
}
else
yield return new SpriteRenderable(anim.Image, pos,
wr.Palette(Args.weapon.Underwater ? "shadow" : "effect"), (int)pos.Y);
} }
var palette = wr.Palette(args.weapon.Underwater ? "shadow" : "effect");
foreach (var r in anim.Render(pos, palette))
yield return r;
} }
} }
void Explode(World world) void Explode(World world)
{ {
if (info.ContrailLength > 0)
world.AddFrameEndTask(w => w.Add(new DelayedAction(info.ContrailLength, () => w.Remove(this))));
else
world.AddFrameEndTask(w => w.Remove(this)); world.AddFrameEndTask(w => w.Remove(this));
Combat.DoImpacts(Args);
Combat.DoImpacts(target, args.sourceActor, args.weapon, args.firepowerModifier);
} }
} }
} }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #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 * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -19,19 +19,26 @@ namespace OpenRA.Mods.RA.Effects
public class GravityBombInfo : IProjectileInfo public class GravityBombInfo : IProjectileInfo
{ {
public readonly string Image = null; public readonly string Image = null;
public readonly WRange Velocity = WRange.Zero;
public readonly WRange Acceleration = new WRange(15);
public IEffect Create(ProjectileArgs args) { return new GravityBomb(this, args); } public IEffect Create(ProjectileArgs args) { return new GravityBomb(this, args); }
} }
public class GravityBomb : IEffect public class GravityBomb : IEffect
{ {
GravityBombInfo info;
Animation anim; Animation anim;
int altitude; ProjectileArgs args;
ProjectileArgs Args; WVec velocity;
WPos pos;
public GravityBomb(GravityBombInfo info, ProjectileArgs args) public GravityBomb(GravityBombInfo info, ProjectileArgs args)
{ {
Args = args; this.info = info;
altitude = args.srcAltitude; this.args = args;
pos = args.source;
velocity = new WVec(WRange.Zero, WRange.Zero, -info.Velocity);
anim = new Animation(info.Image); anim = new Animation(info.Image);
if (anim.HasSequence("open")) if (anim.HasSequence("open"))
@@ -42,10 +49,13 @@ namespace OpenRA.Mods.RA.Effects
public void Tick(World world) public void Tick(World world)
{ {
if (--altitude <= Args.destAltitude) velocity -= new WVec(WRange.Zero, WRange.Zero, info.Acceleration);
pos += velocity;
if (pos.Z <= args.passiveTarget.Z)
{ {
world.AddFrameEndTask(w => w.Remove(this)); world.AddFrameEndTask(w => w.Remove(this));
Combat.DoImpacts(Args); Combat.DoImpacts(args.passiveTarget, args.sourceActor, args.weapon, args.firepowerModifier);
} }
anim.Tick(); anim.Tick();
@@ -53,8 +63,7 @@ namespace OpenRA.Mods.RA.Effects
public IEnumerable<IRenderable> Render(WorldRenderer wr) public IEnumerable<IRenderable> Render(WorldRenderer wr)
{ {
var pos = Args.dest.ToInt2() - new int2(0, altitude) - .5f * anim.Image.size; return anim.Render(pos, wr.Palette("effect"));
yield return new SpriteRenderable(anim.Image, pos, wr.Palette("effect"), Args.dest.Y);
} }
} }
} }

View File

@@ -11,9 +11,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using OpenRA.Effects; using OpenRA.Effects;
using OpenRA.FileFormats;
using OpenRA.GameRules; using OpenRA.GameRules;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.FileFormats;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.RA.Effects namespace OpenRA.Mods.RA.Effects
@@ -29,7 +29,7 @@ namespace OpenRA.Mods.RA.Effects
public IEffect Create(ProjectileArgs args) public IEffect Create(ProjectileArgs args)
{ {
var c = UsePlayerColor ? args.firedBy.Owner.Color.RGB : Color; var c = UsePlayerColor ? args.sourceActor.Owner.Color.RGB : Color;
return new LaserZap(args, this, c); return new LaserZap(args, this, c);
} }
} }
@@ -41,13 +41,16 @@ namespace OpenRA.Mods.RA.Effects
int ticks = 0; int ticks = 0;
Color color; Color color;
bool doneDamage; bool doneDamage;
bool animationComplete;
Animation hitanim; Animation hitanim;
WPos target;
public LaserZap(ProjectileArgs args, LaserZapInfo info, Color color) public LaserZap(ProjectileArgs args, LaserZapInfo info, Color color)
{ {
this.args = args; this.args = args;
this.info = info; this.info = info;
this.color = color; this.color = color;
this.target = args.passiveTarget;
if (info.HitAnim != null) if (info.HitAnim != null)
this.hitanim = new Animation(info.HitAnim); this.hitanim = new Animation(info.HitAnim);
@@ -56,23 +59,22 @@ namespace OpenRA.Mods.RA.Effects
public void Tick(World world) public void Tick(World world)
{ {
// Beam tracks target // Beam tracks target
if (args.target.IsValid) if (args.guidedTarget.IsValid)
args.dest = args.target.CenterLocation; target = args.guidedTarget.CenterPosition;
if (!doneDamage) if (!doneDamage)
{ {
if (hitanim != null) if (hitanim != null)
hitanim.PlayThen("idle", hitanim.PlayThen("idle", () => animationComplete = true);
() => world.AddFrameEndTask(w => w.Remove(this)));
Combat.DoImpacts(args); Combat.DoImpacts(target, args.sourceActor, args.weapon, args.firepowerModifier);
doneDamage = true; doneDamage = true;
} }
++ticks;
if (hitanim != null) if (hitanim != null)
hitanim.Tick(); hitanim.Tick();
else
if (ticks >= info.BeamDuration) if (++ticks >= info.BeamDuration && animationComplete)
world.AddFrameEndTask(w => w.Remove(this)); world.AddFrameEndTask(w => w.Remove(this));
} }
@@ -80,19 +82,13 @@ namespace OpenRA.Mods.RA.Effects
{ {
if (ticks < info.BeamDuration) if (ticks < info.BeamDuration)
{ {
var src = new PPos(args.src.X, args.src.Y).ToWPos(args.srcAltitude);
var dest = new PPos(args.dest.X, args.dest.Y).ToWPos(args.destAltitude);
var rc = Color.FromArgb((info.BeamDuration - ticks) * 255 / info.BeamDuration, color); var rc = Color.FromArgb((info.BeamDuration - ticks) * 255 / info.BeamDuration, color);
yield return new BeamRenderable(args.source, 0, target - args.source, info.BeamWidth, rc);
yield return new BeamRenderable(src, 0, dest - src, info.BeamWidth, rc);
} }
if (hitanim != null) if (hitanim != null)
yield return new SpriteRenderable(hitanim.Image, args.dest.ToFloat2(), foreach (var r in hitanim.Render(target, wr.Palette("effect")))
wr.Palette("effect"), (int)args.dest.Y); yield return r;
if (ticks >= info.BeamDuration)
yield break;
} }
} }
} }

View File

@@ -1,4 +1,4 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS) * Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * This file is part of OpenRA, which is free software. It is made
@@ -23,11 +23,10 @@ namespace OpenRA.Mods.RA.Effects
class MissileInfo : IProjectileInfo class MissileInfo : IProjectileInfo
{ {
public readonly int Speed = 1; public readonly int Speed = 1;
public readonly WAngle MaximumPitch = WAngle.FromDegrees(30);
public readonly int Arm = 0; public readonly int Arm = 0;
[Desc("Check for whether an actor with Wall: trait blocks fire")] [Desc("Check for whether an actor with Wall: trait blocks fire")]
public readonly bool High = false; public readonly bool High = false;
public readonly bool Shadow = true;
public readonly bool Proximity = false;
public readonly string Trail = null; public readonly string Trail = null;
public readonly float Inaccuracy = 0; public readonly float Inaccuracy = 0;
public readonly string Image = null; public readonly string Image = null;
@@ -48,139 +47,155 @@ namespace OpenRA.Mods.RA.Effects
class Missile : IEffect class Missile : IEffect
{ {
readonly MissileInfo Info; readonly MissileInfo info;
readonly ProjectileArgs Args; readonly ProjectileArgs args;
PVecInt offset;
public PSubPos SubPxPosition;
public PPos PxPosition { get { return SubPxPosition.ToPPos(); } }
readonly Animation anim; readonly Animation anim;
int Facing;
int t; ContrailRenderable trail;
int Altitude; WPos pos;
ContrailRenderable Trail; int facing;
WPos target;
WVec offset;
int ticks;
bool exploded;
readonly int speed;
public Missile(MissileInfo info, ProjectileArgs args) public Missile(MissileInfo info, ProjectileArgs args)
{ {
Info = info; this.info = info;
Args = args; this.args = args;
SubPxPosition = Args.src.ToPSubPos(); pos = args.source;
Altitude = Args.srcAltitude; facing = args.facing;
Facing = Args.facing;
target = args.passiveTarget;
// Convert ProjectileArg definitions to world coordinates
// TODO: Change the yaml definitions so we don't need this
var inaccuracy = (int)(info.Inaccuracy * 1024 / Game.CellSize);
speed = info.Speed * 1024 / (5 * Game.CellSize);
if (info.Inaccuracy > 0) if (info.Inaccuracy > 0)
offset = (PVecInt)(info.Inaccuracy * args.firedBy.World.SharedRandom.Gauss2D(2)).ToInt2(); offset = WVec.FromPDF(args.sourceActor.World.SharedRandom, 2) * inaccuracy / 1024;
if (Info.Image != null) if (info.Image != null)
{ {
anim = new Animation(Info.Image, () => Facing); anim = new Animation(info.Image, () => facing);
anim.PlayRepeating("idle"); anim.PlayRepeating("idle");
} }
if (Info.ContrailLength > 0) if (info.ContrailLength > 0)
{ {
var color = Info.ContrailUsePlayerColor ? ContrailRenderable.ChooseColor(args.firedBy) : Info.ContrailColor; var color = info.ContrailUsePlayerColor ? ContrailRenderable.ChooseColor(args.sourceActor) : info.ContrailColor;
Trail = new ContrailRenderable(args.firedBy.World, color, Info.ContrailLength, Info.ContrailDelay, 0); trail = new ContrailRenderable(args.sourceActor.World, color, info.ContrailLength, info.ContrailDelay, 0);
} }
} }
// In pixels static readonly WRange MissileCloseEnough = new WRange(7 * 1024 / Game.CellSize);
const int MissileCloseEnough = 7;
int ticksToNextSmoke; int ticksToNextSmoke;
bool JammedBy(TraitPair<JamsMissiles> tp)
{
if ((tp.Actor.CenterPosition - pos).HorizontalLengthSquared > tp.Trait.Range * tp.Trait.Range)
return false;
if (tp.Actor.Owner.Stances[args.sourceActor.Owner] == Stance.Ally && !tp.Trait.AlliedMissiles)
return false;
return tp.Actor.World.SharedRandom.Next(100 / tp.Trait.Chance) == 0;
}
public void Tick(World world) public void Tick(World world)
{ {
t += 40; // Fade the trail out gradually
if (exploded && info.ContrailLength > 0)
// In pixels
var dist = Args.target.CenterLocation + offset - PxPosition;
var targetAltitude = 0;
if (Args.target.IsValid)
targetAltitude = Args.target.CenterPosition.Z * Game.CellSize / 1024;
var jammed = Info.Jammable && world.ActorsWithTrait<JamsMissiles>().Any(tp =>
(tp.Actor.CenterLocation - PxPosition).ToCVec().Length <= tp.Trait.Range
&& (tp.Actor.Owner.Stances[Args.firedBy.Owner] != Stance.Ally
|| (tp.Actor.Owner.Stances[Args.firedBy.Owner] == Stance.Ally && tp.Trait.AlliedMissiles))
&& world.SharedRandom.Next(100 / tp.Trait.Chance) == 0);
if (!jammed)
{ {
Altitude += Math.Sign(targetAltitude - Altitude); trail.Update(pos);
if (Args.target.IsValid) return;
Facing = Traits.Util.TickFacing(Facing,
Traits.Util.GetFacing(dist, Facing),
Info.ROT);
}
else
{
Altitude += world.SharedRandom.Next(-1, 2);
Facing = Traits.Util.TickFacing(Facing,
Facing + world.SharedRandom.Next(-20, 21),
Info.ROT);
} }
ticks++;
anim.Tick(); anim.Tick();
if (dist.LengthSquared < MissileCloseEnough * MissileCloseEnough && Args.target.IsValid) // Missile tracks target
Explode(world); if (args.guidedTarget.IsValid)
target = args.guidedTarget.CenterPosition;
// TODO: Replace this with a lookup table var dist = target + offset - pos;
var dir = (-float2.FromAngle((float)(Facing / 128f * Math.PI))).ToPSubVec(); var desiredFacing = Traits.Util.GetFacing(dist, facing);
var desiredAltitude = target.Z;
var jammed = info.Jammable && world.ActorsWithTrait<JamsMissiles>().Any(j => JammedBy(j));
var move = Info.Speed * dir; if (jammed)
if (targetAltitude > 0 && Info.TurboBoost) {
desiredFacing = facing + world.SharedRandom.Next(-20, 21);
desiredAltitude = world.SharedRandom.Next(-43, 86);
}
else if (!args.guidedTarget.IsValid)
desiredFacing = facing;
facing = Traits.Util.TickFacing(facing, desiredFacing, info.ROT);
var move = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(facing)) * speed / 1024;
if (target.Z > 0 && info.TurboBoost)
move = (move * 3) / 2; move = (move * 3) / 2;
move = move / 5;
SubPxPosition += move; if (pos.Z != desiredAltitude)
if (Info.Trail != null)
{ {
var sp = ((SubPxPosition - (move * 3) / 2)).ToPPos() - new PVecInt(0, Altitude); var delta = move.HorizontalLength * info.MaximumPitch.Tan() / 1024;
var dz = (target.Z - pos.Z).Clamp(-delta, delta);
if (--ticksToNextSmoke < 0) move += new WVec(0, 0, dz);
{
world.AddFrameEndTask(w => w.Add(new Smoke(w, sp.ToWPos(0), Info.Trail)));
ticksToNextSmoke = Info.TrailInterval;
}
} }
if (Info.RangeLimit != 0 && t > Info.RangeLimit * 40) pos += move;
if (info.Trail != null && --ticksToNextSmoke < 0)
{
world.AddFrameEndTask(w => w.Add(new Smoke(w, pos - 3 * move / 2, info.Trail)));
ticksToNextSmoke = info.TrailInterval;
}
if (info.ContrailLength > 0)
trail.Update(pos);
var shouldExplode = pos.Z < 0 // Hit the ground
|| dist.LengthSquared < MissileCloseEnough.Range * MissileCloseEnough.Range // Within range
|| info.RangeLimit != 0 && ticks > info.RangeLimit // Ran out of fuel
|| (!info.High && world.ActorMap.GetUnitsAt(pos.ToCPos())
.Any(a => a.HasTrait<IBlocksBullets>())); // Hit a wall
if (shouldExplode)
Explode(world); Explode(world);
if (!Info.High) // check for hitting a wall
{
var cell = PxPosition.ToCPos();
if (world.ActorMap.GetUnitsAt(cell).Any(a => a.HasTrait<IBlocksBullets>()))
Explode(world);
}
if (Info.ContrailLength > 0)
Trail.Update(PxPosition.ToWPos(Altitude));
} }
void Explode(World world) void Explode(World world)
{ {
exploded = true;
if (info.ContrailLength > 0)
world.AddFrameEndTask(w => w.Add(new DelayedAction(info.ContrailLength, () => w.Remove(this))));
else
world.AddFrameEndTask(w => w.Remove(this)); world.AddFrameEndTask(w => w.Remove(this));
Args.dest = PxPosition;
if (t > Info.Arm * 40) /* don't blow up in our launcher's face! */ // Don't blow up in our launcher's face!
Combat.DoImpacts(Args); if (ticks <= info.Arm)
return;
Combat.DoImpacts(pos, args.sourceActor, args.weapon, args.firepowerModifier);
} }
public IEnumerable<IRenderable> Render(WorldRenderer wr) public IEnumerable<IRenderable> Render(WorldRenderer wr)
{ {
if (Info.ContrailLength > 0) if (info.ContrailLength > 0)
yield return Trail; yield return trail;
if (!Args.firedBy.World.FogObscures(PxPosition.ToCPos())) if (!args.sourceActor.World.FogObscures(pos.ToCPos()))
yield return new SpriteRenderable(anim.Image, PxPosition.ToFloat2() - new float2(0, Altitude), {
wr.Palette(Args.weapon.Underwater ? "shadow" : "effect"), PxPosition.Y); var palette = wr.Palette(args.weapon.Underwater ? "shadow" : "effect");
foreach (var r in anim.Render(pos, palette))
yield return r;
}
} }
} }
} }

View File

@@ -1,4 +1,4 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS) * Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * This file is part of OpenRA, which is free software. It is made
@@ -30,7 +30,7 @@ namespace OpenRA.Mods.RA.Effects
{ {
readonly ProjectileArgs Args; readonly ProjectileArgs Args;
readonly TeslaZapInfo Info; readonly TeslaZapInfo Info;
IEnumerable<IRenderable> renderables; TeslaZapRenderable zap;
int timeUntilRemove = 2; // # of frames int timeUntilRemove = 2; // # of frames
bool doneDamage = false; bool doneDamage = false;
bool initialized = false; bool initialized = false;
@@ -41,33 +41,15 @@ namespace OpenRA.Mods.RA.Effects
Info = info; Info = info;
} }
public IEnumerable<IRenderable> GenerateRenderables(WorldRenderer wr)
{
var bright = SequenceProvider.GetSequence(Info.Image, "bright");
var dim = SequenceProvider.GetSequence(Info.Image, "dim");
var src = new PPos(Args.src.X, Args.src.Y - Args.srcAltitude);
var dest = new PPos(Args.dest.X, Args.dest.Y - Args.destAltitude);
for (var n = 0; n < Info.DimZaps; n++)
foreach (var z in DrawZapWandering(wr, src, dest, dim))
yield return z;
for (var n = 0; n < Info.BrightZaps; n++)
foreach (var z in DrawZapWandering(wr, src, dest, bright))
yield return z;
}
public void Tick(World world) public void Tick(World world)
{ {
if (timeUntilRemove <= 0) if (timeUntilRemove-- <= 0)
world.AddFrameEndTask(w => w.Remove(this)); world.AddFrameEndTask(w => w.Remove(this));
--timeUntilRemove;
if (!doneDamage) if (!doneDamage)
{ {
if (Args.target.IsValid) var pos = Args.guidedTarget.IsValid ? Args.guidedTarget.CenterPosition : Args.passiveTarget;
Args.dest = Args.target.CenterLocation; Combat.DoImpacts(pos, Args.sourceActor, Args.weapon, Args.firepowerModifier);
Combat.DoImpacts(Args);
doneDamage = true; doneDamage = true;
} }
} }
@@ -76,75 +58,10 @@ namespace OpenRA.Mods.RA.Effects
{ {
if (!initialized) if (!initialized)
{ {
renderables = GenerateRenderables(wr); var pos = Args.guidedTarget.IsValid ? Args.guidedTarget.CenterPosition : Args.passiveTarget;
initialized = true; zap = new TeslaZapRenderable(Args.source, 0, pos - Args.source, Info.Image, Info.BrightZaps, Info.DimZaps);
}
yield return zap;
} }
return renderables;
}
static IEnumerable<IRenderable> DrawZapWandering(WorldRenderer wr, PPos from, PPos to, Sequence s)
{
var z = float2.Zero; /* hack */
var dist = to - from;
var norm = (1f / dist.Length) * new float2(-dist.Y, dist.X);
var renderables = new List<IRenderable>();
if (Game.CosmeticRandom.Next(2) != 0)
{
var p1 = from.ToFloat2() + (1 / 3f) * dist.ToFloat2() + Game.CosmeticRandom.Gauss1D(1) * .2f * dist.Length * norm;
var p2 = from.ToFloat2() + (2 / 3f) * dist.ToFloat2() + Game.CosmeticRandom.Gauss1D(1) * .2f * dist.Length * norm;
renderables.AddRange(DrawZap(wr, from.ToFloat2(), p1, s, out p1));
renderables.AddRange(DrawZap(wr, p1, p2, s, out p2));
renderables.AddRange(DrawZap(wr, p2, to.ToFloat2(), s, out z));
}
else
{
var p1 = from.ToFloat2() + (1 / 2f) * dist.ToFloat2() + Game.CosmeticRandom.Gauss1D(1) * .2f * dist.Length * norm;
renderables.AddRange(DrawZap(wr, from.ToFloat2(), p1, s, out p1));
renderables.AddRange(DrawZap(wr, p1, to.ToFloat2(), s, out z));
}
return renderables;
}
static IEnumerable<IRenderable> DrawZap(WorldRenderer wr, float2 from, float2 to, Sequence s, out float2 p)
{
var dist = to - from;
var q = new float2(-dist.Y, dist.X);
var c = -float2.Dot(from, q);
var rs = new List<IRenderable>();
var z = from;
while ((to - z).X > 5 || (to - z).X < -5 || (to - z).Y > 5 || (to - z).Y < -5)
{
var step = steps.Where(t => (to - (z + new float2(t[0],t[1]))).LengthSquared < (to - z).LengthSquared )
.OrderBy(t => Math.Abs(float2.Dot(z + new float2(t[0], t[1]), q) + c)).First();
rs.Add(new SpriteRenderable(s.GetSprite(step[4]), z + new float2(step[2], step[3]),
wr.Palette("effect"), (int)from.Y));
z += new float2(step[0], step[1]);
if( rs.Count >= 1000 )
break;
}
p = z;
return rs;
}
static int[][] steps = new []
{
new int[] { 8, 8, 4, 4, 0 },
new int[] { -8, -8, -4, -4, 0 },
new int[] { 8, 0, 4, 4, 1 },
new int[] { -8, 0, -4, 4, 1 },
new int[] { 0, 8, 4, 4, 2 },
new int[] { 0, -8, 4, -4, 2 },
new int[] { -8, 8, -4, 4, 3 },
new int[] { 8, -8, 4, -4, 3 }
};
} }
} }

View File

@@ -42,8 +42,7 @@ namespace OpenRA.Mods.RA
this.self = init.self; this.self = init.self;
TopLeft = init.Get<LocationInit, CPos>(); TopLeft = init.Get<LocationInit, CPos>();
var ppos = init.Contains<CenterLocationInit>() ? init.Get<CenterLocationInit, PPos>() : Util.CenterOfCell(TopLeft); CenterPosition = init.Contains<CenterLocationInit>() ? init.Get<CenterLocationInit, PPos>().ToWPos(0) : TopLeft.CenterPosition;
CenterPosition = ppos.ToWPos(0);
Facing = init.Contains<FacingInit>() ? init.Get<FacingInit, int>() : 128; Facing = init.Contains<FacingInit>() ? init.Get<FacingInit, int>() : 128;
var speed = init.Contains<HuskSpeedInit>() ? init.Get<HuskSpeedInit, int>() : 0; var speed = init.Contains<HuskSpeedInit>() ? init.Get<HuskSpeedInit, int>() : 0;

View File

@@ -26,7 +26,8 @@ namespace OpenRA.Mods.RA
{ {
readonly JamsMissilesInfo info; readonly JamsMissilesInfo info;
public int Range { get { return info.Range; } } // Convert cells to world units
public int Range { get { return 1024 * info.Range; } }
public bool AlliedMissiles { get { return info.AlliedMissiles; } } public bool AlliedMissiles { get { return info.AlliedMissiles; } }
public int Chance { get { return info.Chance; } } public int Chance { get { return info.Chance; } }

View File

@@ -462,6 +462,7 @@
<Compile Include="Orders\SetChronoTankDestination.cs" /> <Compile Include="Orders\SetChronoTankDestination.cs" />
<Compile Include="Effects\FrozenActorProxy.cs" /> <Compile Include="Effects\FrozenActorProxy.cs" />
<Compile Include="Widgets\Logic\WorldTooltipLogic.cs" /> <Compile Include="Widgets\Logic\WorldTooltipLogic.cs" />
<Compile Include="TeslaZapRenderable.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj"> <ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -0,0 +1,148 @@
#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.Effects;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
struct TeslaZapRenderable : IRenderable
{
static int[][] steps = new[]
{
new int[] { 8, 8, 4, 4, 0 },
new int[] { -8, -8, -4, -4, 0 },
new int[] { 8, 0, 4, 4, 1 },
new int[] { -8, 0, -4, 4, 1 },
new int[] { 0, 8, 4, 4, 2 },
new int[] { 0, -8, 4, -4, 2 },
new int[] { -8, 8, -4, 4, 3 },
new int[] { 8, -8, 4, -4, 3 }
};
readonly WPos pos;
readonly int zOffset;
readonly WVec length;
readonly string image;
readonly int brightZaps, dimZaps;
WPos cachedPos;
WVec cachedLength;
IEnumerable<IRenderable> cache;
public TeslaZapRenderable(WPos pos, int zOffset, WVec length, string image, int brightZaps, int dimZaps)
{
this.pos = pos;
this.zOffset = zOffset;
this.length = length;
this.image = image;
this.brightZaps = brightZaps;
this.dimZaps = dimZaps;
cachedPos = WPos.Zero;
cachedLength = WVec.Zero;
cache = new IRenderable[] { };
}
public WPos Pos { get { return pos; } }
public float Scale { get { return 1f; } }
public PaletteReference Palette { get { return null; } }
public int ZOffset { get { return zOffset; } }
public IRenderable WithScale(float newScale) { return new TeslaZapRenderable(pos, zOffset, length, image, brightZaps, dimZaps); }
public IRenderable WithPalette(PaletteReference newPalette) { return new TeslaZapRenderable(pos, zOffset, length, image, brightZaps, dimZaps); }
public IRenderable WithZOffset(int newOffset) { return new TeslaZapRenderable(pos, zOffset, length, image, brightZaps, dimZaps); }
public IRenderable WithPos(WPos pos) { return new TeslaZapRenderable(pos, zOffset, length, image, brightZaps, dimZaps); }
public void BeforeRender(WorldRenderer wr) { }
public void RenderDebugGeometry(WorldRenderer wr) { }
public void Render(WorldRenderer wr)
{
if (!cache.Any() || length != cachedLength || pos != cachedPos)
cache = GenerateRenderables(wr);
cache.Do(c => c.Render(wr));
}
public IEnumerable<IRenderable> GenerateRenderables(WorldRenderer wr)
{
var bright = SequenceProvider.GetSequence(image, "bright");
var dim = SequenceProvider.GetSequence(image, "dim");
var source = wr.ScreenPosition(pos);
var target = wr.ScreenPosition(pos + length);
for (var n = 0; n < dimZaps; n++)
foreach (var z in DrawZapWandering(wr, source, target, dim))
yield return z;
for (var n = 0; n < brightZaps; n++)
foreach (var z in DrawZapWandering(wr, source, target, bright))
yield return z;
}
static IEnumerable<IRenderable> DrawZapWandering(WorldRenderer wr, float2 from, float2 to, Sequence s)
{
var z = float2.Zero; /* hack */
var dist = to - from;
var norm = (1f / dist.Length) * new float2(-dist.Y, dist.X);
var renderables = new List<IRenderable>();
if (Game.CosmeticRandom.Next(2) != 0)
{
var p1 = from + (1 / 3f) * dist + WRange.FromPDF(Game.CosmeticRandom, 2).Range * dist.Length / 4096 * norm;
var p2 = from + (2 / 3f) * dist + WRange.FromPDF(Game.CosmeticRandom, 2).Range * dist.Length / 4096 * norm;
renderables.AddRange(DrawZap(wr, from, p1, s, out p1));
renderables.AddRange(DrawZap(wr, p1, p2, s, out p2));
renderables.AddRange(DrawZap(wr, p2, to, s, out z));
}
else
{
var p1 = from + (1 / 2f) * dist + WRange.FromPDF(Game.CosmeticRandom, 2).Range * dist.Length / 4096 * norm;
renderables.AddRange(DrawZap(wr, from, p1, s, out p1));
renderables.AddRange(DrawZap(wr, p1, to, s, out z));
}
return renderables;
}
static IEnumerable<IRenderable> DrawZap(WorldRenderer wr, float2 from, float2 to, Sequence s, out float2 p)
{
var dist = to - from;
var q = new float2(-dist.Y, dist.X);
var c = -float2.Dot(from, q);
var rs = new List<IRenderable>();
var z = from;
while ((to - z).X > 5 || (to - z).X < -5 || (to - z).Y > 5 || (to - z).Y < -5)
{
var step = steps.Where(t => (to - (z + new float2(t[0], t[1]))).LengthSquared < (to - z).LengthSquared)
.OrderBy(t => Math.Abs(float2.Dot(z + new float2(t[0], t[1]), q) + c)).First();
rs.Add(new SpriteRenderable(s.GetSprite(step[4]), z + new float2(step[2], step[3]),
wr.Palette("effect"), (int)from.Y));
z += new float2(step[0], step[1]);
if (rs.Count >= 1000)
break;
}
p = z;
return rs;
}
}
}

View File

@@ -76,7 +76,7 @@ namespace OpenRA.Mods.RA
length = (finalPos - initialPos).Length / info.Velocity; length = (finalPos - initialPos).Length / info.Velocity;
// Facing rotation // Facing rotation
rotation = Game.CosmeticRandom.Gauss1D(2) * info.ROT; rotation = WRange.FromPDF(Game.CosmeticRandom, 2).Range * info.ROT / 1024;
var anim = new Animation(rs.GetImage(self), () => (int)facing); var anim = new Animation(rs.GetImage(self), () => (int)facing);
anim.PlayRepeating(info.Anim); anim.PlayRepeating(info.Anim);

View File

@@ -563,8 +563,6 @@ Napalm:
Image: BOMBLET Image: BOMBLET
Speed: 5 Speed: 5
High: yes High: yes
RangeLimit: 24
Arm: 24
Warhead: Warhead:
Spread: 4 Spread: 4
Versus: Versus:
@@ -865,6 +863,8 @@ ParaBomb:
Report: CHUTE1.AUD Report: CHUTE1.AUD
Projectile: GravityBomb Projectile: GravityBomb
Image: PARABOMB Image: PARABOMB
Velocity: 43
Acceleration: 0
Warhead: Warhead:
Spread: 6 Spread: 6
Versus: Versus:
@@ -950,11 +950,10 @@ SCUD:
Report: MISSILE1.AUD Report: MISSILE1.AUD
Projectile: Bullet Projectile: Bullet
Speed: 10 Speed: 10
Arm: 10
High: true High: true
Shadow: false Shadow: false
Proximity: true
Trail: smokey Trail: smokey
TrailDelay: 5
Inaccuracy: 5 Inaccuracy: 5
Image: V2 Image: V2
Angle: .1 Angle: .1