public static int pathCounter = int.MinValue + 1; // Incremented after every check #endregion Fields #region Methods // Yay C# has generators! /// <summary> /// A generator that produces positions by doing a breadth first iteration of tiles starting at a given position. /// </summary> /// <param name="engine">An engine object to get tiles from</param> /// <param name="position">The starting position.</param> /// <param name="distance">The maximum distance from the starting position. Or negative for no limit.</param> /// <param name="size">The maximum number of tiles traversed. Or negative for no limit.</param> public static IEnumerable<Position> BreadthFirst(Engine engine, Position position, int distance = -1, int size = -1) { // Reset ONE tile's pathIndex on the map to the minimum value to prevent integer rollover int index = pathCounter < 0 ? -pathCounter : pathCounter; engine.map.tiles[index % engine.map.width, (index / engine.map.width)%engine.map.height].pathIndex = int.MinValue; int localPathCounter = pathCounter++; LinkedList<Tile> perimeter = new LinkedList<Tile>(); perimeter.AddLast(engine.map.GetTileNearestTo(position)); perimeter.Last.Value.pathDistance = 0; while (size != 0 && perimeter.Count > 0) { Tile top = perimeter.First.Value; top.pathIndex = localPathCounter; perimeter.RemoveFirst(); if (distance >= 0 && top.pathDistance > distance) { yield break; } yield return top.position; foreach (var neighbor in top.neighbors) { if (neighbor.pathIndex < localPathCounter) { perimeter.AddLast(neighbor); neighbor.pathIndex = localPathCounter; neighbor.pathDistance = top.pathDistance + 1; } } size -= 1; } // This is not the place for cleanup code }
public readonly UnitType unitTypeBuild; // Nullable #endregion Fields #region Constructors public Order(OrderType orderType, Position targetPosition=null, Unit targetUnit=null, UnitType unitTypeBuild=null) { this.orderType = orderType; this.targetPosition = targetPosition; this.targetUnit = targetUnit; this.unitTypeBuild = unitTypeBuild; }
public Tile AddTile(TileType tileType, Position position) { if (!map.Inside(position)) return null; var newTile = new Tile(GetNextId(), position, tileType); map.SetTileAt(position, newTile); return newTile; }
public bool Equals(Position obj) { // If parameter is null return false. if (obj == null) { return false; } return x == obj.x && y == obj.y; }
public Unit AddUnit(UnitType unitType, Position position, Player owner) { if (!map.Inside(position)) return null; var newUnit = new Unit(GetNextId(), this, unitType, position, owner); units.Add(newUnit); unitQuadtrees[owner].AddUnit(newUnit); CacheSetUnitAt(newUnit); return newUnit; }
public Unit NearestUnitTo(Unit startUnit, int maxDistance, Position startPosition=null) { if (startPosition == null) { startPosition = startUnit.position; } if (!Inside(startPosition)) { var nearestPosition = new Position(Math.Max(0, Math.Min(startPosition.x, width - 1)), Math.Max(0, Math.Min(startPosition.y, height - 1))); maxDistance -= nearestPosition.Distance(startPosition); startPosition = nearestPosition; } Chunk chunk = GetChunk(startPosition); // Breadth first search HashSet<Chunk> haveBeenInFrontier = new HashSet<Chunk> {chunk}; LinkedList<Chunk> frontier = new LinkedList<Chunk>(haveBeenInFrontier); while (frontier.Count > 0) { var top = frontier.First(); frontier.RemoveFirst(); // Try to find nearest in this chunk Unit closestUnit = null; int nearestDistance = Int32.MaxValue; foreach (var unit in top.units) { int distance = startPosition.Distance(unit.position); if (distance <= nearestDistance && unit != startUnit) { nearestDistance = distance; closestUnit = unit; } } if (closestUnit != null && nearestDistance <= maxDistance) { return closestUnit; } // if a unit has not been found yet, try the neighbors foreach (var neighbor in top.neighbors) { if (!haveBeenInFrontier.Contains(neighbor) && neighbor.GetDistanceFrom(startPosition) <= maxDistance) { frontier.AddLast(neighbor); haveBeenInFrontier.Add(neighbor); } } } return null; // Didn't find a unit within maxDistance }
public Unit(int id, Engine engine, UnitType unitType, Position position, Player owner) { this.id = id; this.engine = engine; this.type = unitType; this.position = position; this.previousPosition = position; this.nextMove = -1; this.owner = owner; health = type.maxHealth; status = Status.Idle; orders = new List<Order>(); modifiers = new List<UnitModifier>(); animationStartTick = 0; direction = 0; currentPath = null; currentTargetPosition = null; }
public bool CanMove(Position targetPosition) { if (CanMove() && engine.map.Inside(targetPosition)) { var targetUnit = engine.GetUnitAt(targetPosition); if (targetUnit != null) { return targetUnit.owner == owner && targetUnit.CanMove() && targetUnit.orders.Count == 0; } return true; } return false; }
// Private methods private bool Inside(Position position) { return position.x >= 0 && position.y >= 0 && position.x < width && position.y < height; }
private bool Inside(Position position) { return position.x >= topLeft.x && position.y >= topLeft.y && position.x <= bottomRight.x && position.y <= bottomRight.y; }
public int Distance(Position other) { return Math.Abs(other.y - y) + Math.Abs(other.x - x); }
private void MoveTowards(Position targetPosition) { if (currentPath == null || currentPath.Count == 0 || !targetPosition.Equals(currentTargetPosition) || !CanMove(currentPath.First()) || (position.Distance(targetPosition) % 11 == 0)) // Every 11 steps make sure that a new best path hasn't magically appeared { // Try to create a path if the current one is invalid currentPath = Pathfinder.FindPath(engine, this, engine.map.GetTileAt(position), engine.map.GetTileAt(targetPosition)); currentTargetPosition = targetPosition; // Remove the first element because we are already there if (currentPath != null) currentPath.RemoveAt(0); } if (currentPath != null && currentPath.Count > 0 && currentTargetPosition.Equals(targetPosition) && CanMove(currentPath.First())) { Position nextStep = currentPath.First(); currentPath.RemoveAt(0); TryToMove(nextStep); } else { status = Status.Idle; // Retry after moveRetryCooldown ticks engine.ScheduleUpdate(moveRetryCooldown, this); } }
// Kinda private methods public void MoveUnit(Unit unit, Position targetPosition) { Debug.Assert(targetPosition.Distance(unit.position) == 1); Debug.Assert(unitGrid[targetPosition.x, targetPosition.y] == null); CacheRemoveUnitAt(unit.position); unit.previousPosition = unit.position; unit.position = targetPosition; unit.animationStartTick = currentTick; CacheSetUnitAt(unit); }
public Unit GetUnitAt(Position position) { return map.Inside(position) ? unitGrid[position.x, position.y] : null; }
public static Position FindNextStep( Engine engine, Unit unit, Position start, Position end) { return FindNextStep(engine, unit, engine.map.GetTileAt(start), engine.map.GetTileAt(end)); }
public Chunk(Position position, int width, int height) { topLeft = position; bottomRight = topLeft + new Position(width - 1, height - 1); units = new HashSet<Unit>(); neighbors = new List<Chunk>(); }
public void UnitGroupCommand() { MouseState mouse = Mouse.GetState(); KeyboardState keys = Keyboard.GetState(); Vector2 mouseTile = new Vector2(Mouse.GetState().X, Mouse.GetState().Y); mouseTile.X = m.X / (map.GetPxSizeMod()) + map.GetTileIndexX(); mouseTile.Y = m.Y / (map.GetPxSizeMod()) + map.GetTileIndexY(); Position mouseGameTilePosition = new Position((int)mouseTile.X, (int)mouseTile.Y); Unit clickedUnit = engine.GetUnitAt(mouseGameTilePosition); var enumerator = Pathfinder.BreadthFirst(engine, mouseGameTilePosition).GetEnumerator(); enumerator.MoveNext(); foreach (Unit unit in selectedUnits) { if (lastButtonPressed.Equals("None")) { // Hold shift to append orders if (keys.IsKeyUp(Keys.LeftShift) && keys.IsKeyUp(Keys.RightShift)) { unit.orders.Clear(); } // Click on same unit to produce units if (clickedUnit == unit) { if (unit.CanProduce()) { engine.OrderProduce(unit, engine.unitTypes[1]); } break; } // Click on other units to attack them else if (clickedUnit != null) // TODO: make it so you don't attack your buddies { if (selectedUnits.Contains(clickedUnit)) //this check makes it so that you can produce without have the other selected units attack { continue; } if (unit.CanAttack()) { engine.OrderAttack(unit, clickedUnit); } } else if (unit.CanMove()) // Move units or gather { // Click on a resource node to gather var resource = engine.map.tiles[enumerator.Current.x, enumerator.Current.y].tileType.resourceType; if (resource != TileType.ResourceType.None) { engine.OrderGather(unit, enumerator.Current); enumerator.MoveNext(); continue; } // Click on an empty tile to move while (engine.GetUnitAt(enumerator.Current) != null) { enumerator.MoveNext(); } engine.OrderMove(unit, enumerator.Current); enumerator.MoveNext(); } } else { unit.orders.Clear(); switch (lastButtonPressed) { case "Build Town": foreach (Unit u in selectedUnits) { if (u.CanBuild()) { engine.OrderProduce(u, engine.unitTypes[3], mouseGameTilePosition); } } break; case "Build Mine": foreach (Unit u in selectedUnits) { if (u.CanBuild()) { engine.OrderProduce(u, engine.unitTypes[4], mouseGameTilePosition); } } break; case "Produce Knight": foreach (Unit u in selectedUnits) { if (u.CanProduce()) { engine.OrderProduce(u, engine.unitTypes[0]); } } break; case "Produce Archer": foreach (Unit u in selectedUnits) { if (u.CanProduce()) { engine.OrderProduce(u, engine.unitTypes[1]); } } break; case "Produce Peasant": foreach (Unit u in selectedUnits) { if (u.CanProduce()) { engine.OrderProduce(u, engine.unitTypes[2]); } } break; case "Attack": foreach (Unit u in selectedUnits) { if (u.CanProduce()) { engine.OrderProduce(u, engine.unitTypes[2]); } } break; case "Move": foreach (Unit u in selectedUnits) { if (u.CanProduce()) { engine.OrderProduce(u, engine.unitTypes[2]); } } break; case "Gather": foreach (Unit u in selectedUnits) { if (u.CanProduce()) { engine.OrderProduce(u, engine.unitTypes[2]); } } break; case "Stop": foreach (Unit u in selectedUnits) { if (u.CanProduce()) { engine.OrderProduce(u, engine.unitTypes[2]); } } ClearOrders(); break; default: break; } //End switch lastButtonPressed = "None"; } } if (keys.IsKeyUp(Keys.LeftControl) && keys.IsKeyUp(Keys.RightControl)) { lastButtonPressed = "None"; } }
public void AddUnit(Unit unit) { var chunk = GetChunk(unit.position); if (chunk != null) chunk.AddUnit(unit); previousUnitPositions[unit] = new Position(unit.position); }
public int GetMoveCooldown(Position startPosition, Position endPosition) { // TODO: Account for tile effects, unit modifiers, etc. int nominalSpeed = (int)(engine.map.GetTileAt(endPosition).tileType.movementCost * 10.0 / type.movementSpeed); return nominalSpeed > 0 ? nominalSpeed : 1; }
public void OrderGather(Unit gatherer, Position target) { gatherer.orders.Add(Order.CreateGatherOrder(target)); ScheduleUpdate(1, gatherer); }
// Private private int CalculateDirection(Position delta) { bool bottomRight = delta.x > -delta.y; bool topRight = delta.x > delta.y; if (bottomRight && topRight) return 0; if (bottomRight) return 1; if (!topRight) return 2; return 3; // return 1 - delta.x + (delta.y == -1 ? 2 : 0); }
public double EuclideanDistance(Position other) { return Math.Sqrt((other.y - y) * (other.y - y) + (other.x - x) * (other.x - x)); }
private void TryToMove(Position targetPosition) { if (targetPosition != null) { status = Status.Moving; direction = CalculateDirection(targetPosition - position); engine.ScheduleUpdate(GetMoveCooldown(position, targetPosition), this); var blockingUnit = engine.GetUnitAt(targetPosition); if (blockingUnit != null) { engine.SwapUnits(this, blockingUnit); } else { engine.MoveUnit(this, targetPosition); } } else { status = Status.Idle; // Retry after moveRetryCooldown ticks engine.ScheduleUpdate(moveRetryCooldown, this); } }
private Chunk GetChunk(Position position) { if (Inside(position)) { return chunks[position.x/chunkWidth, position.y/chunkHeight]; } return null; }
public Position( Position p ) { x = p.x; y = p.y; }
private void CacheRemoveUnitAt(Position position) { if (map.Inside(position)) { unitGrid[position.x, position.y] = null; } }
public void OrderMove(Unit unit, Position targetPosition) { unit.orders.Add(Order.CreateMoveOrder(targetPosition)); ScheduleUpdate(1, unit); }
public int GetDistanceFrom(Position position) { // Calculate point nearest to startPosition inside this chunk Position nearestPosition = new Position(Math.Max(topLeft.x, Math.Min(bottomRight.x, position.x)), Math.Max(topLeft.y, Math.Min(bottomRight.y, position.y))); // Return distance return nearestPosition.Distance(position); }
public int EuclideanDistanceSquared(Position other) { return (other.y - y)*(other.y - y) + (other.x - x)*(other.x - x); }
public void OrderProduce(Unit factory, UnitType unitType, Position targetPosition = null) { factory.orders.Add(Order.CreateProduceOrder(unitType, targetPosition)); ScheduleUpdate(1, factory); }