From 06e6d50735b4c47e33e1fa2550fa1b0c9b560534 Mon Sep 17 00:00:00 2001 From: Bob Date: Sat, 24 Oct 2009 17:28:51 +1300 Subject: [PATCH] Improvements to Mobile to support smooth movement (like real-ra does it) - McvDeploy got simpler. - BUGFIX: Bullet no longer crashes when it damages a tree --- OpenRa.Game/Bullet.cs | 75 ++++----- OpenRa.Game/MoveOrder.cs | 38 +---- OpenRa.Game/Traits/McvDeploy.cs | 43 +++-- OpenRa.Game/Traits/Mobile.cs | 265 +++++++++++++++++++++---------- OpenRa.Game/Traits/RenderUnit.cs | 5 +- 5 files changed, 255 insertions(+), 171 deletions(-) diff --git a/OpenRa.Game/Bullet.cs b/OpenRa.Game/Bullet.cs index f31c018eda..90cede7c64 100644 --- a/OpenRa.Game/Bullet.cs +++ b/OpenRa.Game/Bullet.cs @@ -4,19 +4,19 @@ using System.Linq; using System.Text; using OpenRa.Game.GameRules; using IjwFramework.Types; -using OpenRa.Game.Graphics; +using OpenRa.Game.Graphics; namespace OpenRa.Game -{ - interface IEffect - { - void Tick(); - IEnumerable> Render(); - Player Owner { get; } - } +{ + interface IEffect + { + void Tick(); + IEnumerable> Render(); + Player Owner { get; } + } class Bullet : IEffect - { + { public Player Owner { get; private set; } readonly Actor FiredBy; readonly WeaponInfo Weapon; @@ -49,22 +49,22 @@ namespace OpenRa.Game int TotalTime() { return (Dest - Src).Length * BaseBulletSpeed / Weapon.Speed; } public void Tick() - { + { if (t == 0) Game.PlaySound(Weapon.Report + ".aud", false); - t += 40; - - if (t > TotalTime()) /* remove finished bullets */ - { - Game.world.AddFrameEndTask(w => w.Remove(this)); - Game.world.AddFrameEndTask(w => w.Add(new Explosion(Dest))); - - var maxSpread = GetMaximumSpread(); - var hitActors = Game.FindUnitsInCircle(Dest, GetMaximumSpread()); - - foreach (var victim in hitActors) - victim.InflictDamage(FiredBy, this, (int)GetDamageToInflict(victim)); + t += 40; + + if (t > TotalTime()) /* remove finished bullets */ + { + Game.world.AddFrameEndTask(w => w.Remove(this)); + Game.world.AddFrameEndTask(w => w.Add(new Explosion(Dest))); + + var maxSpread = GetMaximumSpread(); + var hitActors = Game.FindUnitsInCircle(Dest, GetMaximumSpread()); + + foreach (var victim in hitActors) + victim.InflictDamage(FiredBy, this, (int)GetDamageToInflict(victim)); } } @@ -75,21 +75,24 @@ namespace OpenRa.Game Src.ToFloat2(), Dest.ToFloat2(), (float)t / TotalTime()) - 0.5f * anim.Image.size); - } - - float GetMaximumSpread() + } + + float GetMaximumSpread() + { + return (int)(Warhead.Spread * Math.Log(Weapon.Damage, 2)); + } + + float GetDamageToInflict(Actor target) { - return (int)(Warhead.Spread * Math.Log(Weapon.Damage, 2)); - } - - float GetDamageToInflict(Actor target) - { - /* todo: some things can't be damaged AT ALL by certain weapons! */ - var distance = (target.CenterLocation - Dest).Length; - var rawDamage = Weapon.Damage * (float)Math.Exp(-distance / Warhead.Spread); - var multiplier = Warhead.EffectivenessAgainst(target.unitInfo.Armor); - - return rawDamage * multiplier; + if( target.unitInfo == null ) // tree or other doodad + return 0; + + /* todo: some things can't be damaged AT ALL by certain weapons! */ + var distance = (target.CenterLocation - Dest).Length; + var rawDamage = Weapon.Damage * (float)Math.Exp(-distance / Warhead.Spread); + var multiplier = Warhead.EffectivenessAgainst(target.unitInfo.Armor); + + return rawDamage * multiplier; } } } diff --git a/OpenRa.Game/MoveOrder.cs b/OpenRa.Game/MoveOrder.cs index d441b56368..0ffbaa1656 100644 --- a/OpenRa.Game/MoveOrder.cs +++ b/OpenRa.Game/MoveOrder.cs @@ -18,12 +18,12 @@ namespace OpenRa.Game { this.Unit = unit; this.Destination = destination; - } - - string GetVoiceSuffix() - { - var suffixes = new[] { ".r01", ".r03" }; - return suffixes[Unit.traits.Get().Voice]; + } + + string GetVoiceSuffix() + { + var suffixes = new[] { ".r01", ".r03" }; + return suffixes[Unit.traits.Get().Voice]; } public override void Apply( bool leftMouseButton ) @@ -31,31 +31,9 @@ namespace OpenRa.Game if (leftMouseButton) return; if (Game.LocalPlayer == Unit.Owner) - Game.PlaySound(Game.SovietVoices.First.GetNext() + GetVoiceSuffix(), false); - + Game.PlaySound("ackno.r00", false); var mobile = Unit.traits.Get(); - mobile.destination = Destination; - mobile.desiredFacing = null; - } - } - - class DeployMcvOrder : Order - { - Actor Unit; - int2 Location; - - public DeployMcvOrder( Actor unit, int2 location ) - { - Unit = unit; - Location = location; - } - - public override void Apply( bool leftMouseButton ) - { - if (leftMouseButton) return; - Unit.traits.Get().DeployLocation = Location; - var mobile = Unit.traits.Get(); - mobile.destination = mobile.toCell; + mobile.SetNextAction( new Traits.Mobile.MoveTo( Destination ) ); } } } diff --git a/OpenRa.Game/Traits/McvDeploy.cs b/OpenRa.Game/Traits/McvDeploy.cs index 8071287dcb..1000e84bfa 100644 --- a/OpenRa.Game/Traits/McvDeploy.cs +++ b/OpenRa.Game/Traits/McvDeploy.cs @@ -5,42 +5,51 @@ using System.Text; namespace OpenRa.Game.Traits { - class McvDeploy : IOrder, ITick + class McvDeploy : IOrder { - public int2? DeployLocation; - public McvDeploy(Actor self) { } public Order Order(Actor self, int2 xy) { - DeployLocation = null; // TODO: check that there's enough space at the destination. if( xy == self.Location ) return new DeployMcvOrder( self, xy ); return null; } + } - public void Tick(Actor self) + class DeployMcvOrder : Order + { + Actor Unit; + int2 Location; + + public DeployMcvOrder( Actor unit, int2 location ) { - if( self.Location != DeployLocation ) - return; + Unit = unit; + Location = location; + } - var mobile = self.traits.Get(); - mobile.desiredFacing = 96; - if( mobile.moveFraction < mobile.moveFractionTotal ) - return; + public override void Apply( bool leftMouseButton ) + { + if( leftMouseButton ) return; + Unit.traits.Get().SetNextAction( new Mobile.Turn( 96 ) { NextAction = new DeployAction() } ); + } - if( mobile.facing != mobile.desiredFacing ) - return; + class DeployAction : Mobile.CurrentAction + { + public Mobile.CurrentAction NextAction { get; set; } - Game.world.AddFrameEndTask(_ => + public void Tick( Actor self, Mobile mobile ) { - Game.world.Remove(self); - Game.world.Add(new Actor("fact", self.Location - new int2(1, 1), self.Owner)); - }); + Game.world.AddFrameEndTask( _ => + { + Game.world.Remove( self ); + Game.world.Add( new Actor( "fact", self.Location - new int2( 1, 1 ), self.Owner ) ); + } ); + } } } } diff --git a/OpenRa.Game/Traits/Mobile.cs b/OpenRa.Game/Traits/Mobile.cs index f1d4b7da43..3687128503 100644 --- a/OpenRa.Game/Traits/Mobile.cs +++ b/OpenRa.Game/Traits/Mobile.cs @@ -1,84 +1,181 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using OpenRa.Game.GameRules; -using OpenRa.Game.Graphics; - -namespace OpenRa.Game.Traits -{ - class Mobile : ITick, IOrder - { - public Actor self; - - public int2 fromCell, destination; - public int2 toCell { get { return self.Location; } } - public int moveFraction, moveFractionTotal; - public int facing; - public int? desiredFacing; - public int Voice = Game.CosmeticRandom.Next(2); - - public Mobile(Actor self) - { - this.self = self; - fromCell = destination = self.Location; - } - - void UpdateCenterLocation() - { - float fraction = (moveFraction > 0) ? (float)moveFraction / moveFractionTotal : 0f; - self.CenterLocation = new float2(12, 12) + Game.CellSize * float2.Lerp(fromCell, toCell, fraction); - } - - public void Tick(Actor self) - { - Move(self); - UpdateCenterLocation(); - } - - void Move(Actor self) - { - if( fromCell != toCell ) - desiredFacing = Util.GetFacing( toCell - fromCell, facing ); - - if( desiredFacing != null && desiredFacing != facing ) - { - Util.TickFacing( ref facing, desiredFacing.Value, self.unitInfo.ROT ); - return; - } - desiredFacing = null; - - if( fromCell != toCell ) - moveFraction += ((UnitInfo.MobileInfo)self.unitInfo).Speed; - - if (moveFraction < moveFractionTotal) - return; - - moveFraction = 0; - moveFractionTotal = 0; - fromCell = toCell; - - if (destination == toCell) - return; - - List res = Game.pathFinder.FindUnitPath(toCell, PathFinder.DefaultEstimator(destination)); - if (res.Count != 0) - { - self.Location = res[res.Count - 1]; - - int2 dir = toCell - fromCell; - moveFractionTotal = (dir.X != 0 && dir.Y != 0) ? 70 : 50; - } - else - destination = toCell; - } - - public Order Order(Actor self, int2 xy) - { - if (xy != toCell) - return new MoveOrder(self, xy); - - return null; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using OpenRa.Game.GameRules; +using OpenRa.Game.Graphics; + +namespace OpenRa.Game.Traits +{ + class Mobile : ITick, IOrder + { + public Actor self; + + public int2 fromCell; + public int2 toCell { get { return self.Location; } set { self.Location = value; } } + public int facing; + + public int Voice = Game.CosmeticRandom.Next(2); + CurrentAction currentAction; + + public Mobile(Actor self) + { + this.self = self; + fromCell = toCell; + } + + public void SetNextAction( CurrentAction nextAction ) + { + if( currentAction == null ) + currentAction = nextAction; + else + currentAction.NextAction = nextAction; + } + + public void Tick(Actor self) + { + if( currentAction != null ) + currentAction.Tick( self, this ); + else + fromCell = toCell; + } + + public Order Order(Actor self, int2 xy) + { + if (xy != toCell) + return new MoveOrder(self, xy); + + return null; + } + + + public interface CurrentAction + { + CurrentAction NextAction { set; } + void Tick( Actor self, Mobile mobile ); + } + + public class Turn : CurrentAction + { + public CurrentAction NextAction { get; set; } + + public readonly int desiredFacing; + + public Turn( int desiredFacing ) + { + this.desiredFacing = desiredFacing; + } + + public void Tick( Actor self, Mobile mobile ) + { + if( desiredFacing == mobile.facing ) + { + mobile.currentAction = NextAction; + if( NextAction != null ) + NextAction.Tick( self, mobile ); + return; + } + Util.TickFacing( ref mobile.facing, desiredFacing, self.unitInfo.ROT ); + } + } + + public class MoveTo : CurrentAction + { + public CurrentAction NextAction { get; set; } + + int2 destination; + List path; + + int moveFraction, moveFractionTotal; + float2 from, to; + + Action OnComplete; + + public MoveTo( int2 destination ) + { + this.destination = destination; + } + + public void Tick( Actor self, Mobile mobile ) + { + if( moveFractionTotal != 0 ) + { + TickMove( self, mobile ); + return; + } + + if( destination == self.Location ) + { + mobile.currentAction = NextAction; + return; + } + + if( path == null ) + path = Game.pathFinder.FindUnitPath( self.Location, PathFinder.DefaultEstimator( destination ) ); + if( path.Count == 0 ) + { + destination = mobile.toCell; + return; + } + + var nextCell = path[ path.Count - 1 ]; + int2 dir = nextCell - mobile.fromCell; + var firstFacing = Util.GetFacing( dir, mobile.facing ); + if( firstFacing != mobile.facing ) + mobile.currentAction = new Turn( firstFacing ) { NextAction = this }; + else + { + mobile.toCell = nextCell; + path.RemoveAt( path.Count - 1 ); + moveFractionTotal = ( dir.X != 0 && dir.Y != 0 ) ? 35 : 25; + from = CenterOfCell( mobile.fromCell ); + to = BetweenCells( mobile.fromCell, mobile.toCell ); + OnComplete = OnCompleteFirstHalf; + } + mobile.currentAction.Tick( self, mobile ); + } + + void TickMove( Actor self, Mobile mobile ) + { + moveFraction += ( self.unitInfo as UnitInfo.MobileInfo ).Speed; + UpdateCenterLocation( self, (float)moveFraction / moveFractionTotal, from, to ); + if( moveFraction >= moveFractionTotal ) + { + moveFraction -= moveFractionTotal; + OnComplete( self, mobile ); + mobile.fromCell = mobile.toCell; + } + return; + } + + static void UpdateCenterLocation( Actor self, float frac, float2 from, float2 to ) + { + self.CenterLocation = float2.Lerp( from, to, frac ); + } + + static float2 CenterOfCell( int2 loc ) + { + return new float2( 12, 12 ) + Game.CellSize * (float2)loc; + } + + static float2 BetweenCells( int2 from, int2 to ) + { + return 0.5f * ( CenterOfCell( from ) + CenterOfCell( to ) ); + } + + void OnCompleteFirstHalf( Actor self, Mobile mobile ) + { + from = BetweenCells( mobile.fromCell, mobile.toCell ); + to = CenterOfCell( mobile.toCell ); + OnComplete = OnCompleteSecondHalf; + } + + void OnCompleteSecondHalf( Actor self, Mobile mobile ) + { + moveFractionTotal = 0; + self.CenterLocation = CenterOfCell( mobile.toCell ); + OnComplete = null; + } + } + } +} diff --git a/OpenRa.Game/Traits/RenderUnit.cs b/OpenRa.Game/Traits/RenderUnit.cs index 93410781ff..bb1e6f349d 100644 --- a/OpenRa.Game/Traits/RenderUnit.cs +++ b/OpenRa.Game/Traits/RenderUnit.cs @@ -23,10 +23,7 @@ namespace OpenRa.Game.Traits public override IEnumerable> Render(Actor self) { - var mobile = self.traits.Get(); - float fraction = (mobile.moveFraction > 0) ? (float)mobile.moveFraction / mobile.moveFractionTotal : 0f; - var centerLocation = new float2(12, 12) + Game.CellSize * float2.Lerp(mobile.fromCell, mobile.toCell, fraction); - yield return Centered(anim.Image, centerLocation); + yield return Centered( anim.Image, self.CenterLocation ); } } }