private bool Build(EnemyShip design) { // find sector to place ship in var places = new List <dynamic>(); var sys = StarSystem; var x = X; var y = Y; for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { if (sys.SpaceObjects.AreCoordsInBounds(x + dx, y + dy) && sys.SpaceObjects[x + dx, y + dy] == null) { places.Add(new { X = x + dx, Y = y + dy }); } } } if (!places.Any()) { return(false); // nowhere to put the ship } var coords = places.PickRandom(); // create the ship and place it var ship = design.Clone(); if (PlayerShip.Instance.StarSystem == StarSystem) { Log.Add("An enemy shipyard builds a " + ship.Name + "!", Color.Red); } Savings -= ship.Cost; StarSystem.PlaceSpaceObject(ship, coords.X, coords.Y, 0); Galaxy.Current.RefreshEnemyCounts(); return(true); }
/// <summary> /// Dijkstra-style pathfinding algorithm. /// based on http://www.roguebasin.com/index.php?title=Pathfinding /// </summary> /// <param name="sys">The star system to navigate.</param> /// <param name="x">Starting X coordinate.</param> /// <param name="y">Starting Y coordinate.</param> /// <param name="priority">Priority function. Takes x and y coordinates; returns priority value (zero is optimal, higher numbers are worse).</param> /// <returns>Direction to travel.</returns> public static Direction Pathfind(StarSystem sys, int x, int y, Func <int, int, int> priority) { // create "check-it" queue and add start node to it var pQueue = new List <PathfindingNode>(); pQueue.Add(new PathfindingNode(x, y, null, 0)); // create list of previously visitied nodes var visited = new List <PathfindingNode>(); // search for paths while (pQueue.Any()) { // take out the lowest cost node and work on it var lowestCost = pQueue.Min(n => n.Cost); var node = pQueue.Where(n => n.Cost == lowestCost).First(); pQueue.Remove(node); // did we reach the goal? if (priority(node.X, node.Y) <= 0) { var first = node.SecondAncestor; return(Direction.Get(first.X - x, first.Y - y)); } // find successor nodes foreach (var dir in Direction.All) { var next = new PathfindingNode(node.X + dir.DeltaX, node.Y + dir.DeltaY, node, node.Cost + 1); if (sys.SpaceObjects.AreCoordsInBounds(next.X, next.Y) && (sys.SpaceObjects[next.X, next.Y] == null || sys.SpaceObjects[next.X, next.Y] is WarpPoint)) { var sameNodes = visited.Where(n => n.X == next.X && n.Y == next.Y).ToArray(); var queuedNodes = pQueue.Where(n => n.X == next.X && n.Y == next.Y).ToArray(); if (!sameNodes.Any() || queuedNodes.Any(n => n.Cost > next.Cost) || sameNodes.Any(n => n.Cost > next.Cost)) { // if node already exists, get rid of old node with higher cost foreach (var n in sameNodes) { visited.Remove(n); } foreach (var n in queuedNodes) { pQueue.Remove(n); } // add to visited list and priority queue pQueue.Add(next); visited.Add(next); } } } } // get as close as possible var closest = visited.OrderBy(n => priority(n.X, n.Y)).FirstOrDefault(); if (closest == null) { return(Direction.None); // no path leads closer than where we are now } // find first node in path var resultFirst = closest.SecondAncestor; return(Direction.Get(resultFirst.X - x, resultFirst.Y - y)); }
/// <summary> /// Creates a galaxy with the specified settings (radius 0 = 1x1, radius 1 = 3x3, etc.) /// </summary> /// <param name="radius"></param> public Galaxy(int radius, int numSystems, int systemRadius, int planetsPerSystem, int colonyChance, int mineralsChance, int maxMinerals, int enemyShipyards, int enemyShipyardBuildRate, int enemyShipyardScrapRate, int enemyShipScrapRate) { Current = this; ColonyChance = colonyChance; MineralsChance = mineralsChance; MaxMinerals = maxMinerals; EnemyShipyardScrapRate = enemyShipyardScrapRate; EnemyShipScrapRate = enemyShipScrapRate; StarSystems = new Grid <StarSystem>(radius); // place star systems on grid { for (int i = 0; i < numSystems && i < (radius + 1) * (radius + 1); i++) { if (i == 0) { // place randomly var x = Dice.Range(-StarSystems.Radius, StarSystems.Radius); var y = Dice.Range(-StarSystems.Radius, StarSystems.Radius); StarSystems[x, y] = new StarSystem(systemRadius, planetsPerSystem); } else { // place adjacent to some other system in an empty space var places = new List <dynamic>(); for (var x = -StarSystems.Radius; x <= StarSystems.Radius; x++) { for (var y = -StarSystems.Radius; y <= StarSystems.Radius; y++) { if (!IsFilled(x, y) && HasFilledNeighbor(x, y)) { places.Add(new { X = x, Y = y }); } } } var coords = places.PickRandom(); StarSystems[coords.X, coords.Y] = new StarSystem(systemRadius, planetsPerSystem); } } } // place player ship { var sys = StarSystems.Where(s => s != null).PickRandom(); var places = new List <dynamic>(); for (var x = -sys.SpaceObjects.Radius; x <= sys.SpaceObjects.Radius; x++) { for (var y = -sys.SpaceObjects.Radius; y <= sys.SpaceObjects.Radius; y++) { if (sys.SpaceObjects[x, y] == null) { places.Add(new { X = x, Y = y }); } } } var coords = places.PickRandom(); sys.PlaceSpaceObject(PlayerShip.Instance, coords.X, coords.Y, 0); // perform initial sensor sweep for (var x = coords.X - PlayerShip.Instance.SensorRange; x <= coords.X + PlayerShip.Instance.SensorRange; x++) { for (var y = coords.Y - PlayerShip.Instance.SensorRange; y <= coords.Y + PlayerShip.Instance.SensorRange; y++) { if (sys.SensorGrid.AreCoordsInBounds(x, y)) { sys.SensorGrid[x, y] = true; } } } } // place enemy shipyards { for (int i = 0; i < enemyShipyards; i++) { var sys = StarSystems.Where(s => s != null).PickRandom(); var places = new List <dynamic>(); for (var x = -sys.SpaceObjects.Radius; x <= sys.SpaceObjects.Radius; x++) { for (var y = -sys.SpaceObjects.Radius; y <= sys.SpaceObjects.Radius; y++) { if (sys.SpaceObjects[x, y] == null) { places.Add(new { X = x, Y = y }); } } } var coords = places.PickRandom(); sys.PlaceSpaceObject(new EnemyShipyard(enemyShipyardBuildRate), coords.X, coords.Y, 0); } } // delete warp points that lead nowhere for (int x = -StarSystems.Radius; x <= StarSystems.Radius; x++) { for (int y = -StarSystems.Radius; y <= StarSystems.Radius; y++) { var wpsys = StarSystems[x, y]; if (wpsys != null) { for (int sx = -wpsys.SpaceObjects.Radius; sx <= wpsys.SpaceObjects.Radius; sx++) { for (int sy = -wpsys.SpaceObjects.Radius; sy <= wpsys.SpaceObjects.Radius; sy++) { if (wpsys.SpaceObjects[sx, sy] is WarpPoint) { var wp = wpsys.SpaceObjects[sx, sy] as WarpPoint; if (wp.TargetSystem == null) { wpsys.SpaceObjects[sx, sy] = null; } } } } } } } RefreshEnemyCounts(); }
public override bool Move() { if (PlayerShip.Instance.StarSystem == StarSystem) { // player ship sighted! NeedsNewWaypoint = false; int desiredRange; var ourRanges = Components.Select(c => c.WeaponInfo).Where(w => w != null).Select(w => w.Range).Distinct(); var theirRanges = PlayerShip.Instance.Components.Select(c => c.WeaponInfo).Where(w => w != null).Select(w => w.Range).Distinct(); if (!ourRanges.Any()) { // RUN AWAY!!! // get new waypoint - random warp point to "patrol" desiredRange = int.MaxValue; NeedsNewWaypoint = false; var wp = StarSystem.FindSpaceObjects <WarpPoint>().PickRandom(); WaypointX = wp.X; WaypointY = wp.Y; } else if (ourRanges.Any(r => r > theirRanges.Max())) { // stay at max range of closest ranged weapon that is outside their max range desiredRange = ourRanges.Where(r => r > theirRanges.Max()).Min(); } else { // stay at max range of closest ranged weapon desiredRange = ourRanges.Min(); } if (desiredRange < int.MaxValue) { // stay at desired range var dir = Utilities.Pathfind(StarSystem, X, Y, (nx, ny) => { var dist = Math.Abs(Utilities.Distance(nx, ny, PlayerShip.Instance.X, PlayerShip.Instance.Y)); var offset = dist - desiredRange; // being too close is better than being too far away if (offset > 0) { return(offset * 2); } else { return(-offset); } }); Go(dir); return(true); } } else if (NeedsNewWaypoint) { // get new waypoint - random warp point to "patrol" NeedsNewWaypoint = false; var wp = StarSystem.FindSpaceObjects <WarpPoint>().PickRandom(); WaypointX = wp.X; WaypointY = wp.Y; } else { // continue toward old waypoint, nothing to do here } // pursue warp point var wpDir = Utilities.Pathfind(StarSystem, X, Y, (nx, ny) => Utilities.Distance(nx, ny, WaypointX, WaypointY)); Go(wpDir); // no need to wait for player input return(true); }