void _rushTo(AWizard self, AWizard opp) { // TODO: check angle if (self.CanUseFrostBolt() && self.GetDistanceTo(opp) <= Game.WizardCastRange + opp.Radius) { opp.ApplyMagicalDamage(self.FrostBoltDamage); opp.RemainingFrozen = Game.FrozenDurationTicks; self.RemainingFrostBoltCooldownTicks = Game.FrostBoltCooldownTicks; self.RemainingActionCooldownTicks = Game.WizardActionCooldownTicks; } if (self.CanUseStaff() && self.GetDistanceTo(opp) <= Game.StaffRange + opp.Radius) { opp.ApplyDamage(self.StaffDamage); self.RemainingStaffCooldownTicks = Game.StaffCooldownTicks; self.RemainingActionCooldownTicks = Game.WizardActionCooldownTicks; } if (self.CanUseMagicMissile() && self.GetDistanceTo(opp) <= Game.WizardCastRange + opp.Radius) { opp.ApplyMagicalDamage(self.MagicMissileDamage); self.RemainingMagicMissileCooldownTicks = self.MmSkillLevel == 5 ? 0 : Game.MagicMissileCooldownTicks; self.RemainingActionCooldownTicks = Game.WizardActionCooldownTicks; } if (self.GetDistanceTo(opp) > Game.StaffRange + opp.Radius) { self.MoveTo(opp, opp); } self.SkipTick(); }
bool TryPreDodgeProjectile() { const int preTicks = 18; var opp = OpponentWizards .OrderBy(x => x.GetDistanceTo2(ASelf)) .FirstOrDefault(x => Math.Min(x.RemainingActionCooldownTicks, x.RemainingMagicMissileCooldownTicks) <= preTicks && x.GetDistanceTo(ASelf) <= x.CastRange + ASelf.Radius + Game.MagicMissileRadius + 7 && Math.Abs(x.GetAngleTo(ASelf)) <= Game.StaffSector /* /2*/ ); if (opp == null) { return(false); } if (Math.Abs(ASelf.GetAngleTo(opp)) < Math.PI / 2 && ASelf.GetDistanceTo(opp) > 500) { var obstacles = Combats.Where(x => x.Id != Self.Id && x.GetDistanceTo(ASelf) < 300).ToArray(); var selSign = 0; double selPriority = int.MaxValue; var requiredAngle = ASelf.GetDistanceTo(opp) <= opp.CastRange + ASelf.Radius ? Math.PI - 2 * ASelf.MaxTurnAngle - 0.001 : Math.PI / 2; for (var sign = -1; sign <= 1; sign += 2) { var my = new AWizard(ASelf); var priority = 0.0; while (Math.Abs(my.GetAngleTo(opp)) < requiredAngle) { my.Angle += sign * my.MaxTurnAngle; priority += 0.1; } for (var i = 0; i < 15; i++) { if (!my.MoveTo(my + Point.ByAngle(my.Angle), null, w => !CheckIntersectionsAndTress(w, obstacles))) { break; } priority--; } if (priority < selPriority) { selPriority = priority; selSign = sign; } } FinalMove.Turn = selSign * 10; return(true); } return(false); }
MovingInfo FindTreeTarget(AWizard self) { var res = new MovingInfo(null, int.MaxValue, new FinalMove(new Move())); var trees = TreesObserver.Trees.Where(t => self.GetDistanceTo(t) <= Game.StaffRange + t.Radius).ToArray(); var minTicks = int.MaxValue; ATree selTarget = null; foreach (var tree in trees) { var my = new AWizard(self); var ticks = 0; while (Math.Abs(my.GetAngleTo(tree)) > Game.StaffSector / 2) { my.MoveTo(null, tree); ticks++; } if (ticks < minTicks && my.CanStaffAttack(tree)) { minTicks = ticks; selTarget = tree; } } if (selTarget == null) { return(res); } res.Time = minTicks; res.Target = selTarget; res.TargetId = selTarget.Id; if (minTicks == 0) { res.Move.Action = ActionType.Staff; } else { res.Move.MoveTo(null, selTarget); } return(res); }
MovingInfo _findCastTarget2(AWizard self, Point moveTo, ProjectileType projectileType) { var move = new FinalMove(new Move()); if (projectileType == ProjectileType.MagicMissile) { AUnit mmSelTarget = null; Point mmSelFirstMoveTo = null; var mmMinTicks = int.MaxValue; double mmMinPriority = int.MaxValue; foreach (var opp in OpponentCombats) { if (self.GetDistanceTo2(opp) > Geom.Sqr(self.CastRange + opp.Radius + 40) || !opp.IsAssailable) { continue; } var nearest = Combats .Where(x => self.GetDistanceTo2(x) < Geom.Sqr(Math.Max(x.VisionRange, self.VisionRange) * 1.3)) .Select(Utility.CloneCombat) .ToArray(); var targetsSelector = new TargetsSelector(nearest) { EnableMinionsCache = true }; var nearstOpponents = nearest .Where(x => x.IsOpponent) .ToArray(); var canHitNow = opp.EthalonCanHit(self, checkCooldown: !(opp is AWizard)); var ticks = 0; var my = nearest.FirstOrDefault(x => x.Id == self.Id) as AWizard; var his = nearest.FirstOrDefault(x => x.Id == opp.Id); if (my == null || his == null) { continue; } Point firstMoveTo = null; var buildingsHit = false; while (!my.EthalonCanCastMagicMissile(his, checkCooldown: false)) { if (ticks > 40) { break; } var m = moveTo; var stopIfCannotMove = true; if (m == null && my.EthalonCanCastMagicMissile(his, checkCooldown: false, checkAngle: false)) { stopIfCannotMove = false; m = my + (my - his); var tmp = new AWizard(my); tmp.MoveTo(m, his, w => !CheckIntersectionsAndTress(w, nearest)); if (EstimateDanger(my, false) <= EstimateDanger(tmp, false)) { m = null; } } if (m == null) { m = his; } if (ticks == 0) { firstMoveTo = m; } if (!my.MoveTo(m, his, w => !CheckIntersectionsAndTress(w, nearest)) && Utility.PointsEqual(m, his) && stopIfCannotMove) { break; } foreach (var x in nearest) { if (x.Id == my.Id) { continue; } var tar = targetsSelector.Select(x); buildingsHit = buildingsHit || (x.IsOpponent && x is ABuilding && tar != null && tar.Id == my.Id && x.EthalonCanHit(my)); if (x.IsOpponent) { x.EthalonMove(tar ?? my); } else if (tar != null) { x.EthalonMove(tar); } else { x.SkipTick(); } } ticks++; } if (his is AWizard && (his as AWizard).IsBesieded) { ticks -= 15; // чтобы дать больше приоритета визарду } var priority = GetCombatPriority(self, his); if (ticks < mmMinTicks || ticks == mmMinTicks && priority < mmMinPriority) { if (my.EthalonCanCastMagicMissile(his)) { if (nearstOpponents.All(x => { if (canHitNow && x.Id == opp.Id) // он и так доставал { return(true); } if (!x.EthalonCanHit(my) && (!(x is ABuilding) || !buildingsHit)) { return(true); } if (his.Id == x.Id && CanRush(my, x)) { return(true); } var target = targetsSelector.Select(x); if (target != null && target.Id != my.Id) { return(true); } return(false); }) ) { mmMinTicks = ticks; mmMinPriority = priority; mmSelTarget = opp; mmSelFirstMoveTo = firstMoveTo; } } } } if (mmSelTarget != null) { mmMinTicks = Math.Max(0, mmMinTicks); move.MoveTo(moveTo ?? mmSelFirstMoveTo, mmSelTarget); return(new MovingInfo(mmSelTarget, mmMinTicks, move) { TargetId = mmSelTarget.Id }); } } const int walkLimit = 9; if (projectileType == ProjectileType.Fireball && self.FireballSkillLevel == 5 && Math.Max(self.RemainingActionCooldownTicks, self.RemainingFireballCooldownTicks) <= walkLimit) { var fbMaxDamage = 0.0; Point fbSelTarget = null; var fbMinTicks = int.MaxValue; foreach (var ang in Utility.Range(-Game.StaffSector, Game.StaffSector, 10)) { var nearest = Combats .Where(x => self.GetDistanceTo2(x) < Geom.Sqr(Math.Max(x.VisionRange, self.VisionRange) * 1.3)) .Select(Utility.CloneCombat) .ToArray(); var targetsSelector = new TargetsSelector(nearest) { EnableMinionsCache = true }; var ticks = 0; var my = nearest.FirstOrDefault(x => x.Id == self.Id) as AWizard; var dir = my + Point.ByAngle(my.Angle + ang) * 1000; while (ticks <= walkLimit) { if (my.CanUseFireball()) { var proj = new AProjectile(my, 0, ProjectileType.Fireball); var path = proj.Emulate(nearest, 0.0); var damage = path.Where(x => _isFireballGoodSeg(my, x)) .Select(x => x.OpponentDamage) .DefaultIfEmpty(0) .Max(); if (damage > fbMaxDamage) { fbMaxDamage = damage; fbSelTarget = dir; fbMinTicks = ticks; } } foreach (var x in nearest) { if (x.Id == my.Id) { continue; } if (x is AMinion) { x.EthalonMove(targetsSelector.Select(x)); } else { x.SkipTick(); } } if (!my.MoveTo(dir, dir, w => !CheckIntersectionsAndTress(w, nearest))) { break; } if (nearest.Any(x => x.IsOpponent && x is ABuilding && x.EthalonCanHit(my) && targetsSelector.Select(x) == my)) { break; } ticks++; } } if (fbSelTarget != null) { move.MoveTo(fbSelTarget, fbSelTarget); return(new MovingInfo(fbSelTarget, fbMinTicks, move) { Damage = fbMaxDamage }); } } return(new MovingInfo(null, int.MaxValue, move)); }
MovingInfo FindBonusTarget(AWizard self) { var minTime = int.MaxValue; var selGo = 0; Point selMoveTo = null; foreach (var _bonus in BonusesObserver.Bonuses) { if (_bonus.GetDistanceTo(self) - self.Radius - _bonus.Radius > Game.StaffRange * 3) { continue; } if (_bonus.RemainingAppearanceTicks > 60) { continue; } var nearest = Combats .Where(x => x.Id != self.Id && self.GetDistanceTo2(x) < Geom.Sqr(self.VisionRange)) .ToArray(); foreach (var angle in Utility.Range(self.Angle, Math.PI * 2 + self.Angle, 24, false)) { var bonus = new ABonus(_bonus); var my = new AWizard(self); var moveTo = my + Point.ByAngle(angle) * self.VisionRange; int time = 0; int go = 0; while (my.GetDistanceTo(bonus) > my.Radius + bonus.Radius && time < 60) { if (!my.MoveTo(moveTo, null, w => !CheckIntersectionsAndTress(w, nearest))) { break; } var wait = !bonus.Exists; bonus.SkipTick(); time++; if (my.GetDistanceTo(bonus) <= my.Radius + bonus.Radius) { while (!bonus.Exists) { bonus.SkipTick(); time++; } if (wait) { time++; } if (time < minTime) { minTime = time; selMoveTo = moveTo; selGo = go; } break; } go++; } } } var moving = new MovingInfo(selMoveTo, minTime, new FinalMove(new Move())); if (selMoveTo != null) { if (minTime == 1 || selGo > 0) { moving.Move.MoveTo(selMoveTo, null); } else { moving.Target = self; } } return(moving); }
MovingInfo FindUltimateTarget(AWizard self) { var ret = new MovingInfo(null, int.MaxValue, new FinalMove(new Move())); const int threshold = 29; var minTicks = int.MaxValue; AWizard selTarget = null; var selAction = ActionType.None; foreach (var action in new[] { ActionType.Haste, ActionType.Shield }) { if (action == ActionType.Haste && self.HasteSkillLevel < 5 || action == ActionType.Shield && self.ShieldSkillLevel < 5 ) { continue; } var teammates = MyWizards .Where(x => x.Id != self.Id && self.GetDistanceTo(x) <= self.CastRange && x.RemainingStatusByAction(action) <= threshold ). ToArray(); foreach (var teammate in teammates) { var ticks = 0; var my = new AWizard(self); while (Math.Abs(my.GetAngleTo(teammate)) > Game.StaffSector / 2) { ticks++; my.MoveTo(null, teammate); } if (ticks < minTicks && my.CanCastUltimate(action, teammate)) { minTicks = ticks; selTarget = teammate; selAction = action; } } if (selTarget == null && teammates.Length == 0 && self.RemainingStatusByAction(action) <= threshold && self.CanUseUltimate(action) ) { minTicks = 100; selTarget = self; selAction = action; } } if (selTarget == null) { return(ret); } ret.Target = selTarget; ret.TargetId = selTarget.Id; ret.Time = minTicks; if (self.CanCastUltimate(selAction, selTarget)) { ret.Move.Action = selAction; ret.Move.StatusTargetId = selTarget.Id; } else { ret.Move.MoveTo(null, selTarget); } return(ret); }
bool _tryDodgeProjectile() { var obstacles = Combats.Where(x => x.Id != Self.Id && x.GetDistanceTo(ASelf) < 300).ToArray(); var minTicks = int.MaxValue; var minDamage = 1000.0; Point selMoveTo = null; Point selTurnTo = null; foreach (var doTurn in new[] { false, true }) { foreach (var angle in Utility.Range(0, Math.PI * 2, 40, false)) { if (minTicks == 0 && minDamage < Const.Eps) // ничего не грозит { break; } var ticks = 0; var my = new AWizard(ASelf); var bonus = new ABonus(BonusesObserver.Bonuses.ArgMin(b => b.GetDistanceTo(Self))); var moveTo = my + Point.ByAngle(angle) * 1000; var turnTo = doTurn ? moveTo : null; var myStates = new List <AWizard> { new AWizard(my) }; while (ticks < ProjectilesCheckTicks) { var totalDamage = _getProjectilesDamage(myStates); if (Utility.Less(totalDamage, minDamage) || Utility.Equals(totalDamage, minDamage) && ticks < minTicks) { minTicks = ticks; minDamage = totalDamage; selMoveTo = moveTo; selTurnTo = turnTo; } bonus.SkipTick(); my.MoveTo(moveTo, turnTo, w => { if (CheckIntersectionsAndTress(w, obstacles)) { return(false); } if (bonus.RemainingAppearanceTicks < 15 && bonus.IntersectsWith(w)) { return(false); } return(true); }); myStates.Add(new AWizard(my)); ticks++; } } } if (minTicks == 0 || minTicks == int.MaxValue) // нет необходимости уворачиваться { return(false); } if (selTurnTo != null || Math.Abs(ASelf.GetAngleTo(selMoveTo)) < Math.PI / 2) { FinalMove.Turn = 0; } FinalMove.MoveTo(selMoveTo, selTurnTo); return(true); }