Merge pull request #3545 from pchote/infantry-range-fix

Infantry range fix
This commit is contained in:
Matthias Mailänder
2013-07-11 11:45:15 -07:00
36 changed files with 219 additions and 177 deletions

View File

@@ -79,7 +79,7 @@ namespace OpenRA
public static void SetListenerPosition(float2 position) { soundEngine.SetListenerPosition(position); } public static void SetListenerPosition(float2 position) { soundEngine.SetListenerPosition(position); }
static ISound Play(Player player, string name, bool headRelative, PPos pos, float volumeModifier) static ISound Play(Player player, string name, bool headRelative, WPos pos, float volumeModifier)
{ {
if (String.IsNullOrEmpty(name)) if (String.IsNullOrEmpty(name))
return null; return null;
@@ -87,16 +87,16 @@ namespace OpenRA
return null; return null;
return soundEngine.Play2D(sounds[name], return soundEngine.Play2D(sounds[name],
false, headRelative, pos.ToFloat2(), false, headRelative, PPos.FromWPosHackZ(pos).ToFloat2(),
InternalSoundVolume * volumeModifier, true); InternalSoundVolume * volumeModifier, true);
} }
public static ISound Play(string name) { return Play(null, name, true, PPos.Zero, 1); } public static ISound Play(string name) { return Play(null, name, true, WPos.Zero, 1); }
public static ISound Play(string name, PPos pos) { return Play(null, name, false, pos, 1); } public static ISound Play(string name, WPos pos) { return Play(null, name, false, pos, 1); }
public static ISound Play(string name, float volumeModifier) { return Play(null, name, true, PPos.Zero, volumeModifier); } public static ISound Play(string name, float volumeModifier) { return Play(null, name, true, WPos.Zero, volumeModifier); }
public static ISound Play(string name, PPos pos, float volumeModifier) { return Play(null, name, false, pos, volumeModifier); } public static ISound Play(string name, WPos pos, float volumeModifier) { return Play(null, name, false, pos, volumeModifier); }
public static ISound PlayToPlayer(Player player, string name) { return Play(player, name, true, PPos.Zero, 1); } public static ISound PlayToPlayer(Player player, string name) { return Play(player, name, true, WPos.Zero, 1); }
public static ISound PlayToPlayer(Player player, string name, PPos pos) { return Play(player, name, false, pos, 1); } public static ISound PlayToPlayer(Player player, string name, WPos pos) { return Play(player, name, false, pos, 1); }
public static void PlayVideo(byte[] raw) public static void PlayVideo(byte[] raw)
{ {

View File

@@ -80,7 +80,7 @@ namespace OpenRA.Traits
if (targetable == null) if (targetable == null)
return new []{actor.CenterPosition}; return new []{actor.CenterPosition};
return targetable.TargetableCells(actor).Select(c => c.CenterPosition); return targetable.TargetablePositions(actor);
} }
} }

View File

@@ -201,7 +201,7 @@ namespace OpenRA.Traits
public interface ITargetable public interface ITargetable
{ {
string[] TargetTypes { get; } string[] TargetTypes { get; }
IEnumerable<CPos> TargetableCells(Actor self); IEnumerable<WPos> TargetablePositions(Actor self);
bool TargetableBy(Actor self, Actor byActor); bool TargetableBy(Actor self, Actor byActor);
} }

View File

@@ -32,7 +32,7 @@ namespace OpenRA.Mods.Cnc
{ {
self.World.AddFrameEndTask(w => self.World.AddFrameEndTask(w =>
{ {
Sound.Play(Info.LaunchSound, order.TargetLocation.ToPPos()); Sound.Play(Info.LaunchSound, order.TargetLocation.CenterPosition);
w.Add(new IonCannon(self, w, order.TargetLocation)); w.Add(new IonCannon(self, w, order.TargetLocation));
}); });
} }

View File

@@ -226,18 +226,18 @@ namespace OpenRA.Mods.RA.AI
protected static bool CanAttackTarget(Actor a, Actor target) protected static bool CanAttackTarget(Actor a, Actor target)
{ {
if (!a.HasTrait<AttackBase>()) return false; if (!a.HasTrait<AttackBase>())
if (!target.HasTrait<TargetableUnit<TargetableUnitInfo>>() && return false;
!target.HasTrait<TargetableBuilding>()) return false;
var targetable = target.TraitOrDefault<ITargetable>();
if (targetable == null)
return false;
var arms = a.TraitsImplementing<Armament>(); var arms = a.TraitsImplementing<Armament>();
foreach (var arm in arms) foreach (var arm in arms)
if (target.HasTrait<TargetableUnit<TargetableUnitInfo>>() && if (arm.Weapon.ValidTargets.Intersect(targetable.TargetTypes) != null)
arm.Weapon.ValidTargets.Intersect(target.Trait<TargetableUnit<TargetableUnitInfo>>().TargetTypes) != null)
return true;
else if (target.HasTrait<TargetableBuilding>() &&
arm.Weapon.ValidTargets.Intersect(target.Trait<TargetableBuilding>().TargetTypes) != null)
return true; return true;
return false; return false;
} }
} }

View File

