public MapTile[] GeneratePath(Point start, Point end, Map map) { List <MapTile> path = new List <MapTile>(); //DEBUG int counter = 0; Point active = start; path.Add(map.map.Get(active)); while (active != end) { //DEBUG if (++counter == 100) { throw new Exception("Something went wrong!"); } //Find field with lowest value for graph (lowest movement cost from start) List <Point> fields = AIUtility.GetFields(active, this); active = fields.Aggregate((best, field) => { if (path.Exists(f => f.position == field)) { return(best); } double b = graph[best.X, best.Y]; //Do not use Get method for optimization double n = graph[field.X, field.Y]; if (n == b) { int bestDistance = AIUtility.Distance(best, end); int fieldDistance = AIUtility.Distance(field, end); //if they have the same cost take the one which is closer if they are the same distance take the newer return(bestDistance >= fieldDistance ? field : best); } else { if (b > n) { return(field); } else { return(best); } } }); path.Add(map.map.Get(active)); } return(path.ToArray()); }
public override void PlayTurn(MainGameWindow main, bool singleTurn) { //Decrease spell cooldown foreach (Spell spell in spells) { spell.coolDown = spell.coolDown == 0 ? 0 : spell.coolDown - 1; } //Panic if he has been hit or player is close and he is on low health if (troop.health.Value != lastHealth || (AIUtility.Distance(troop.Position, enemies[0].troop.Position) < 4 && troop.health.Value != troop.health.MaxValue().Value)) { lastHealth = troop.health.Value; //If teleport spell is ready if (spells[1].Ready) { //Find heighest free space var HeightSorted = from field in map.map.Cast <MapTile>() where field.free where AIUtility.Distance(field.position, enemies[0].troop.Position) > 10 orderby field.Height descending select field; //Teleport spells[1].Activate(new SpellInformation() { positions = new List <Point>() { troop.Position, HeightSorted.Take(1).ToList()[0].position }, mage = this }); return; //Finish turn } else { main.WriteConsole("The wizard wimpers"); return; } } //Attack if (spells[0].Ready) { spells[0].Activate(new SpellInformation() { positions = new List <Point> { enemies[0].troop.Position }, mage = this }); } }
public override void PlayTurn(MainGameWindow main, bool SingleTurn) { DistanceGraphCreator distanceGraph = new DistanceGraphCreator(this, troop.Position.X, troop.Position.Y, map, false); Thread path = new Thread(distanceGraph.CreateGraph); path.Start(); path.Join(); int damageDealt = 0; int dodged = 0; while (actionPoints.Value > 0) { Point playerPos = enemies[0].troop.Position; //Check if it can attack player int playerDistance = AIUtility.Distance(playerPos, troop.Position); if (playerDistance <= troop.activeWeapon.range && troop.activeWeapon.Attacks() > 0) { //Attack var(damage, killed, hit) = main.Attack(this, enemies[0]); damageDealt += damage; if (!hit) { dodged++; } if (killed) { map.overlayObjects.Add(new OverlayText(enemies[0].troop.Position.X * MapCreator.fieldSize, enemies[0].troop.Position.Y * MapCreator.fieldSize, Color.Red, $"-{damageDealt}")); main.PlayerDied($"You have been killed by {Name}!"); break; } actionPoints.RawValue--; troop.activeWeapon.UseWeapon(enemies[0], main); main.RenderMap(); continue; } else if (troop.weapons.Exists(t => t.range >= playerDistance && t.Attacks() > 0)) { //Change weapon Weapon best = troop.weapons.FindAll(t => t.range >= playerDistance) .Aggregate((t1, t2) => t1.range > t2.range ? t1 : t2); troop.activeWeapon = best; continue; } Point closestField = new Point(-1, -1); try { // Try finding closer field to player closestField = AIUtility.FindClosestField(distanceGraph, playerPos, movementPoints.Value, map, (List <(Point point, double cost, double height)> list) => { list.Sort((o1, o2) => { double diffCost = o1.cost - o2.cost; double heightDiff = o1.height - o2.height; if (Math.Abs(diffCost) >= 1) //assume that using the weapon costs 1 action point { return(diffCost < 0 ? -1 : 1); } else if (heightDiff != 0) { return(diffCost < 0 ? -1 : 1); } return(0); }); return(list.First().point); }); }
public override void PlayTurn(MainGameWindow main, bool SingleTurn) { DistanceGraphCreator distanceGraph = new DistanceGraphCreator(this, troop.Position, map, true); Thread path = new Thread(distanceGraph.CreateGraph); DistanceGraphCreator eggGraph = new DistanceGraphCreator(this, troop.Position, map, true); Thread pathE = new Thread(eggGraph.CreateGraph); path.Start(); pathE.Start(); int damageDealt = 0; int dodged = 0; Point player = enemies[0].troop.Position; int playerDistance = AIUtility.Distance(player, troop.Position); if (!enraged && AIUtility.Distance(troop.Position, player) < 30 && AIUtility.Distance(egg, player) < 20) { enraged = true; main.WriteConsole($"{Name} has become enraged!"); main.Combat -= Update; } void AttackPlayer(bool continues = false) { bool attacked = false; //Check if it can attack player foreach (var weapon in troop.weapons) { if (playerDistance <= weapon.range && weapon.Attacks() > 0) { //Attack troop.activeWeapon = weapon; var(damage, killed, hit) = main.Attack(this, enemies[0]); damageDealt += damage; if (!hit) { dodged++; } if (killed) { map.overlayObjects.Add(new OverlayText(enemies[0].troop.Position.X * MapCreator.fieldSize, enemies[0].troop.Position.Y * MapCreator.fieldSize, Color.Red, $"-{damageDealt}")); main.PlayerDied($"You have been killed by {Name} using {weapon.name}!"); } actionPoints.RawValue--; weapon.UseWeapon(enemies[0], main); attacked = true; } } if (attacked) { AttackPlayer(true); } } if (enraged) { if (fireLineCoolDown == 0 && playerDistance > 5) { //Spew fire //Shoot fire line - will most likely not kill but be in the area Point end = new Point(player.X + (World.World.random.Next(0, 2) == 1 ? -2 : 2), player.Y + (World.World.random.Next(0, 2) == 1 ? -2 : 2)); Point diff = end.Sub(troop.Position); Point start = troop.Position.Add(diff.Div(5)); int diffX = diff.X; int diffY = diff.Y; int time = Math.Max(Math.Abs(diffX), Math.Abs(diffY)); main.actionOccuring = true; lock (map.RenderController) { for (int i = 0; i < time; i++) { Point position = new Point(Math.Max(start.X + (int)(((double)diffX / time) * i), 0), Math.Max(start.Y + (int)(((double)diffY / time) * i), 0)); new Fire(World.World.random.Next(5) + 2, 5, position, troop.Position, map, main); } } main.actionOccuring = false; fireLineCoolDown = maxFireLineCooldown; } else { fireLineCoolDown = fireLineCoolDown == 0 ? 0 : fireLineCoolDown - 1; } if (fireBombPlaces.Count != 0) { lock (map.RenderController) { main.actionOccuring = true; int appliedRadius = 2; //Fire real explosions foreach (var hit in fireBombPlaces) { for (int x = 0; x < appliedRadius * 2 + 1; x++) { for (int y = 0; y < appliedRadius * 2 + 1; y++) { //Check in bounds Point point = new Point(x + hit.X - appliedRadius, y + hit.Y - appliedRadius); if ((point.X < 0 || point.X >= map.map.GetUpperBound(0) - 1) || (point.Y < 0 || point.Y >= map.map.GetUpperBound(1) - 1)) { continue; } int dis = AIUtility.Distance(point, hit); if (dis <= appliedRadius) { //Damage fields new Fire(World.World.random.Next(0, 6), fireDamage, point, point, map, main); } } } } main.actionOccuring = false; } fireBombPlaces.Clear(); } if (fireBombCoolDown == 0) { //Summon fire explosions at certain positions - first one then ring int tries = 0; //Longerm: Create deterministic version while (tries != 100 && fireBombPlaces.Count < bombNumber) { Point test = new Point(World.World.random.Next(0, map.map.GetUpperBound(0)), World.World.random.Next(map.map.GetUpperBound(1))); if (AIUtility.Distance(test, player) < 20 && AIUtility.Distance(test, troop.Position) > 3) { fireBombPlaces.Add(test); } tries++; } if (tries == 100) { E.LogInfo("Did not find locations for fire bombs!"); } else { bombNumber++; } lock (map.RenderController) { foreach (var point in fireBombPlaces) { new Fire(World.World.random.Next(3, 8), fireDamage, point, troop.Position, map, main); } } fireBombCoolDown = maxFireBombCooldown; } else { fireBombCoolDown--; } AttackPlayer(); } //Find field to move to //If enraged go to player //If player is close to camp go to player //Else stay around camp if (actionPoints.Value == 0) { path.Abort(); pathE.Abort(); return; } Point closestField; path.Join(); pathE.Join(); DistanceGraphCreator graph = enraged || AIUtility.Distance(egg, player) < 8 ? distanceGraph : eggGraph; Point goalPos = enraged || AIUtility.Distance(egg, player) < 8 ? player : egg; if (jumpCooldown == 0 && enraged) { //Jump towards player and create earthquake closestField = AIUtility.FindClosestField(graph, goalPos, jumpDistance, map, (List <(Point point, double cost, double height)> list) => { list.Sort((o1, o2) => { double diffCost = o1.cost - o2.cost; double heightDiff = o1.height - o2.height; if (Math.Abs(diffCost) >= 1) //assume that using the weapon costs 1 action point { return(diffCost < 0 ? -1 : 1); } else if (heightDiff != 0) { return(diffCost < 0 ? -1 : 1); } return(0); }); return(list.First().point); });
public static void GeneralFighterAI(MainGameWindow main, bool SingleTurn, Player ai, Map map) { //TODO: Seperate code, which logs details from code which decides what to do Troop troop = ai.troop; var enemies = ai.enemies; ActionPoint actionPoints = ai.actionPoints; MovementPoints movementPoints = ai.movementPoints; DistanceGraphCreator distanceGraph = new DistanceGraphCreator(ai, troop.Position.X, troop.Position.Y, map, true); Thread path = new Thread(distanceGraph.CreateGraph); path.Start(); path.Join(); int damageDealt = 0; int dodged = 0; while (actionPoints.Value > 0) { Point playerPos = enemies[0].troop.Position; //If the weapon is ranged and empty first load the weapon if (troop.activeWeapon.Attacks() == 0 && troop.activeWeapon is RangedWeapon w) { Ammo selectedAmmo = w.GetSelectedAmmo(); foreach (var ammo in w.Ammo) { if (selectedAmmo is null || selectedAmmo.damage.Value < ammo.damage.Value) { ammo.Select(w); selectedAmmo = ammo; } } } //Check if it can attack player int playerDistance = AIUtility.Distance(playerPos, troop.Position); int attacks = troop.activeWeapon.Attacks(); if (playerDistance <= troop.activeWeapon.range && attacks > 0) { //Attack var(damage, killed, hit) = main.Attack(ai, enemies[0]); damageDealt += damage; if (!hit) { dodged++; } if (killed) { map.overlayObjects.Add(new OverlayText(enemies[0].troop.Position.X * MapCreator.fieldSize, enemies[0].troop.Position.Y * MapCreator.fieldSize, Color.Red, $"-{damageDealt}")); main.PlayerDied($"You have been killed by {ai.Name}!"); break; } actionPoints.RawValue--; troop.activeWeapon.UseWeapon(enemies[0], main); continue; } else if (troop.weapons.Exists(t => t.range >= playerDistance && t.Attacks() > 0)) { //Change weapon Weapon best = troop.weapons.FindAll(t => t.range >= playerDistance) .Aggregate((t1, t2) => t1.range > t2.range ? t1 : t2); troop.activeWeapon = best; continue; } //Generate map of left value double[,] movementCosts = new double[map.width, map.height]; for (int x = 0; x <= movementCosts.GetUpperBound(0); x++) { for (int y = 0; y <= movementCosts.GetUpperBound(1); y++) { movementCosts[x, y] = -1; } } //Find closest field to player Point closestField = new Point(-1, -1); try { closestField = AIUtility.FindClosestField(distanceGraph, playerPos, movementPoints.Value, map, (List <(Point point, double cost, double height)> list) => { list.Sort((o1, o2) => { double diffCost = o1.cost - o2.cost; double heightDiff = o1.height - o2.height; if (Math.Abs(diffCost) >= 1) //assume that using the weapon costs 1 action point { return(diffCost < 0 ? -1 : 1); } else if (heightDiff != 0) { return(diffCost < 0 ? -1 : 1); } return(0); }); return(list.First().point); }); }