bool TryCutTrees(bool cutNearest, FinalMove move) { var self = new AWizard(ASelf); var nearestTrees = TreesObserver.Trees.Where( t => self.GetDistanceTo(t) < self.CastRange + t.Radius + Game.MagicMissileRadius ).ToArray(); if (nearestTrees.Length == 0) { return(false); } if (self.RemainingActionCooldownTicks == 0) { if (self.GetStaffAttacked(nearestTrees).Length > 0) { move.Action = ActionType.Staff; return(true); } if (self.RemainingMagicMissileCooldownTicks == 0) { var proj = new AProjectile(self, 0, ProjectileType.MagicMissile); var path = EmulateProjectileWithNearest(proj); if (path.Count == 0 || path[path.Count - 1].EndDistance < self.CastRange - Const.Eps) { move.MinCastDistance = path[path.Count - 1].EndDistance; move.Action = ActionType.MagicMissile; return(true); } } } if (cutNearest) { var nearest = nearestTrees.OrderBy(t => self.GetDistanceTo2(t)).FirstOrDefault(); move.MoveTo(null, nearest); } return(false); }
MovingInfo _findStaffTarget(AWizard self) { var potentialColliders = Combats .Where(x => x.Id != self.Id && self.GetDistanceTo2(x) < Geom.Sqr(Game.StaffRange * 6)) .ToArray(); int minTicks = int.MaxValue; var move = new FinalMove(new Move()); var attacked = self.GetStaffAttacked(potentialColliders).Cast <ACombatUnit>().ToArray(); ACircularUnit selTarget = attacked.FirstOrDefault(x => x.IsOpponent); if (selTarget != null) // если уже можно бить { move.Action = ActionType.Staff; return(new MovingInfo(selTarget, 0, move)); } if (self.MmSkillLevel == 5) { // т.к. стрелять можно без задержки // возможно, нужно сделать исключение, если прокачан посох return(new MovingInfo(null, int.MaxValue, move)); } Point selMoveTo = null; foreach (var opp in OpponentCombats) { var dist = self.GetDistanceTo(opp); if (dist > Game.StaffRange * 5 || !opp.IsAssailable) { continue; } var range = opp.Radius + Game.StaffRange; foreach (var delta in new[] { -range, -range / 2, 0, range / 2, range }) { var angle = Math.Atan2(delta, dist); var moveTo = self + (opp - self).Normalized().RotateClockwise(angle) * self.VisionRange; var nearstCombats = Combats .Where(x => x.GetDistanceTo(self) <= Math.Max(x.VisionRange, self.VisionRange) * 1.2) .Select(Utility.CloneCombat) .ToArray(); var targetsSelector = new TargetsSelector(nearstCombats) { EnableMinionsCache = true }; var nearstOpponents = nearstCombats.Where(x => x.IsOpponent).ToArray(); var my = nearstCombats.FirstOrDefault(x => x.Id == self.Id) as AWizard; var his = nearstCombats.FirstOrDefault(x => x.Id == opp.Id); var allowRush = opp is AFetish || opp is AWizard; var canHitNow = opp.EthalonCanHit(self, checkCooldown: !allowRush); var ticks = 0; var ok = true; var buildingsHit = false; while (ticks < (allowRush ? 65 : 35) && my.GetDistanceTo2(his) > Geom.Sqr(Game.StaffRange + his.Radius)) { foreach (var x in nearstOpponents) // свои как-бы стоят на месте { var tar = targetsSelector.Select(x); buildingsHit = buildingsHit || (x is ABuilding && tar != null && tar.Id == my.Id && x.EthalonCanHit(my)); x.EthalonMove(tar ?? my); } if (!my.MoveTo(moveTo, his, w => !CheckIntersectionsAndTress(w, potentialColliders))) { ok = false; break; } ticks++; } if (ok && !(opp is AOrc)) { while (Math.Abs(my.GetAngleTo(his)) > Game.StaffSector / 2) { my.MoveTo(null, his); foreach (var x in nearstOpponents) { var tar = targetsSelector.Select(x); buildingsHit = buildingsHit || (x is ABuilding && tar != null && tar.Id == my.Id && x.EthalonCanHit(my)); x.EthalonMove(tar ?? my); } ticks++; } } Func <ACombatUnit, bool> check = x => { if ((opp is AWizard) && (opp as AWizard).IsBesieded && !(x is ABuilding)) { return(true); } if (canHitNow && x.Id == opp.Id) // он и так доставал { return(true); } if (!x.EthalonCanHit(my) && (!(x is ABuilding) || !buildingsHit)) { return(true); } if (his.Id == x.Id && my.StaffDamage >= his.Life) { return(true); } var target = targetsSelector.Select(x); if (target != null && target.Id != my.Id) { return(true); } return(false); }; if (opp is AWizard) { ticks -= 5; if ((opp as AWizard).IsBesieded) { ticks -= 10; } } if (ok && ticks < minTicks) { if (my.CanStaffAttack(his)) { if (nearstOpponents.All(check)) { // успею-ли я вернуться обратно while (my.GetDistanceTo(self) > my.MaxForwardSpeed)//TODO:HACK { my.MoveTo(self, null); foreach (var x in nearstOpponents) { var tar = targetsSelector.Select(x); buildingsHit = buildingsHit || (x is ABuilding && tar != null && tar.Id == my.Id && x.EthalonCanHit(my)); if (tar != null) { x.EthalonMove(tar); } else { x.SkipTick(); } } } if (nearstOpponents.All(check)) { selTarget = opp; selMoveTo = moveTo; minTicks = ticks; } } } } } } if (selTarget != null) { bool angleOk = Math.Abs(self.GetAngleTo(selTarget)) <= Game.StaffSector / 2, distOk = self.GetDistanceTo2(selTarget) <= Geom.Sqr(Game.StaffRange + selTarget.Radius); if (!distOk) { move.MoveTo(selMoveTo, selTarget); } else if (!angleOk) { move.MoveTo(null, selTarget); } } return(new MovingInfo(selTarget, Math.Max(0, minTicks), move)); }