@@ -70,7 +70,7 @@ namespace OpenRA.Mods.RA.Activities
nextPathTime = self.World.SharedRandom.Next(delayBetweenPathingAttempts - delaySpread, nextPathTime = self.World.SharedRandom.Next(delayBetweenPathingAttempts - delaySpread,
delayBetweenPathingAttempts + delaySpread); delayBetweenPathingAttempts + delaySpread);
return (AllowMovement) ? Util.SequenceActivities(self.Trait<Mobile>().MoveWithinRange(Target, Range.Range / 1024), this) : NextActivity; return (AllowMovement) ? Util.SequenceActivities(self.Trait<Mobile>().MoveWithinRange(Target, Range), this) : NextActivity;
} }
var desiredFacing = Util.GetFacing(Target.CenterPosition - self.CenterPosition, 0); var desiredFacing = Util.GetFacing(Target.CenterPosition - self.CenterPosition, 0);

View File

@@ -15,34 +15,32 @@ namespace OpenRA.Mods.RA.Activities
{ {
public class Follow : Activity public class Follow : Activity
{ {
Target Target; Target target;
int Range; WRange range;
int nextPathTime; int nextPathTime;
const int delayBetweenPathingAttempts = 20; const int delayBetweenPathingAttempts = 20;
const int delaySpread = 5; const int delaySpread = 5;
public Follow(Target target, int range) public Follow(Target target, WRange range)
{ {
Target = target; this.target = target;
Range = range; this.range = range;
} }
public override Activity Tick(Actor self) public override Activity Tick(Actor self)
{ {
if (IsCanceled) return NextActivity; if (IsCanceled || !target.IsValid)
if (!Target.IsValid) return NextActivity; return NextActivity;
var inRange = ( Target.CenterPosition.ToCPos() - self.Location ).LengthSquared < Range * Range; if (target.IsInRange(self.CenterPosition, range) || --nextPathTime > 0)
return this;
if( inRange ) return this;
if (--nextPathTime > 0) return this;
nextPathTime = self.World.SharedRandom.Next(delayBetweenPathingAttempts - delaySpread, nextPathTime = self.World.SharedRandom.Next(delayBetweenPathingAttempts - delaySpread,
delayBetweenPathingAttempts + delaySpread); delayBetweenPathingAttempts + delaySpread);
var mobile = self.Trait<Mobile>(); var mobile = self.Trait<Mobile>();
return Util.SequenceActivities( mobile.MoveWithinRange( Target, Range ), this ); return Util.SequenceActivities(mobile.MoveWithinRange(target, range), this);
} }
} }
} }

View File

@@ -48,7 +48,7 @@ namespace OpenRA.Mods.RA.Activities
self.Trait<RenderInfantry>().Attacking(self, Target.FromActor(target)); self.Trait<RenderInfantry>().Attacking(self, Target.FromActor(target));
if (weapon.Report != null && weapon.Report.Any()) if (weapon.Report != null && weapon.Report.Any())
Sound.Play(weapon.Report.Random(self.World.SharedRandom), self.CenterLocation); Sound.Play(weapon.Report.Random(self.World.SharedRandom), self.CenterPosition);
} }
public override Activity Tick(Actor self) public override Activity Tick(Actor self)

View File

@@ -55,7 +55,7 @@ namespace OpenRA.Mods.RA.Activities
var bi = self.Info.Traits.GetOrDefault<BuildingInfo>(); var bi = self.Info.Traits.GetOrDefault<BuildingInfo>();
if (bi != null) if (bi != null)
foreach (var s in bi.SellSounds) foreach (var s in bi.SellSounds)
Sound.PlayToPlayer(self.Owner, s, self.CenterLocation); Sound.PlayToPlayer(self.Owner, s, self.CenterPosition);
rb.PlayCustomAnimBackwards(self, "make", () => { OnComplete(); complete = true;}); rb.PlayCustomAnimBackwards(self, "make", () => { OnComplete(); complete = true;});
} }

View File

