diff --git a/OpenRA.FileFormats/WVec.cs b/OpenRA.FileFormats/WVec.cs index 0140a78616..4eab6e15d1 100644 --- a/OpenRA.FileFormats/WVec.cs +++ b/OpenRA.FileFormats/WVec.cs @@ -26,8 +26,11 @@ namespace OpenRA public static readonly WVec Zero = new WVec(0, 0, 0); public static WVec operator +(WVec a, WVec b) { return new WVec(a.X + b.X, a.Y + b.Y, a.Z + b.Z); } - public static WVec operator -(WVec a, WVec b) { return new WVec(a.X - b.X, a.Y - b.Y, a.Y - b.Y); } + public static WVec operator -(WVec a, WVec b) { return new WVec(a.X - b.X, a.Y - b.Y, a.Z - b.Z); } public static WVec operator -(WVec a) { return new WVec(-a.X, -a.Y, -a.Z); } + public static WVec operator /(WVec a, int b) { return new WVec(a.X / b, a.Y / b, a.Z / b); } + public static WVec operator *(int a, WVec b) { return new WVec(a * b.X, a * b.Y, a * b.Z); } + public static WVec operator *(WVec a, int b) { return b*a; } public static bool operator ==(WVec me, WVec other) { return (me.X == other.X && me.Y == other.Y && me.Z == other.Z); } public static bool operator !=(WVec me, WVec other) { return !(me == other); } @@ -48,6 +51,8 @@ namespace OpenRA (int)((lx * mtx[2] + ly*mtx[6] + lz*mtx[10]) / mtx[15])); } + public static WVec Lerp(WVec a, WVec b, int mul, int div) { return a + (b - a) * mul / div; } + public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); } public override bool Equals(object obj) diff --git a/OpenRA.Mods.RA/ThrowsParticle.cs b/OpenRA.Mods.RA/ThrowsParticle.cs index 746bca1d3f..55dca13b3f 100644 --- a/OpenRA.Mods.RA/ThrowsParticle.cs +++ b/OpenRA.Mods.RA/ThrowsParticle.cs @@ -8,19 +8,30 @@ */ #endregion +using OpenRA.FileFormats; using OpenRA.Graphics; using OpenRA.Mods.RA.Render; using OpenRA.Traits; namespace OpenRA.Mods.RA { - class ThrowsParticleInfo : ITraitInfo, Requires + class ThrowsParticleInfo : ITraitInfo, Requires { public readonly string Anim = null; - public readonly int[] Offset = new[] { 0, 0, 0, 0 }; - public readonly int[] Spread = new[] { 0, 0 }; - public readonly float Speed = 20; - public readonly string AnimKey = null; + + [Desc("Initial position relative to body")] + public readonly WVec Offset = WVec.Zero; + + [Desc("Maximum distance to throw the particle")] + public readonly WRange ThrowRange = new WRange(768); + + [Desc("Maximum height to throw the particle")] + public readonly WRange ThrowHeight = new WRange(256); + + [Desc("Number of ticks to animate")] + public readonly int Length = 15; + + [Desc("Maximum rotation rate")] public readonly float ROT = 15; public object Create(ActorInitializer init) { return new ThrowsParticle(init, this); } @@ -28,51 +39,56 @@ namespace OpenRA.Mods.RA class ThrowsParticle : ITick { - float2 pos; - float alt; + ThrowsParticleInfo info; + WVec pos; + WVec initialPos; + WVec finalPos; + int tick = 0; - float2 v; - float va; float facing; - float dfacing; - - const float gravity = 1.3f; + float rotation; public ThrowsParticle(ActorInitializer init, ThrowsParticleInfo info) { + this.info = info; + var self = init.self; - var ifacing = self.Trait(); - var ru = self.Trait(); + var rs = self.Trait(); - alt = 0; - facing = Turreted.GetInitialTurretFacing( init, 0 ); - pos = new Turret(info.Offset).PxPosition(self, ifacing).ToFloat2(); + // TODO: Carry orientation over from the parent instead of just facing + var bodyFacing = init.Contains() ? init.Get() : 0; + facing = Turreted.GetInitialTurretFacing(init, 0); - v = Game.CosmeticRandom.Gauss2D(1) * info.Spread.RelOffset(); - dfacing = Game.CosmeticRandom.Gauss1D(2) * info.ROT; - va = info.Speed; + // Calculate final position + var throwRotation = WRot.FromFacing(Game.CosmeticRandom.Next(1024)); + var throwOffset = new WVec((int)(Game.CosmeticRandom.Gauss1D(1)*info.ThrowRange.Range), 0, 0).Rotate(throwRotation); - var anim = new Animation(ru.GetImage(self), () => (int)facing); + initialPos = pos = info.Offset.Rotate(rs.QuantizeOrientation(self, WRot.FromFacing(bodyFacing))); + finalPos = initialPos + throwOffset; + + // Facing rotation + rotation = Game.CosmeticRandom.Gauss1D(2) * info.ROT; + + var anim = new Animation(rs.GetImage(self), () => (int)facing); anim.PlayRepeating(info.Anim); - - ru.anims.Add(info.AnimKey, new AnimationWithOffset( - anim, wr => pos - new float2(0, alt), null)); + rs.anims.Add(info.Anim, new AnimationWithOffset(anim, wr => wr.ScreenPxOffset(pos), null)); } public void Tick(Actor self) { - va -= gravity; - alt += va; + if (tick == info.Length) + return; + tick++; - if (alt < 0) alt = 0; - else - { - pos += v; - v = .9f * v; + // Lerp position horizontally and height along a sinusoid using a cubic ease + var t = (tick*tick*tick / (info.Length*info.Length) - 3*tick*tick / info.Length + 3*tick); + var tp = WVec.Lerp(initialPos, finalPos, t, info.Length); + var th = new WAngle(512*(info.Length - t) / info.Length).Sin()*info.ThrowHeight.Range / 1024; + pos = new WVec(tp.X, tp.Y, th); - facing += dfacing; - dfacing *= .9f; - } + // Spin the particle + facing += rotation; + rotation *= .9f; } } } diff --git a/mods/cnc/rules/husks.yaml b/mods/cnc/rules/husks.yaml index 96bb6deb75..735d2a3f57 100644 --- a/mods/cnc/rules/husks.yaml +++ b/mods/cnc/rules/husks.yaml @@ -73,9 +73,6 @@ LTNK.Husk: Image: ltnk ThrowsParticle@turret: Anim: turret - Spread: 3,3 - Speed: 6 - AnimKey: turret MTNK.Husk: Inherits: ^Husk @@ -86,9 +83,6 @@ MTNK.Husk: Image: mtnk ThrowsParticle@turret: Anim: turret - Spread: 3,3 - Speed: 6 - AnimKey: turret HTNK.Husk: Inherits: ^Husk @@ -99,9 +93,6 @@ HTNK.Husk: Image: htnk ThrowsParticle@turret: Anim: turret - Spread: 3,3 - Speed: 6 - AnimKey: turret MSAM.Husk: Inherits: ^Husk @@ -112,9 +103,6 @@ MSAM.Husk: Image: msam ThrowsParticle@turret: Anim: turret - Spread: 3,3 - Speed: 6 - AnimKey: turret MLRS.Husk: Inherits: ^Husk @@ -125,9 +113,6 @@ MLRS.Husk: Image: mlrs ThrowsParticle@turret: Anim: turret - Spread: 3,3 - Speed: 6 - AnimKey: turret STNK.Husk: Inherits: ^Husk diff --git a/mods/d2k/rules/structures.yaml b/mods/d2k/rules/structures.yaml index b74d92d3d6..124edae33b 100644 --- a/mods/d2k/rules/structures.yaml +++ b/mods/d2k/rules/structures.yaml @@ -498,9 +498,6 @@ GUNTOWER.Husk: Image: GUNTOWER ThrowsParticle@turret: Anim: turret - Spread: 4,4 - Speed: 6 - AnimKey: turret ROCKETTOWER: Inherits: ^Building @@ -559,9 +556,6 @@ ROCKETTOWER.Husk: Image: ROCKETTOWER ThrowsParticle@turret: Anim: turret - Spread: 4,4 - Speed: 6 - AnimKey: turret REPAIR: Inherits: ^Building diff --git a/mods/d2k/rules/vehicles.yaml b/mods/d2k/rules/vehicles.yaml index 16b3f88db0..9a63161572 100644 --- a/mods/d2k/rules/vehicles.yaml +++ b/mods/d2k/rules/vehicles.yaml @@ -242,9 +242,6 @@ QUAD.starport: HP: 100 ThrowsParticle@turret: Anim: turret - Spread: 3,3 - Speed: 6 - AnimKey: turret SIEGETANK: Inherits: ^Tank @@ -299,9 +296,6 @@ SIEGETANK.Husk: Icon: siegetankicon ThrowsParticle@turret: Anim: turret - Spread: 3,3 - Speed: 6 - AnimKey: turret RenderUnit: Image: SIEGETANK diff --git a/mods/ra/maps/monster-tank-madness/map.yaml b/mods/ra/maps/monster-tank-madness/map.yaml index d63881610b..c9892bffa0 100644 --- a/mods/ra/maps/monster-tank-madness/map.yaml +++ b/mods/ra/maps/monster-tank-madness/map.yaml @@ -2602,9 +2602,6 @@ Rules: Image: 4TNK ThrowsParticle@turret: Anim: turret - Spread: 3,3 - Speed: 6 - AnimKey: turret Health: HP: 2000 SILO: diff --git a/mods/ra/rules/vehicles.yaml b/mods/ra/rules/vehicles.yaml index cdde0e078b..40ec8702e7 100644 --- a/mods/ra/rules/vehicles.yaml +++ b/mods/ra/rules/vehicles.yaml @@ -523,10 +523,7 @@ MGG.Husk: Image: mgg ThrowsParticle@spinner: Anim: spinner-idle - Spread: 3,3 - Speed: 6 - AnimKey: spinner-idle - Offset: 0,6 + Offset: -299,0,171 MRJ: Inherits: ^Vehicle @@ -564,9 +561,6 @@ MRJ: Image: 1tnk ThrowsParticle@turret: Anim: turret - Spread: 3,3 - Speed: 6 - AnimKey: turret 2TNK.Husk: Inherits: ^Husk @@ -576,9 +570,6 @@ MRJ: Image: 2tnk ThrowsParticle@turret: Anim: turret - Spread: 3,3 - Speed: 6 - AnimKey: turret 3TNK.Husk: Inherits: ^Husk @@ -588,9 +579,6 @@ MRJ: Image: 3tnk ThrowsParticle@turret: Anim: turret - Spread: 3,3 - Speed: 6 - AnimKey: turret 4TNK.Husk: Inherits: ^Husk @@ -600,9 +588,6 @@ MRJ: Image: 4tnk ThrowsParticle@turret: Anim: turret - Spread: 3,3 - Speed: 6 - AnimKey: turret HARV.FullHusk: Inherits: ^Husk