MovingInfo _findCastTarget(AWizard self, ProjectileType projectileType) { var actionType = Utility.GetActionByProjectileType(projectileType); var move = new FinalMove(new Move()); if (self.RemainingActionCooldownTicks > 0 || self.RemainingCooldownTicksByAction[(int)actionType] > 0 || self.Mana < Const.ProjectileInfo[(int)projectileType].ManaCost || !self.IsActionAvailable(actionType) ) { return(new MovingInfo(null, int.MaxValue, move)); } var angles = new List <double>(); foreach (var x in OpponentCombats) { var distTo = self.GetDistanceTo(x); if (distTo > self.CastRange + x.Radius + Const.ProjectileInfo[(int)projectileType].DamageRadius + 3) { continue; } var angleTo = self.GetAngleTo(x); if (Math.Abs(angleTo) > Math.PI / 3) { continue; } var deltaAngle = Math.Atan2(x.Radius, distTo); angles.AddRange(new[] { angleTo, angleTo + deltaAngle, angleTo - deltaAngle }.Where(a => Math.Abs(a) <= Game.StaffSector / 2)); } if (angles.Count > 0) { angles.AddRange(Utility.Range(-Game.StaffSector / 2, Game.StaffSector / 2, 16)); } ACombatUnit selTarget = null; double selMinDist = 0, selMaxDist = self.CastRange + 20, selCastAngle = 0, selMaxDamage = 0; if (projectileType == ProjectileType.Fireball) { var maxDamage = 0.0; var maxBurned = 0; foreach (var angle in angles) { var proj = new AProjectile(new AWizard(self), angle, projectileType); var path = EmulateProjectileWithNearest(proj); for (var i = 0; i < path.Count; i++) { var seg = path[i]; if (_isFireballGoodSeg(self, seg)) { if (seg.OpponentBurned > maxBurned || seg.OpponentBurned == maxBurned && seg.OpponentDamage > maxDamage //|| seg.OpponentBurned == maxBurned && Utility.Equals(seg.OpponentDamage, maxDamage) //TODO: combare by angle and priority ) { maxBurned = seg.OpponentBurned; maxDamage = seg.OpponentDamage; selCastAngle = angle; selMinDist = selMaxDist = seg.StartDistance; selTarget = seg.Target; selMaxDamage = seg.OpponentDamage; } } } } } else { double selPriority = int.MaxValue, selAngleTo = 0; foreach (var angle in angles) { var proj = new AProjectile(new AWizard(self), angle, projectileType); var path = EmulateProjectileWithNearest(proj); for (var i = 0; i < path.Count; i++) { if (path[i].State == AProjectile.ProjectilePathState.Free) { continue; } // TODO: если можно убить нескольких, убивать того, у кого больше жизней var combat = path[i].Target; if (!combat.IsAssailable) { continue; } var myAngle = self.Angle + angle; var hisAngle = self.Angle + self.GetAngleTo(combat); var angleTo = Geom.GetAngleBetween(myAngle, hisAngle); var priority = GetCombatPriority(self, combat); if (combat.IsOpponent && (priority < selPriority || Utility.Equals(priority, selPriority) && angleTo < selAngleTo) && self.CheckProjectileCantDodge(proj, Combats.FirstOrDefault(x => x.Id == combat.Id)) ) { selTarget = combat; selCastAngle = angle; selAngleTo = angleTo; selMinDist = i == 0 || path[i - 1].State == AProjectile.ProjectilePathState.Free && path[i - 1].Length < 40 ? path[i].StartDistance - 1 : path[i].StartDistance - 20; selMaxDist = i >= path.Count - 2 ? (self.CastRange + 500) : (path[i + 1].EndDistance + path[i].EndDistance) / 2; selPriority = priority; selMaxDamage = path[i].OpponentDamage; } } } } if (selTarget == null) { return(new MovingInfo(null, int.MaxValue, move)); } move.Action = actionType; move.MinCastDistance = selMinDist; move.MaxCastDistance = selMaxDist; move.CastAngle = selCastAngle; #if DEBUG _lastProjectileTick = World.TickIndex; _lastProjectilePoints = new[] { self + Point.ByAngle(self.Angle + selCastAngle) * selMinDist, self + Point.ByAngle(self.Angle + selCastAngle) * Math.Min(Self.CastRange, selMaxDist), }; #endif return(new MovingInfo(selTarget, 0, move) { Damage = selMaxDamage, TargetId = selTarget.Id }); }