@@ -29,8 +29,8 @@ namespace OpenRA.Mods.RA.Activities
public override Activity Tick(Actor self) public override Activity Tick(Actor self)
{ {
Sound.Play("chrono2.aud", self.Location.ToPPos()); Sound.Play("chrono2.aud", self.CenterPosition);
Sound.Play("chrono2.aud", destination.ToPPos()); Sound.Play("chrono2.aud", destination.CenterPosition);
self.Trait<ITeleportable>().SetPosition(self, destination); self.Trait<ITeleportable>().SetPosition(self, destination);
self.Generation++; self.Generation++;

View File

@@ -44,7 +44,7 @@ namespace OpenRA.Mods.RA.Activities
self.Destroy(); self.Destroy();
foreach (var s in Sounds) foreach (var s in Sounds)
Sound.PlayToPlayer(self.Owner, s, self.CenterLocation); Sound.PlayToPlayer(self.Owner, s, self.CenterPosition);
var init = new TypeDictionary var init = new TypeDictionary
{ {

View File

@@ -36,7 +36,7 @@ namespace OpenRA.Mods.RA
&& self.Owner.WinState != WinState.Lost) && self.Owner.WinState != WinState.Lost)
{ {
self.World.AddFrameEndTask(w => w.Add(new Parachute(pilot, self.CenterPosition))); self.World.AddFrameEndTask(w => w.Add(new Parachute(pilot, self.CenterPosition)));
Sound.Play(info.ChuteSound, self.CenterLocation); Sound.Play(info.ChuteSound, self.CenterPosition);
} }
else else
pilot.Destroy(); pilot.Destroy();

View File

@@ -21,12 +21,15 @@ namespace OpenRA.Mods.RA.Air
public override object Create(ActorInitializer init) { return new TargetableAircraft(init.self, this); } public override object Create(ActorInitializer init) { return new TargetableAircraft(init.self, this); }
} }
public class TargetableAircraft : TargetableUnit<TargetableAircraftInfo> public class TargetableAircraft : TargetableUnit
{ {
Aircraft Aircraft; readonly TargetableAircraftInfo info;
readonly Aircraft Aircraft;
public TargetableAircraft(Actor self, TargetableAircraftInfo info) public TargetableAircraft(Actor self, TargetableAircraftInfo info)
: base(self, info) : base(self, info)
{ {
this.info = info;
Aircraft = self.Trait<Aircraft>(); Aircraft = self.Trait<Aircraft>();
} }

View File

@@ -154,7 +154,7 @@ namespace OpenRA.Mods.RA
self.World.Add(projectile); self.World.Add(projectile);
if (args.weapon.Report != null && args.weapon.Report.Any()) if (args.weapon.Report != null && args.weapon.Report.Any())
Sound.Play(args.weapon.Report.Random(self.World.SharedRandom), self.CenterLocation); Sound.Play(args.weapon.Report.Random(self.World.SharedRandom), self.CenterPosition);
} }
}); });

View File

@@ -25,8 +25,12 @@ namespace OpenRA.Mods.RA
public class AttackFrontal : AttackBase public class AttackFrontal : AttackBase
{ {
readonly AttackFrontalInfo info; readonly AttackFrontalInfo info;
public AttackFrontal(Actor self, AttackFrontalInfo info) public AttackFrontal(Actor self, AttackFrontalInfo info)
: base( self ) { this.info = info; } : base(self)
{
this.info = info;
}
protected override bool CanAttack(Actor self, Target target) protected override bool CanAttack(Actor self, Target target)
{ {

View File

@@ -95,12 +95,11 @@ namespace OpenRA.Mods.RA
if (weapon != null) if (weapon != null)
{ {
attack.target = target; var range = WRange.FromCells(Math.Max(0, (int)weapon.Weapon.Range - RangeTolerance));
attack.target = target;
if (allowMove && self.HasTrait<Mobile>() && !self.Info.Traits.Get<MobileInfo>().OnRails) if (allowMove && self.HasTrait<Mobile>() && !self.Info.Traits.Get<MobileInfo>().OnRails)
return Util.SequenceActivities( return Util.SequenceActivities(new Follow(target, range), this);
new Follow( target, Math.Max( 0, (int)weapon.Weapon.Range - RangeTolerance ) ),
this );
} }
return NextActivity; return NextActivity;

View File

@@ -32,9 +32,9 @@ namespace OpenRA.Mods.RA.Buildings
public void DamageStateChanged(Actor self, AttackInfo e) public void DamageStateChanged(Actor self, AttackInfo e)
{ {
if (e.DamageState == DamageState.Dead) if (e.DamageState == DamageState.Dead)
Sound.Play(Info.DestroyedSound, self.CenterLocation); Sound.Play(Info.DestroyedSound, self.CenterPosition);
else if (e.DamageState >= DamageState.Heavy && e.PreviousDamageState < DamageState.Heavy) else if (e.DamageState >= DamageState.Heavy && e.PreviousDamageState < DamageState.Heavy)
Sound.Play(Info.DamagedSound, self.CenterLocation); Sound.Play(Info.DamagedSound, self.CenterPosition);
} }
} }
} }

View File

@@ -44,7 +44,7 @@ namespace OpenRA.Mods.RA.Buildings
public void OnCrush(Actor crusher) public void OnCrush(Actor crusher)
{ {
self.Kill(crusher); self.Kill(crusher);
Sound.Play(info.CrushSound, self.CenterLocation); Sound.Play(info.CrushSound, self.CenterPosition);
} }
} }
} }

View File

@@ -70,7 +70,7 @@ namespace OpenRA.Mods.RA
self.World.Add(args.weapon.Projectile.Create(args)); self.World.Add(args.weapon.Projectile.Create(args));
if (args.weapon.Report != null && args.weapon.Report.Any()) if (args.weapon.Report != null && args.weapon.Report.Any())
Sound.Play(args.weapon.Report.Random(self.World.SharedRandom), self.CenterLocation); Sound.Play(args.weapon.Report.Random(self.World.SharedRandom), self.CenterPosition);
} }
} }
} }

View File

