public static Room FindNearestRoom(Tile start) { Path_AStar tileResolver = new Path_AStar(World.Current, start, GoalHasRoomEvaluator(), DijkstraDistance()); if (tileResolver.Length() >= 1) { return(tileResolver.EndTile().Room); } return(null); }
public void PrioritizeJob(Job job) { AbandonJob(false); World.Current.jobQueue.Remove(job); job.IsBeingWorked = true; /*Check if the character is carrying any materials and if they could be used for the new job, * if the character is carrying materials but is not used in the new job, then drop them * on the current tile for now.*/ if (inventory != null && !job.inventoryRequirements.ContainsKey(inventory.ObjectType)) { World.Current.inventoryManager.PlaceInventory(CurrTile, inventory); DumpExcessInventory(); } MyJob = job; // Get our destination from the job. DestTile = MyJob.tile; // If the destination tile does not have neighbours that are walkable it's very likable that they can't be walked to. if (DestTile.HasWalkableNeighbours() == false) { Debug.ULogChannel("Character", "No neighbouring floor tiles! Abandoning job."); AbandonJob(false); return; } MyJob.OnJobStopped += OnJobStopped; pathAStar = new Path_AStar(World.Current, CurrTile, DestTile); if (pathAStar != null && pathAStar.Length() == 0) { Debug.ULogChannel("Character", "Path_AStar returned no path to target job tile!"); AbandonJob(false); return; } if (MyJob.adjacent) { IEnumerable <Tile> reversed = pathAStar.Reverse(); reversed = reversed.Skip(1); pathAStar = new Path_AStar(new Queue <Tile>(reversed.Reverse())); DestTile = pathAStar.EndTile(); jobTile = DestTile; } else { jobTile = MyJob.tile; } }
/// <summary> /// Checks whether the current job has all the materials in place and if not instructs the working character to get the materials there first. /// Only ever returns true if all materials for the job are at the job location and thus signals to the calling code, that it can proceed with job execution. /// </summary> /// <returns></returns> private bool CheckForJobMaterials() { List <string> fulfillableInventoryRequirements = new List <string>(); if (MyJob != null && MyJob.IsNeed && MyJob.Critical == false) { MyJob.tile = jobTile = new Path_AStar(World.Current, CurrTile, null, MyJob.JobObjectType, 0, false, true).EndTile(); } if (MyJob == null || MyJob.MaterialNeedsMet()) { // We can return early. return(true); } else { fulfillableInventoryRequirements = MyJob.FulfillableInventoryRequirements(); // If we somehow get here and fulfillableInventoryRequirements is empty then there is a problem! if (fulfillableInventoryRequirements == null || fulfillableInventoryRequirements.Count() == 0) { Debug.ULogChannel("Character", "CheckForJobMaterials: no fulfillable inventory requirements"); AbandonJob(true); return(false); } } // At this point we know that the job still needs materials and these needs are satisfiable. // First we check if we carry any materials the job wants by chance. if (inventory != null) { if (MyJob.AmountDesiredOfInventoryType(inventory) > 0) { // If so, deliver the goods. // Walk to the job tile, then drop off the stack into the job. if (CurrTile == JobTile) { // We are at the job's site, so drop the inventory World.Current.inventoryManager.PlaceInventory(MyJob, inventory); MyJob.DoWork(0); // This will call all cbJobWorked callbacks, because even though // we aren't progressing, it might want to do something with the fact // that the requirements are being met. // at this point we should dump anything in our inventory DumpExcessInventory(); } else { // We still need to walk to the job site. DestTile = JobTile; return(false); } } else { // We are carrying something, but the job doesn't want it! // Dump the inventory so we can be ready to carry what the job actually wants. DumpExcessInventory(); } } else { // At this point, the job still requires inventory, but we aren't carrying it! // Are we standing on a tile with goods that are desired by the job? if (CurrTile.Inventory != null && MyJob.AmountDesiredOfInventoryType(CurrTile.Inventory) > 0 && !CurrTile.Inventory.Locked && (MyJob.canTakeFromStockpile || CurrTile.Furniture == null || CurrTile.Furniture.IsStockpile() == false)) { // Pick up the stuff! World.Current.inventoryManager.PlaceInventory( this, CurrTile.Inventory, MyJob.AmountDesiredOfInventoryType(CurrTile.Inventory)); } else { // Walk towards a tile containing the required goods. if (CurrTile != nextTile) { // We are still moving somewhere, so just bail out. return(false); } // Any chance we already have a path that leads to the items we want? // Check that we have an end tile and that it has content. // Check if contains the desired objectType�. if (WalkingToUsableInventory() && fulfillableInventoryRequirements.Contains(pathAStar.EndTile().Inventory.ObjectType)) { // We are already moving towards a tile that contains what we want! // so....do nothing? return(false); } else { Inventory desired = null; Path_AStar newPath = null; foreach (string itemType in fulfillableInventoryRequirements) { desired = MyJob.inventoryRequirements[itemType]; newPath = World.Current.inventoryManager.GetPathToClosestInventoryOfType( desired.ObjectType, CurrTile, desired.MaxStackSize - desired.StackSize, MyJob.canTakeFromStockpile); if (newPath == null || newPath.Length() < 1) { // Try the next requirement Debug.ULogChannel("Character", "No tile contains objects of type '" + desired.ObjectType + "' to satisfy job requirements."); continue; } // else, there is a valid path to an item that will satisfy the job break; } if (newPath == null || newPath.Length() < 1) { // tried all requirements and found no path Debug.ULogChannel("Character", "No reachable tile contains objects able to satisfy job requirements."); AbandonJob(true); return(false); } Debug.ULogChannel("Character", "pathAStar returned with length of: " + newPath.Length()); DestTile = newPath.EndTile(); // Since we already have a path calculated, let's just save that. pathAStar = newPath; // Ignore first tile, because that's what we're already in. nextTile = newPath.Dequeue(); } // One way or the other, we are now on route to an object of the right type. return(false); } } return(false); // We can't continue until all materials are satisfied. }
private void GetNewJob() { float needPercent = 0; Need need = null; foreach (Need n in needs) { if (n.Amount > needPercent) { need = n; needPercent = n.Amount; } } if (needPercent > 50 && needPercent < 100 && need.RestoreNeedFurn != null) { if (World.Current.CountFurnitureType(need.RestoreNeedFurn.ObjectType) > 0) { MyJob = new Job(null, need.RestoreNeedFurn.ObjectType, need.CompleteJobNorm, need.RestoreNeedTime, null, Job.JobPriority.High, false, true, false); } } if (needPercent == 100 && need != null && need.CompleteOnFail) { MyJob = new Job(CurrTile, null, need.CompleteJobCrit, need.RestoreNeedTime * 10, null, Job.JobPriority.High, false, true, true); } // Get the first job on the queue. if (MyJob == null) { MyJob = World.Current.jobQueue.Dequeue(); // Check if we got a job from the queue. if (MyJob == null) { Debug.ULogChannel("Character", name + " did not find a job."); MyJob = new Job( CurrTile, "Waiting", null, UnityEngine.Random.Range(0.1f, 0.5f), null, Job.JobPriority.Low, false); MyJob.JobDescription = "job_waiting_desc"; } else { if (MyJob.tile == null) { Debug.ULogChannel("Character", name + " found a job."); } else { Debug.ULogChannel("Character", name + " found a job at x " + MyJob.tile.X + " y " + MyJob.tile.Y + "."); } } } // Get our destination from the job. DestTile = MyJob.tile; // If the destination tile does not have neighbours that are walkable it's very likely that they can't be walked to if (DestTile != null) { if (DestTile.HasWalkableNeighbours() == false) { Debug.ULogChannel("Character", "No neighbouring floor tiles! Abandoning job."); AbandonJob(false); return; } } MyJob.OnJobStopped += OnJobStopped; // Immediately check to see if the job tile is reachable. // NOTE: We might not be pathing to it right away (due to // requiring materials), but we still need to verify that the // final location can be reached. Profiler.BeginSample("PathGeneration"); if (MyJob.IsNeed) { // This will calculate a path from current tile to destination tile. pathAStar = new Path_AStar(World.Current, CurrTile, DestTile, need.RestoreNeedFurn.ObjectType, 0, false, true); } else { pathAStar = new Path_AStar(World.Current, CurrTile, DestTile); } Profiler.EndSample(); if (pathAStar != null && pathAStar.Length() == 0) { Debug.ULogChannel("Character", "Path_AStar returned no path to target job tile!"); AbandonJob(false); return; } if (MyJob.adjacent) { IEnumerable <Tile> reversed = pathAStar.Reverse(); reversed = reversed.Skip(1); pathAStar = new Path_AStar(new Queue <Tile>(reversed.Reverse())); DestTile = pathAStar.EndTile(); jobTile = DestTile; } else { jobTile = MyJob.tile; } MyJob.IsBeingWorked = true; }
/// <summary> /// Gets the type of the closest inventory of. /// </summary> /// <returns>The closest inventory of type.</returns> /// <param name="objectType">Object type.</param> /// <param name="t">Tile from, which distance is mesured.</param> /// <param name="desiredAmount">Desired amount. If no stack has enough, it instead returns the largest.</param> public Inventory GetClosestInventoryOfType(string objectType, Tile t, int desiredAmount, bool canTakeFromStockpile) { Path_AStar path = GetPathToClosestInventoryOfType(objectType, t, desiredAmount, canTakeFromStockpile); return(path.EndTile().Inventory); }
/// <summary> /// Checks weather the current job has all the materials in place and if not instructs the working character to get the materials there first. /// Only ever returns true if all materials for the job are at the job location and thus signals to the calling code, that it can proceed with job execution. /// </summary> /// <returns></returns> bool CheckForJobMaterials() { if (myJob.HasAllMaterial()) { return(true); //we can return early } // Job still needs materials // Am I carrying anything the job location wants? if (inventory != null) { if (myJob.DesiresInventoryType(inventory) > 0) { // If so, deliver // Walk to the job tile then drop off the stack into the job if (CurrTile == myJob.tile) { // We are at the job site so drop the inventory World.Current.inventoryManager.PlaceInventory(myJob, inventory); myJob.DoWork(0); // This will call all cbJobWorked callbacks //at this point we should dump anything in our inventory DumpExcessInventory(); } else { // Walk to job site DestTile = myJob.tile; return(false); } } else { // We are carrying something but the job doesn't want it // Dump it DumpExcessInventory(); } } else { // At this point, the job still requires inventory but we aren't carrying it // Are we standing on a tile with goods desired by job? if (CurrTile.inventory != null && myJob.DesiresInventoryType(CurrTile.inventory) > 0 && (myJob.canTakeFromStockpile || CurrTile.furniture == null || CurrTile.furniture.IsStockpile() == false)) { // Pick up the stuff World.Current.inventoryManager.PlaceInventory( this, CurrTile.inventory, myJob.DesiresInventoryType(CurrTile.inventory) ); } else { // If not, walk towards a tile containing the required goods // Find the first thing in the job that isn't satisfied Inventory desired = myJob.GetFirstDesiredInventory(); if (CurrTile != NextTile) { // We are still moving somewhere so just bail out return(false); } // Any chance we already have a path that leads to the items we want? if (pathAStar != null && pathAStar.EndTile() != null && pathAStar.EndTile().inventory != null && pathAStar.EndTile().inventory.objectType == desired.objectType) { // We are already moving towards a tile that we want so do nothing } else { Path_AStar newPath = World.Current.inventoryManager.GetPathToClosestInventoryOfType( desired.objectType, CurrTile, desired.maxStackSize - desired.stackSize, myJob.canTakeFromStockpile ); if (newPath == null || newPath.Length() == 0) { Debug.Log("No tile contains objects of type " + desired.objectType + "to satisfy job requirements"); AbandonJob(); return(false); } Debug.Log("pathAStar returned with length of: " + newPath.Length()); DestTile = newPath.EndTile(); // Since we already have a path calculated let's just save that pathAStar = newPath; // Ignore the first tile because that's the tile we're already in NextTile = newPath.Dequeue(); } // One way or the other, we are now on route to an object of the right type return(false); } } return(false); // Can't continue until all materials are satisifed }
/// <summary> /// Checks weather the current job has all the materials in place and if not instructs the working character to get the materials there first. /// Only ever returns true if all materials for the job are at the job location and thus signals to the calling code, that it can proceed with job execution. /// </summary> /// <returns></returns> bool CheckForJobMaterials() { if (myJob.HasAllMaterial()) { return(true); //we can return early } // At this point we know, that the job still needs materials. // First we check if we carry any materials the job wants by chance. if (inventory != null) { if (myJob.DesiresInventoryType(inventory) > 0) { // If so, deliver the goods. // Walk to the job tile, then drop off the stack into the job. if (CurrTile == myJob.tile) { // We are at the job's site, so drop the inventory World.current.inventoryManager.PlaceInventory(myJob, inventory); myJob.DoWork(0); // This will call all cbJobWorked callbacks, because even though // we aren't progressing, it might want to do something with the fact // that the requirements are being met. //at this point we should dump anything in our inventory DumpExcessInventory(); } else { // We still need to walk to the job site. DestTile = myJob.tile; return(false); } } else { // We are carrying something, but the job doesn't want it! // Dump the inventory so we can be ready to carry what the job actually wants. DumpExcessInventory(); } } else { // At this point, the job still requires inventory, but we aren't carrying it! // Are we standing on a tile with goods that are desired by the job? Debug.Log("Standing on Tile check"); if (CurrTile.inventory != null && myJob.DesiresInventoryType(CurrTile.inventory) > 0 && (myJob.canTakeFromStockpile || CurrTile.furniture == null || CurrTile.furniture.IsStockpile() == false)) { // Pick up the stuff! Debug.Log("Pick up the stuff"); World.current.inventoryManager.PlaceInventory( this, CurrTile.inventory, myJob.DesiresInventoryType(CurrTile.inventory) ); } else { // Walk towards a tile containing the required goods. Debug.Log("Walk to the stuff"); Debug.Log(myJob.canTakeFromStockpile); // Find the first thing in the Job that isn't satisfied. Inventory desired = myJob.GetFirstDesiredInventory(); if (CurrTile != NextTile) { // We are still moving somewhere, so just bail out. return(false); } // Any chance we already have a path that leads to the items we want? if (pathAStar != null && pathAStar.EndTile() != null && pathAStar.EndTile().inventory != null && (pathAStar.EndTile().furniture != null && !(myJob.canTakeFromStockpile == false && pathAStar.EndTile().furniture.IsStockpile() == true)) && pathAStar.EndTile().inventory.objectType == desired.objectType) { // We are already moving towards a tile that contains what we want! // so....do nothing? } else { Path_AStar newPath = World.current.inventoryManager.GetPathToClosestInventoryOfType( desired.objectType, CurrTile, desired.maxStackSize - desired.stackSize, myJob.canTakeFromStockpile ); if (newPath == null || newPath.Length() < 1) { //Debug.Log("pathAStar is null and we have no path to object of type: " + desired.objectType); // Cancel the job, since we have no way to get any raw materials! Debug.Log("No tile contains objects of type '" + desired.objectType + "' to satisfy job requirements."); AbandonJob(); return(false); } Debug.Log("pathAStar returned with length of: " + newPath.Length()); DestTile = newPath.EndTile(); // Since we already have a path calculated, let's just save that. pathAStar = newPath; // Ignore first tile, because that's what we're already in. NextTile = newPath.Dequeue(); } // One way or the other, we are now on route to an object of the right type. return(false); } } return(false); // We can't continue until all materials are satisfied. }
private void GetNewJob() { float needPercent = 0; Need need = null; foreach (Need n in needs) { if (n.Amount > needPercent) { need = n; needPercent = n.Amount; } } if (needPercent > 50 && needPercent < 100 && need != null) { myJob = new Job(null, need.restoreNeedFurn.objectType, need.CompleteJobNorm, need.restoreNeedTime, null, Job.JobPriority.High, false, true, false); } if (needPercent == 100 && need != null && need.completeOnFail) { myJob = new Job(CurrTile, null, need.CompleteJobCrit, need.restoreNeedTime * 10, null, Job.JobPriority.High, false, true, true); } // Get the first job on the queue. if (myJob == null) { myJob = World.current.jobQueue.Dequeue(); } if (myJob == null) { Debug.Log(name + " did not find a job."); myJob = new Job( CurrTile, "Waiting", null, UnityEngine.Random.Range(0.1f, 0.5f), null, Job.JobPriority.Low, false); } else { if (myJob.tile == null) { Debug.Log(name + " found a job."); } else { Debug.Log(name + " found a job at x " + myJob.tile.X + " y " + myJob.tile.Y + "."); } } // Get our destination from the job DestTile = myJob.tile; // If the dest tile does not have neighbours it's very if ((DestTile == null || DestTile.HasNeighboursOfType(TileType.Floor) || DestTile.HasNeighboursOfType(TileType.Ladder)) == false) { Debug.Log("No neighbouring floor tiles! Abandoning job."); AbandonJob(false); return; } myJob.cbJobStopped += OnJobStopped; // Immediately check to see if the job tile is reachable. // NOTE: We might not be pathing to it right away (due to // requiring materials), but we still need to verify that the // final location can be reached. Profiler.BeginSample("PathGeneration"); if (myJob.isNeed) { pathAStar = new Path_AStar(World.current, CurrTile, DestTile, need.restoreNeedFurn.objectType, 0, false, true); // This will calculate a path from curr to dest. } else { pathAStar = new Path_AStar(World.current, CurrTile, DestTile); } Profiler.EndSample(); if (pathAStar != null && pathAStar.Length() == 0) { Debug.Log("Path_AStar returned no path to target job tile!"); AbandonJob(false); return; } if (myJob.adjacent) { IEnumerable <Tile> reversed = pathAStar.Reverse(); reversed = reversed.Skip(1); pathAStar = new Path_AStar(new Queue <Tile>(reversed.Reverse())); DestTile = pathAStar.EndTile(); jobTile = DestTile; } else { jobTile = myJob.tile; } }
void Update_DoJob(float deltaTime) { // Do I have a job? jobSearchCooldown -= Time.deltaTime; if (myJob == null) { if (jobSearchCooldown > 0) { // Don't look for job now. return; } GetNewJob(); if (myJob == null) { // There was no job on the queue for us, so just return. jobSearchCooldown = UnityEngine.Random.Range(0.1f, 0.5f); DestTile = currTile; return; } } // We have a job! (And the job tile is reachable) // STEP 1: Does the job have all the materials it needs? if (myJob.HasAllMaterial() == false) { // No, we are missing something! // STEP 2: Are we CARRYING anything that the job location wants? if (inventory != null) { if (myJob.DesiresInventoryType(inventory) > 0) { // If so, deliver the goods. // Walk to the job tile, then drop off the stack into the job. if (currTile == myJob.tile) { // We are at the job's site, so drop the inventory World.current.inventoryManager.PlaceInventory(myJob, inventory); myJob.DoWork(0); // This will call all cbJobWorked callbacks, because even though // we aren't progressing, it might want to do something with the fact // that the requirements are being met. // Are we still carrying things? if (inventory.stackSize == 0) { inventory = null; } else { Debug.LogError("Character is still carrying inventory, which shouldn't be. Just setting to NULL for now, but this means we are LEAKING inventory."); inventory = null; } } else { // We still need to walk to the job site. DestTile = myJob.tile; return; } } else { // We are carrying something, but the job doesn't want it! // Dump the inventory at our feet // TODO: Actually, walk to the nearest empty tile and dump it there. if (World.current.inventoryManager.PlaceInventory(currTile, inventory) == false) { Debug.LogError("Character tried to dump inventory into an invalid tile (maybe there's already something here."); // FIXME: For the sake of continuing on, we are still going to dump any // reference to the current inventory, but this means we are "leaking" // inventory. This is permanently lost now. inventory = null; } } } else { // At this point, the job still requires inventory, but we aren't carrying it! // Are we standing on a tile with goods that are desired by the job? Debug.Log("Standing on Tile check"); if (currTile.inventory != null && (myJob.canTakeFromStockpile || currTile.furniture == null || currTile.furniture.IsStockpile() == false) && myJob.DesiresInventoryType(currTile.inventory) > 0) { // Pick up the stuff! Debug.Log("Pick up the stuff"); World.current.inventoryManager.PlaceInventory( this, currTile.inventory, myJob.DesiresInventoryType(currTile.inventory) ); } else { // Walk towards a tile containing the required goods. Debug.Log("Walk to the stuff"); Debug.Log(myJob.canTakeFromStockpile); // Find the first thing in the Job that isn't satisfied. Inventory desired = myJob.GetFirstDesiredInventory(); if (currTile != nextTile) { // We are still moving somewhere, so just bail out. return; } // Any chance we already have a path that leads to the items we want? if (pathAStar != null && pathAStar.EndTile() != null && pathAStar.EndTile().inventory != null && pathAStar.EndTile().inventory.objectType == desired.objectType) { // We are already moving towards a tile that contains what we want! // so....do nothing? } else { Path_AStar newPath = World.current.inventoryManager.GetPathToClosestInventoryOfType( desired.objectType, currTile, desired.maxStackSize - desired.stackSize, myJob.canTakeFromStockpile ); if (newPath == null) { //Debug.Log("pathAStar is null and we have no path to object of type: " + desired.objectType); // Cancel the job, since we have no way to get any raw materials! AbandonJob(); return; } Debug.Log("pathAStar returned with length of: " + newPath.Length()); if (newPath == null || newPath.Length() == 0) { Debug.Log("No tile contains objects of type '" + desired.objectType + "' to satisfy job requirements."); AbandonJob(); return; } DestTile = newPath.EndTile(); // Since we already have a path calculated, let's just save that. pathAStar = newPath; // Ignore first tile, because that's what we're already in. nextTile = newPath.Dequeue(); } // One way or the other, we are now on route to an object of the right type. return; } } return; // We can't continue until all materials are satisfied. } // If we get here, then the job has all the material that it needs. // Lets make sure that our destination tile is the job site tile. DestTile = myJob.tile; // Are we there yet? if (currTile == myJob.tile) { // We are at the correct tile for our job, so // execute the job's "DoWork", which is mostly // going to countdown jobTime and potentially // call its "Job Complete" callback. myJob.DoWork(deltaTime); } // Nothing left for us to do here, we mostly just need Update_DoMovement to // get us where we want to go. }
private BehaviourTreeStatus FindRequiredStock_Action() { //Debug.Log("FindRequiredStock_Action"); // If the current job has all the materials it needs already, we can just skip this step. if (CurrentJob.HasAllMaterial()) { AbandonMove(); return(BehaviourTreeStatus.Success); } // The job needs some more materials, perhaps we're holding them already so don't need to go anywhere. if (Inventory != null && CurrentJob.NeedsMaterial(Inventory) > 0) { // We are carrying at least some of what the current job needs, so continue on our merry way. AbandonMove(); return(BehaviourTreeStatus.Success); } // The job needs some more materials, and we're not carrying it already, so we need to move to where there are some. // Perhaps the stock is right where we're stood? if (CurrentTile.Inventory != null && (CurrentJob.CanTakeFromStockpile || CurrentTile.Furniture == null || CurrentTile.Furniture.IsStockpile() == false) && (CurrentJob.NeedsMaterial(CurrentTile.Inventory) != 0)) { // The materials we need are right where we're stood! AbandonMove(); return(BehaviourTreeStatus.Success); } // The Job needs some of this: var unsatisfied = CurrentJob.GetFirstRequiredInventory(); // We might have a path to the item we need already var endTile = _path == null ? null : _path.EndTile(); if (_path != null && endTile != null && endTile.Inventory != null && endTile.Inventory.ObjectType == unsatisfied.ObjectType) { // We are already moving towards a tile with the items we want, just keep going. return(BehaviourTreeStatus.Success); } // Look for the first item that matches. _path = World.Instance.InventoryManager.GetClosestPathToInventoryOfType( objectType: unsatisfied.ObjectType, t: CurrentTile, desiredQty: unsatisfied.MaxStackSize - unsatisfied.StackSize, searchInStockpiles: CurrentJob.CanTakeFromStockpile); // If there are no items anywhere that satisfy the requirements, we have to give up on this job. if (_path == null || _path.Length() == 0) { //// Debug.LogFormat("No Tile found containing the desired type ({0}).", unsatisfied.ObjectType); AbandonJob(); return(BehaviourTreeStatus.Failure); } // We've identified where the missing items can be found, so head there. _destinationTile = _path.EndTile(); _nextTile = _path.Dequeue(); return(BehaviourTreeStatus.Success); }