/// <summary> /// Finds the shortest path from the given start to the given goal. /// Does not include the "start" pos in the list. /// Returns "null" if a path wasn't found. /// IMPORTANT: The returned list is reused for other calls to this method, /// so treat it as a temp variable! /// </summary> /// <param name="heuristicCalc"> /// The heuristic/path length calculator, /// or "null" if the default one (manhattan distance) should be used. /// </param> public List <Vector2i> FindPath(Vector2i start, Pathfinding.Goal <Vector2i> goal, Pathfinding.PathFinder <Vector2i> .CostCalculator heuristicCalc = null) { return(FindPath(start, goal, tempPath, heuristicCalc) ? tempPath : null); }
public override IEnumerable TakeTurn() { //If the tile no longer supports building a bed, stop the job. if (!TheMap.Value.Tiles[Tile].IsBuildableOn() || TheMap.Value.AnyUnitsAt(Tile, u => u.BlocksStructures)) { EndJob(false, Localization.Get("CANT_BUILD_ON_TILE")); yield break; } //If we're not currently building, path to the tile to build from. if (!IsCurrentlyBuilding) { var movementStatus = new TryMoveToPos_Status(); var goal = new Pathfinding.Goal <Vector2i>(Tile); foreach (object o in TryMoveToPos(goal, movementStatus)) { yield return(o); } switch (movementStatus.CurrentState) { case TryMoveToPos_States.Finished: //Start building. IsCurrentlyBuilding = true; TurnsLeft = Consts.TurnsToBuildStructure(Owner.Value.Strength, Owner.Value.AdultMultiplier); break; case TryMoveToPos_States.EnRoute: //End the turn. yield break; case TryMoveToPos_States.NoPath: //Cancel the job. EndJob(false, Localization.Get("NO_PATH_JOB")); yield break; } } //Build. if (TurnsLeft > 0) { TurnsLeft -= 1; yield break; } //Create the bed. var bed = new Bed(TheMap, OwnerGroupId, Tile); TheMap.Value.AddUnit(bed); EndJob(true); }
/// <summary> /// Gets the first applicable job from the given set. /// Returns null if none were applicable. /// </summary> public Job ChooseApplicable(HashSet <Job> jobs) { foreach (Job j in jobs) { switch (j.ThisType) { case Job.Types.MoveToPos: { var jMove = (Job_MoveToPos)j; //Edge-case: the PlayerChar is already there, and doesn't have to do anything. if (Owner.Pos.Value == jMove.TargetPos.Value) { return(j); } //This job is doable if the PlayerChar is not too far away // and can actually path to the object. int maxDist = MoveToPos_MaxDist; if (maxDist != 0) { var goal = new Pathfinding.Goal <Vector2i>(jMove.TargetPos); var path = Owner.FindPath(goal, AvoidEnemiesWhenPathing); if (path != null && path.Count < maxDist) { return(j); } } } break; case Job.Types.Mine: if (AcceptJob_Mining) { return(j); } break; case Job.Types.BuildBed: if (AcceptJob_Build) { return(j); } break; default: throw new NotImplementedException(j.ThisType.ToString()); } } return(null); }
/// <summary> /// Outputs the shortest path from the given start to the given goal /// into the "outPath" list. /// Does not include the "start" pos in the list. /// Returns whether a path was actually found. /// </summary> /// <param name="heuristicCalc"> /// The heuristic/path length calculator, /// or "null" if the default one (manhattan distance) should be used. /// </param> public bool FindPath(Vector2i start, Pathfinding.Goal <Vector2i> goal, List <Vector2i> outPath, Pathfinding.PathFinder <Vector2i> .CostCalculator heuristicCalc = null) { if (heuristicCalc == null) { pathing.CalcCosts = Graph.AStarEdgeCalc; } else { pathing.CalcCosts = heuristicCalc; } return(pathing.FindPath(start, goal, float.PositiveInfinity, false, outPath)); }
/// <summary> /// The heuristic function for doing A* pathfinding for this unit. /// Note that to reduce garbage, this heuristic may reuse global variables, /// which means only one Unit can have a heuristic at any time. /// </summary> protected void AStarEdgeCalc(Pathfinding.Goal <Vector2i> goal, Pathfinding.Edge <Vector2i> edge, out float edgeLength, out float heuristic) { Graph.AStarEdgeCalc(goal, edge, out edgeLength, out heuristic); //Subtract enemy distances squared from the heuristic. foreach (Unit enemy in _temp_enemies) { float distSqr = enemy.Pos.Value.DistanceSqr(Pos); float distT = distSqr * One_Over_MaxEnemyDistance; distT = Math.Max(0.0f, Math.Min(1.0f, distT)); heuristic += Units.Consts.EnemyDistanceHeuristicMax * distT; } }
/// <summary> /// A coroutine that attempts to make the owning PlayerChar move to the given position. /// </summary> /// <param name="nMoves"> /// The maximum number of moves to take, /// or -1 to take as many as he is allowed in one turn. /// </param> /// <param name="nActualMoves"> /// The number of actual moves the PlayerChar took by the end of this. /// He will stop early if he reaches the goal. /// </param> /// <param name="outStatus"> /// As this method runs, it outputs information into this structure. /// </param> protected System.Collections.IEnumerable TryMoveToPos(Pathfinding.Goal <Vector2i> goal, TryMoveToPos_Status outStatus, int nMoves = -1) { outStatus.NActualMoves = 0; outStatus.CurrentState = TryMoveToPos_States.EnRoute; //Try to find the best path. List <Vector2i> path = Owner.Value.FindPath(goal, Owner.Value.Career.AvoidEnemiesWhenPathing); if (path == null) { outStatus.CurrentState = TryMoveToPos_States.NoPath; yield break; } //Move along the path. if (nMoves == -1) { nMoves = PlayerConsts.MovesPerTurn; } nMoves = Math.Min(nMoves, path.Count); for (int i = 0; i < nMoves; ++i) { Owner.Value.Pos.Value = path[i]; outStatus.NActualMoves += 1; //If we're at the end of the path, exit. if (goal.IsValidEnd(Owner.Value.Pos)) { outStatus.CurrentState = TryMoveToPos_States.Finished; yield break; } yield return(null); } //We didn't make it to the end of the path. outStatus.CurrentState = TryMoveToPos_States.EnRoute; }
/// <summary> /// Does the basic edge length/heuristic calculation /// that all units will likely use for their pathing. /// </summary> public static void AStarEdgeCalc(Pathfinding.Goal <Vector2i> goal, Pathfinding.Edge <Vector2i> edge, out float edgeLength, out float heuristic) { //For performance (and because they'll generally be adjacent), // use manhattan distance between nodes. edgeLength = Math.Abs(edge.Start.x - edge.End.x) + Math.Abs(edge.Start.y - edge.End.y); heuristic = 0.0f; //Manhattan distance to the goal. if (goal.SpecificGoal.HasValue) { Vector2i specificGoal = goal.SpecificGoal.Value; float dist = Math.Abs(edge.End.x - specificGoal.x) + Math.Abs(edge.End.y - specificGoal.y); //Square it to make it more important. heuristic += dist * dist; } }
public override IEnumerable TakeTurn() { //If the unit's energy and health is high enough, end the job. if (Owner.Value.Health.Value >= Consts.Max_Health && Owner.Value.Energy.Value >= Consts.MaxEnergy(Owner.Value.Strength)) { EndJob(true); } //Otherwise, if we're not sleeping yet, path to a bed. else if (!BedID.HasValue) { //Get all friendly beds. friendlyBedPoses.Clear(); foreach (Bed b in TheMap.Value.GetUnits(Unit.Types.Bed).Cast <Bed>()) { if (b.MyGroup == Owner.Value.MyGroup || b.MyGroup.IsAllyTo(Owner.Value.MyGroup)) { friendlyBedPoses.Add(b.Pos); } } //If there are no friendly beds, cancel the job. if (friendlyBedPoses.Count == 0) { EndJob(false, Localization.Get("NO_BED")); yield break; } //Either move towards a specific bed, or towards any friendly bed. var goal = new Pathfinding.Goal <Vector2i>(); if (TargetBedID.Value.HasValue) { goal.SpecificGoal = TheMap.Value.GetUnit(TargetBedID.Value.Value).Pos.Value; } else { goal.GeneralGoal = friendlyBedPoses.Contains; } //Move to the goal. TryMoveToPos_Status moveStatus = new TryMoveToPos_Status(); foreach (object o in TryMoveToPos(goal, moveStatus)) { yield return(o); } switch (moveStatus.CurrentState) { case TryMoveToPos_States.Finished: //Start sleeping. StartSleeping((Bed)TheMap.Value.FirstUnitAt(Owner.Value.Pos, u => u is Bed)); break; case TryMoveToPos_States.EnRoute: //End the turn. yield break; case TryMoveToPos_States.NoPath: //Cancel the job. EndJob(false, Localization.Get("NO_PATH_JOB")); break; default: throw new NotImplementedException(moveStatus.CurrentState.ToString()); } } }
/// <summary> /// Finds the shortest path from this Unit to the given goal. /// Does not include this Unit's own position in the list. /// Returns "null" if a path wasn't found. /// IMPORTANT: The returned list is reused for other calls to this method, /// so treat it as a temp variable! /// </summary> public List <Vector2i> FindPath(Pathfinding.Goal <Vector2i> goal, bool avoidEnemies) { return(TheMap.FindPath(Pos, goal, MakeHeuristic(avoidEnemies))); }