@@ -51,7 +51,7 @@ namespace OpenRA.Mods.RA
public void Uncloak(int time) public void Uncloak(int time)
{ {
if (Cloaked) if (Cloaked)
Sound.Play(info.UncloakSound, self.CenterLocation); Sound.Play(info.UncloakSound, self.CenterPosition);
remainingTime = Math.Max(remainingTime, time); remainingTime = Math.Max(remainingTime, time);
} }
@@ -86,7 +86,7 @@ namespace OpenRA.Mods.RA
{ {
if (remainingTime > 0 && canCloak) if (remainingTime > 0 && canCloak)
if (--remainingTime <= 0) if (--remainingTime <= 0)
Sound.Play(info.CloakSound, self.CenterLocation); Sound.Play(info.CloakSound, self.CenterPosition);
if (self.IsDisabled()) if (self.IsDisabled())
Uncloak(); Uncloak();

View File

@@ -43,11 +43,12 @@ namespace OpenRA.Mods.RA
var isWater = args.destAltitude == 0 && world.GetTerrainInfo(targetTile).IsWater; var isWater = args.destAltitude == 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, args.dest.ToWPos(args.destAltitude), explosionType))); w => w.Add(new Explosion(w, dest, explosionType)));
Sound.Play(GetImpactSound(warhead, isWater), args.dest); Sound.Play(GetImpactSound(warhead, isWater), dest);
var smudgeLayers = world.WorldActor.TraitsImplementing<SmudgeLayer>().ToDictionary(x => x.Info.Type); var smudgeLayers = world.WorldActor.TraitsImplementing<SmudgeLayer>().ToDictionary(x => x.Info.Type);
@@ -156,7 +157,7 @@ namespace OpenRA.Mods.RA
}; };
if (args.weapon.Report != null && args.weapon.Report.Any()) if (args.weapon.Report != null && args.weapon.Report.Any())
Sound.Play(args.weapon.Report.Random(attacker.World.SharedRandom), pxPos); Sound.Play(args.weapon.Report.Random(attacker.World.SharedRandom), pos);
DoImpacts(args); DoImpacts(args);
} }

View File

@@ -52,7 +52,7 @@ namespace OpenRA.Mods.RA.Crates
w.Remove(collector); w.Remove(collector);
collector.AddTrait(cloak); collector.AddTrait(cloak);
var t = collector.TraitOrDefault<TargetableUnit<TargetableUnitInfo>>(); var t = collector.TraitOrDefault<TargetableUnit>();
if (t != null) t.ReceivedCloak(collector); if (t != null) t.ReceivedCloak(collector);
w.Add(collector); w.Add(collector);

View File

@@ -47,7 +47,7 @@ namespace OpenRA.Mods.RA
public void OnCrush(Actor crusher) public void OnCrush(Actor crusher)
{ {
Sound.Play(Info.CrushSound, crusher.CenterLocation); Sound.Play(Info.CrushSound, crusher.CenterPosition);
ri.SpawnCorpse(self, Info.CorpseSequence); ri.SpawnCorpse(self, Info.CorpseSequence);
self.Kill(crusher); self.Kill(crusher);
} }

View File

@@ -28,8 +28,10 @@ namespace OpenRA.Mods.RA
{ {
var target = Target.FromActor(order.TargetActor); var target = Target.FromActor(order.TargetActor);
self.SetTargetLine(target, Color.Yellow); self.SetTargetLine(target, Color.Yellow);
var range = WRange.FromCells(target.Actor.Info.Traits.Get<GuardableInfo>().Range);
self.QueueActivity(false, new AttackMove.AttackMoveActivity(self, self.QueueActivity(false, new AttackMove.AttackMoveActivity(self,
new Follow(target, target.Actor.Info.Traits.Get<GuardableInfo>().Range))); new Follow(target, range)));
} }
} }

View File

@@ -191,14 +191,14 @@ namespace OpenRA.Mods.RA.Missions
if (einstein != null) if (einstein != null)
{ {
if (einstein.IsInWorld) if (einstein.IsInWorld)
innerActivity = new Move.Move(Target.FromActor(einstein), 3); innerActivity = new Move.Move(Target.FromActor(einstein), WRange.FromCells(3));
else else
{ {
var container = world.UnitContaining(einstein); var container = world.UnitContaining(einstein);
if (container != null && !container.HasTrait<Aircraft>() && container.HasTrait<Mobile>()) if (container != null && !container.HasTrait<Aircraft>() && container.HasTrait<Mobile>())
innerActivity = new Move.Move(Target.FromActor(container), 3); innerActivity = new Move.Move(Target.FromActor(container), WRange.FromCells(3));
else else
innerActivity = new Move.Move(extractionLZ.Location, 3); innerActivity = new Move.Move(extractionLZ.Location, 3);

View File

@@ -516,7 +516,7 @@ namespace OpenRA.Mods.RA.Move
public Activity ScriptedMove(CPos cell) { return new Move(cell); } public Activity ScriptedMove(CPos cell) { return new Move(cell); }
public Activity MoveTo(CPos cell, int nearEnough) { return new Move(cell, nearEnough); } public Activity MoveTo(CPos cell, int nearEnough) { return new Move(cell, nearEnough); }
public Activity MoveTo(CPos cell, Actor ignoredActor) { return new Move(cell, ignoredActor); } public Activity MoveTo(CPos cell, Actor ignoredActor) { return new Move(cell, ignoredActor); }
public Activity MoveWithinRange(Target target, int range) { return new Move(target, range); } public Activity MoveWithinRange(Target target, WRange range) { return new Move(target, range); }
public Activity MoveTo(Func<List<CPos>> pathFunc) { return new Move(pathFunc); } public Activity MoveTo(Func<List<CPos>> pathFunc) { return new Move(pathFunc); }
} }
} }

View File

