public LeapAttack(Actor self, Target target, bool allowMovement, bool forceAttack, AttackLeap attack, AttackLeapInfo info) { this.target = target; this.info = info; this.attack = attack; this.allowMovement = allowMovement; this.forceAttack = forceAttack; mobile = self.Trait <Mobile>(); // The target may become hidden between the initial order request and the first tick (e.g. if queued) // Moving to any position (even if quite stale) is still better than immediately giving up if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner)) || target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain) { lastVisibleTarget = Target.FromPos(target.CenterPosition); lastVisibleMinRange = attack.GetMinimumRangeVersusTarget(target); lastVisibleMaxRange = attack.GetMaximumRangeVersusTarget(target); if (target.Type == TargetType.Actor) { lastVisibleOwner = target.Actor.Owner; lastVisibleTargetTypes = target.Actor.GetEnabledTargetTypes(); } else if (target.Type == TargetType.FrozenActor) { lastVisibleOwner = target.FrozenActor.Owner; lastVisibleTargetTypes = target.FrozenActor.TargetTypes; } } }
public override Activity Tick(Actor self) { // Run this even if the target became invalid to avoid visual glitches if (ChildActivity != null) { ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity); return(this); } if (IsCanceling) { return(NextActivity); } bool targetIsHiddenActor; target = target.Recalculate(self.Owner, out targetIsHiddenActor); if (!targetIsHiddenActor && target.Type == TargetType.Actor) { lastVisibleTarget = Target.FromTargetPositions(target); lastVisibleMinRange = attack.GetMinimumRangeVersusTarget(target); lastVisibleMaxRange = attack.GetMaximumRangeVersusTarget(target); lastVisibleOwner = target.Actor.Owner; lastVisibleTargetTypes = target.Actor.GetEnabledTargetTypes(); } var oldUseLastVisibleTarget = useLastVisibleTarget; useLastVisibleTarget = targetIsHiddenActor || !target.IsValidFor(self); // Update target lines if required if (useLastVisibleTarget != oldUseLastVisibleTarget) { self.SetTargetLine(useLastVisibleTarget ? lastVisibleTarget : target, Color.Red, false); } // Target is hidden or dead, and we don't have a fallback position to move towards if (useLastVisibleTarget && !lastVisibleTarget.IsValidFor(self)) { return(NextActivity); } var pos = self.CenterPosition; var checkTarget = useLastVisibleTarget ? lastVisibleTarget : target; if (!checkTarget.IsInRange(pos, lastVisibleMaxRange) || checkTarget.IsInRange(pos, lastVisibleMinRange)) { if (!allowMovement || lastVisibleMaxRange == WDist.Zero || lastVisibleMaxRange < lastVisibleMinRange) { return(NextActivity); } QueueChild(self, mobile.MoveWithinRange(target, lastVisibleMinRange, lastVisibleMaxRange, checkTarget.CenterPosition, Color.Red), true); return(this); } // Ready to leap, but target isn't visible if (targetIsHiddenActor || target.Type != TargetType.Actor) { return(NextActivity); } // Target is not valid if (!target.IsValidFor(self) || !attack.HasAnyValidWeapons(target)) { return(NextActivity); } var edible = target.Actor.TraitOrDefault <EdibleByLeap>(); if (edible == null || !edible.CanLeap(self)) { return(NextActivity); } // Can't leap yet if (attack.Armaments.All(a => a.IsReloading)) { return(this); } // Use CenterOfSubCell with ToSubCell instead of target.Centerposition // to avoid continuous facing adjustments as the target moves var targetMobile = target.Actor.TraitOrDefault <Mobile>(); var targetSubcell = targetMobile != null ? targetMobile.ToSubCell : SubCell.Any; var destination = self.World.Map.CenterOfSubCell(target.Actor.Location, targetSubcell); var origin = self.World.Map.CenterOfSubCell(self.Location, mobile.FromSubCell); var desiredFacing = (destination - origin).Yaw.Facing; if (mobile.Facing != desiredFacing) { QueueChild(self, new Turn(self, desiredFacing), true); return(this); } QueueChild(self, new Leap(self, target, mobile, targetMobile, info.Speed.Length, attack, edible), true); // Re-queue the child activities to kill the target if it didn't die in one go return(this); }