@@ -21,11 +21,16 @@ namespace OpenRA.Mods.RA.Move
class Move : Activity class Move : Activity
{ {
CPos? destination; CPos? destination;
int nearEnough; WRange nearEnough;
public List<CPos> path; public List<CPos> path;
Func<Actor, Mobile, List<CPos>> getPath; Func<Actor, Mobile, List<CPos>> getPath;
public Actor ignoreBuilding; public Actor ignoreBuilding;
// For dealing with blockers
bool hasWaited;
bool hasNotifiedBlocker;
int waitTicksRemaining;
// Scriptable move order // Scriptable move order
// Ignores lane bias and nearby units // Ignores lane bias and nearby units
public Move(CPos destination) public Move(CPos destination)
@@ -35,10 +40,14 @@ namespace OpenRA.Mods.RA.Move
PathSearch.FromPoint( self.World, mobile.Info, self, mobile.toCell, destination, false ) PathSearch.FromPoint( self.World, mobile.Info, self, mobile.toCell, destination, false )
.WithoutLaneBias()); .WithoutLaneBias());
this.destination = destination; this.destination = destination;
this.nearEnough = 0; this.nearEnough = WRange.Zero;
} }
// Hack for legacy code
public Move(CPos destination, int nearEnough) public Move(CPos destination, int nearEnough)
: this(destination, WRange.FromCells(nearEnough)) {}
public Move(CPos destination, WRange nearEnough)
{ {
this.getPath = (self,mobile) => self.World.WorldActor.Trait<PathFinder>().FindUnitPath( mobile.toCell, destination, self ); this.getPath = (self,mobile) => self.World.WorldActor.Trait<PathFinder>().FindUnitPath( mobile.toCell, destination, self );
this.destination = destination; this.destination = destination;
@@ -54,12 +63,12 @@ namespace OpenRA.Mods.RA.Move
); );
this.destination = destination; this.destination = destination;
this.nearEnough = 0; this.nearEnough = WRange.Zero;
this.ignoreBuilding = ignoreBuilding; this.ignoreBuilding = ignoreBuilding;
} }
static readonly List<CPos> NoPath = new List<CPos>(); static readonly List<CPos> NoPath = new List<CPos>();
public Move(Target target, int range) public Move(Target target, WRange range)
{ {
this.getPath = (self, mobile) => this.getPath = (self, mobile) =>
{ {
@@ -67,7 +76,7 @@ namespace OpenRA.Mods.RA.Move
return NoPath; return NoPath;
return self.World.WorldActor.Trait<PathFinder>().FindUnitPathToRange( return self.World.WorldActor.Trait<PathFinder>().FindUnitPathToRange(
mobile.toCell, target.CenterPosition.ToCPos(), range, self); mobile.toCell, mobile.toSubCell, target.CenterPosition, range, self);
}; };
this.destination = null; this.destination = null;
@@ -78,7 +87,7 @@ namespace OpenRA.Mods.RA.Move
{ {
this.getPath = (_1,_2) => getPath(); this.getPath = (_1,_2) => getPath();
this.destination = null; this.destination = null;
this.nearEnough = 0; this.nearEnough = WRange.Zero;
} }
static int HashList<T>(List<T> xs) static int HashList<T>(List<T> xs)
@@ -95,8 +104,6 @@ namespace OpenRA.Mods.RA.Move
{ {
var path = getPath(self, mobile).TakeWhile(a => a != mobile.toCell).ToList(); var path = getPath(self, mobile).TakeWhile(a => a != mobile.toCell).ToList();
mobile.PathHash = HashList(path); mobile.PathHash = HashList(path);
Log.Write("debug", "EvalPathHash #{0} {1}",
self.ActorID, mobile.PathHash);
return path; return path;
} }
@@ -109,6 +116,7 @@ namespace OpenRA.Mods.RA.Move
{ {
if (mobile.Altitude < info.Altitude) if (mobile.Altitude < info.Altitude)
++mobile.Altitude; ++mobile.Altitude;
return this; return this;
} }
@@ -155,7 +163,8 @@ namespace OpenRA.Mods.RA.Move
Util.BetweenCells(mobile.fromCell, mobile.toCell) + (MobileInfo.SubCellOffsets[mobile.fromSubCell] + MobileInfo.SubCellOffsets[mobile.toSubCell]) / 2, Util.BetweenCells(mobile.fromCell, mobile.toCell) + (MobileInfo.SubCellOffsets[mobile.fromSubCell] + MobileInfo.SubCellOffsets[mobile.toSubCell]) / 2,
mobile.Facing, mobile.Facing,
mobile.Facing, mobile.Facing,
0 ); 0
);
return move; return move;
} }
@@ -171,10 +180,6 @@ namespace OpenRA.Mods.RA.Move
throw new InvalidOperationException("(Move) Sanity check failed"); throw new InvalidOperationException("(Move) Sanity check failed");
} }
bool hasWaited;
bool hasNotifiedBlocker;
int waitTicksRemaining;
void NotifyBlocker(Actor self, CPos nextCell) void NotifyBlocker(Actor self, CPos nextCell)
{ {
foreach (var blocker in self.World.ActorMap.GetUnitsAt(nextCell)) foreach (var blocker in self.World.ActorMap.GetUnitsAt(nextCell))
@@ -191,22 +196,30 @@ namespace OpenRA.Mods.RA.Move
Pair<CPos, SubCell>? PopPath(Actor self, Mobile mobile) Pair<CPos, SubCell>? PopPath(Actor self, Mobile mobile)
{ {
if( path.Count == 0 ) return null; if (path.Count == 0)
return null;
var nextCell = path[path.Count - 1]; var nextCell = path[path.Count - 1];
// Next cell in the move is blocked by another actor
if (!mobile.CanEnterCell(nextCell, ignoreBuilding, true)) if (!mobile.CanEnterCell(nextCell, ignoreBuilding, true))
{ {
if( ( mobile.toCell - destination.Value ).LengthSquared <= nearEnough ) // Are we close enough?
var cellRange = nearEnough.Range / 1024;
if ((mobile.toCell - destination.Value).LengthSquared <= cellRange*cellRange)
{ {
path.Clear(); path.Clear();
return null; return null;
} }
// See if they will move
if (!hasNotifiedBlocker) if (!hasNotifiedBlocker)
{ {
NotifyBlocker(self, nextCell); NotifyBlocker(self, nextCell);
hasNotifiedBlocker = true; hasNotifiedBlocker = true;
} }
// Wait a bit to see if they leave
if (!hasWaited) if (!hasWaited)
{ {
var info = self.Info.Traits.Get<MobileInfo>(); var info = self.Info.Traits.Get<MobileInfo>();
@@ -223,6 +236,7 @@ namespace OpenRA.Mods.RA.Move
return null; return null;
} }
// Calculate a new path
mobile.RemoveInfluence(); mobile.RemoveInfluence();
var newPath = EvalPath(self, mobile); var newPath = EvalPath(self, mobile);
mobile.AddInfluence(); mobile.AddInfluence();
@@ -232,6 +246,7 @@ namespace OpenRA.Mods.RA.Move
return null; return null;
} }
hasNotifiedBlocker = false; hasNotifiedBlocker = false;
hasWaited = false; hasWaited = false;
path.RemoveAt(path.Count - 1); path.RemoveAt(path.Count - 1);
@@ -271,7 +286,7 @@ namespace OpenRA.Mods.RA.Move
this.fromFacing = fromFacing; this.fromFacing = fromFacing;
this.toFacing = toFacing; this.toFacing = toFacing;
this.moveFraction = startingFraction; this.moveFraction = startingFraction;
this.moveFractionTotal = ( ( to - from ) * 3 ).Length; this.moveFractionTotal = 3*(to - from).Length;
} }
public override void Cancel(Actor self) public override void Cancel(Actor self)
@@ -357,7 +372,8 @@ namespace OpenRA.Mods.RA.Move
Util.BetweenCells(mobile.toCell, nextCell.Value.First) + (toSubcellOffset + nextSubcellOffset) / 2, Util.BetweenCells(mobile.toCell, nextCell.Value.First) + (toSubcellOffset + nextSubcellOffset) / 2,
mobile.Facing, mobile.Facing,
Util.GetNearestFacing(mobile.Facing, Util.GetFacing(nextCell.Value.First - mobile.toCell, mobile.Facing)), Util.GetNearestFacing(mobile.Facing, Util.GetFacing(nextCell.Value.First - mobile.toCell, mobile.Facing)),
moveFraction - moveFractionTotal ); moveFraction - moveFractionTotal
);
mobile.SetLocation(mobile.toCell, mobile.toSubCell, nextCell.Value.First, nextCell.Value.Second); mobile.SetLocation(mobile.toCell, mobile.toSubCell, nextCell.Value.First, nextCell.Value.Second);
return ret; return ret;
@@ -372,7 +388,8 @@ namespace OpenRA.Mods.RA.Move
Util.CenterOfCell(mobile.toCell) + toSubcellOffset, Util.CenterOfCell(mobile.toCell) + toSubcellOffset,
mobile.Facing, mobile.Facing,
mobile.Facing, mobile.Facing,
moveFraction - moveFractionTotal ); moveFraction - moveFractionTotal
);
mobile.EnteringCell(self); mobile.EnteringCell(self);
mobile.SetLocation(mobile.toCell, mobile.toSubCell, mobile.toCell, mobile.toSubCell); mobile.SetLocation(mobile.toCell, mobile.toSubCell, mobile.toCell, mobile.toSubCell);
@@ -383,9 +400,7 @@ namespace OpenRA.Mods.RA.Move
class MoveSecondHalf : MovePart class MoveSecondHalf : MovePart
{ {
public MoveSecondHalf(Move move, PPos from, PPos to, int fromFacing, int toFacing, int startingFraction) public MoveSecondHalf(Move move, PPos from, PPos to, int fromFacing, int toFacing, int startingFraction)
: base( move, from, to, fromFacing, toFacing, startingFraction ) : base(move, from, to, fromFacing, toFacing, startingFraction) {}
{
}
protected override MovePart OnComplete(Actor self, Mobile mobile, Move parent) protected override MovePart OnComplete(Actor self, Mobile mobile, Move parent)
{ {
@@ -401,7 +416,8 @@ namespace OpenRA.Mods.RA.Move
{ {
public static bool IsMoving(this Actor self) public static bool IsMoving(this Actor self)
{ {
if (self.IsIdle) return false; if (self.IsIdle)
return false;
Activity a = self.GetCurrentActivity(); Activity a = self.GetCurrentActivity();
Debug.Assert(a != null); Debug.Assert(a != null);

View File

@@ -68,17 +68,27 @@ namespace OpenRA.Mods.RA.Move
} }
} }
public List<CPos> FindUnitPathToRange(CPos src, CPos target, int range, Actor self) public List<CPos> FindUnitPathToRange(CPos src, SubCell srcSub, WPos target, WRange range, Actor self)
{ {
using (new PerfSample("Pathfinder")) using (new PerfSample("Pathfinder"))
{ {
var mi = self.Info.Traits.Get<MobileInfo>(); var mi = self.Info.Traits.Get<MobileInfo>();
var tilesInRange = world.FindTilesInCircle(target, range) var targetCell = target.ToCPos();
.Where(t => mi.CanEnterCell(self.World, self, t, null, true, true)); var rangeSquared = range.Range*range.Range;
// Correct for SubCell offset
var so = MobileInfo.SubCellOffsets[srcSub];
target -= new WVec(so.X * 1024 / Game.CellSize, so.Y * 1024 / Game.CellSize, 0);
// Select only the tiles that are within range from the requested SubCell
// This assumes that the SubCell does not change during the path traversal
var tilesInRange = world.FindTilesInCircle(targetCell, range.Range / 1024 + 1)
.Where(t => (t.CenterPosition - target).LengthSquared <= rangeSquared
&& mi.CanEnterCell(self.World, self, t, null, true, true));
var path = FindBidiPath( var path = FindBidiPath(
PathSearch.FromPoints(world, mi, self, tilesInRange, src, true), PathSearch.FromPoints(world, mi, self, tilesInRange, src, true),
PathSearch.FromPoint(world, mi, self, src, target, true).InReverse() PathSearch.FromPoint(world, mi, self, src, targetCell, true).InReverse()
); );
return path; return path;

View File

@@ -53,7 +53,7 @@ namespace OpenRA.Mods.RA
var a = cargo.Unload(self); var a = cargo.Unload(self);
self.World.AddFrameEndTask(w => w.Add(new Parachute(a, self.CenterPosition))); self.World.AddFrameEndTask(w => w.Add(new Parachute(a, self.CenterPosition)));
Sound.Play(info.ChuteSound, self.CenterLocation); Sound.Play(info.ChuteSound, self.CenterPosition);
} }
} }
} }

View File

@@ -56,7 +56,7 @@ namespace OpenRA.Mods.RA
if (playSounds) if (playSounds)
foreach (var s in buildingInfo.BuildSounds) foreach (var s in buildingInfo.BuildSounds)
Sound.PlayToPlayer(order.Player, s, building.CenterLocation); Sound.PlayToPlayer(order.Player, s, building.CenterPosition);
playSounds = false; playSounds = false;
} }
} }
@@ -74,7 +74,7 @@ namespace OpenRA.Mods.RA
new OwnerInit( order.Player ), new OwnerInit( order.Player ),
}); });
foreach (var s in buildingInfo.BuildSounds) foreach (var s in buildingInfo.BuildSounds)
Sound.PlayToPlayer(order.Player, s, building.CenterLocation); Sound.PlayToPlayer(order.Player, s, building.CenterPosition);
} }
PlayBuildAnim( self, unit ); PlayBuildAnim( self, unit );

View File

@@ -29,7 +29,7 @@ namespace OpenRA.Mods.RA.Render
public void PlayCharge(Actor self) public void PlayCharge(Actor self)
{ {
Sound.Play(info.ChargeAudio, self.CenterLocation); Sound.Play(info.ChargeAudio, self.CenterPosition);
anim.PlayThen(NormalizeSequence(self, "active"), anim.PlayThen(NormalizeSequence(self, "active"),
() => anim.PlayRepeating(NormalizeSequence(self, "idle"))); () => anim.PlayRepeating(NormalizeSequence(self, "idle")));
} }

View File

@@ -69,7 +69,7 @@ namespace OpenRA.Mods.RA
var target = Target.FromOrder(order); var target = Target.FromOrder(order);
self.CancelActivity(); self.CancelActivity();
self.QueueActivity(mobile.MoveWithinRange(target, info.CloseEnough)); self.QueueActivity(mobile.MoveWithinRange(target, new WRange(1024*info.CloseEnough)));
self.QueueActivity(new Repair(order.TargetActor)); self.QueueActivity(new Repair(order.TargetActor));
self.SetTargetLine(target, Color.Green, false); self.SetTargetLine(target, Color.Green, false);

View File

@@ -39,7 +39,7 @@ namespace OpenRA.Mods.RA
{ {
self.Trait<RenderBuilding>().PlayCustomAnim(self, "active"); self.Trait<RenderBuilding>().PlayCustomAnim(self, "active");
Sound.Play("ironcur9.aud", order.TargetLocation.ToPPos()); Sound.Play("ironcur9.aud", order.TargetLocation.CenterPosition);
foreach (var target in UnitsInRange(order.TargetLocation) foreach (var target in UnitsInRange(order.TargetLocation)
.Where(a => a.Owner.Stances[self.Owner] == Stance.Ally)) .Where(a => a.Owner.Stances[self.Owner] == Stance.Ally))

View File

@@ -15,25 +15,30 @@ using OpenRA.Traits;
namespace OpenRA.Mods.RA namespace OpenRA.Mods.RA
{ {
class TargetableBuildingInfo : ITraitInfo, Requires<BuildingInfo> public class TargetableBuildingInfo : ITraitInfo, Requires<BuildingInfo>
{ {
public readonly string[] TargetTypes = { }; public readonly string[] TargetTypes = { };
public object Create( ActorInitializer init ) { return new TargetableBuilding( this ); } public object Create(ActorInitializer init) { return new TargetableBuilding(init.self, this); }
} }
class TargetableBuilding : ITargetable public class TargetableBuilding : ITargetable
{ {
readonly TargetableBuildingInfo info; readonly TargetableBuildingInfo info;
readonly Building building;
public TargetableBuilding( TargetableBuildingInfo info ) { this.info = info; } public TargetableBuilding(Actor self, TargetableBuildingInfo info)
{
this.info = info;
building = self.Trait<Building>();
}
public string[] TargetTypes { get { return info.TargetTypes; } } public string[] TargetTypes { get { return info.TargetTypes; } }
public bool TargetableBy(Actor self, Actor byActor) { return true; } public bool TargetableBy(Actor self, Actor byActor) { return true; }
public IEnumerable<CPos> TargetableCells( Actor self ) public IEnumerable<WPos> TargetablePositions(Actor self)
{ {
return self.Trait<Building>().OccupiedCells().Select(c => c.First); return building.OccupiedCells().Select(c => c.First.CenterPosition);
} }
} }
} }

View File

@@ -20,14 +20,19 @@ namespace OpenRA.Mods.RA
public override object Create(ActorInitializer init) { return new TargetableSubmarine(init.self, this); } public override object Create(ActorInitializer init) { return new TargetableSubmarine(init.self, this); }
} }
public class TargetableSubmarine : TargetableUnit<TargetableSubmarineInfo> public class TargetableSubmarine : TargetableUnit
{ {
readonly TargetableSubmarineInfo info;
public TargetableSubmarine(Actor self, TargetableSubmarineInfo info) public TargetableSubmarine(Actor self, TargetableSubmarineInfo info)
: base(self, info) {} : base(self, info)
{
this.info = info;
}
public override string[] TargetTypes public override string[] TargetTypes
{ {
get { return Cloak.Cloaked ? info.CloakedTargetTypes get { return cloak.Cloaked ? info.CloakedTargetTypes
: info.TargetTypes;} : info.TargetTypes;}
} }
} }

View File

@@ -18,16 +18,15 @@ namespace OpenRA.Mods.RA
{ {
public readonly string[] TargetTypes = { }; public readonly string[] TargetTypes = { };
public virtual object Create( ActorInitializer init ) { return new TargetableUnit<TargetableUnitInfo>( init.self, this ); } public virtual object Create(ActorInitializer init) { return new TargetableUnit(init.self, this); }
} }
public class TargetableUnit<Info> : ITargetable public class TargetableUnit : ITargetable
where Info : TargetableUnitInfo
{ {
protected readonly Info info; readonly TargetableUnitInfo info;
protected Cloak Cloak; protected Cloak cloak;
public TargetableUnit( Actor self, Info info ) public TargetableUnit(Actor self, TargetableUnitInfo info)
{ {
this.info = info; this.info = info;
ReceivedCloak(self); ReceivedCloak(self);
@@ -36,15 +35,15 @@ namespace OpenRA.Mods.RA
// Arbitrary units can receive cloak via a crate during gameplay // Arbitrary units can receive cloak via a crate during gameplay
public void ReceivedCloak(Actor self) public void ReceivedCloak(Actor self)
{ {
Cloak = self.TraitOrDefault<Cloak>(); cloak = self.TraitOrDefault<Cloak>();
} }
public virtual bool TargetableBy(Actor self, Actor byActor) public virtual bool TargetableBy(Actor self, Actor byActor)
{ {
if (Cloak == null) if (cloak == null || !cloak.Cloaked)
return true; return true;
if (!Cloak.Cloaked || self.Owner == byActor.Owner || self.Owner.Stances[byActor.Owner] == Stance.Ally) if (self.Owner.IsAlliedWith(byActor.Owner))
return true; return true;
return self.World.ActorsWithTrait<DetectCloaked>().Any(a => (self.Location - a.Actor.Location).Length < a.Actor.Info.Traits.Get<DetectCloakedInfo>().Range); return self.World.ActorsWithTrait<DetectCloaked>().Any(a => (self.Location - a.Actor.Location).Length < a.Actor.Info.Traits.Get<DetectCloakedInfo>().Range);
@@ -52,9 +51,9 @@ namespace OpenRA.Mods.RA
public virtual string[] TargetTypes { get { return info.TargetTypes; } } public virtual string[] TargetTypes { get { return info.TargetTypes; } }
public virtual IEnumerable<CPos> TargetableCells( Actor self ) public virtual IEnumerable<WPos> TargetablePositions(Actor self)
{ {
yield return self.CenterPosition.ToCPos(); yield return self.CenterPosition;
} }
} }
} }