// If the animal is stuck somewhere it shouldn't be help it escape or die if its very far from walkable terrain public static BTResult LandAnimalUnStuckOrDie(Animal agent) { const int maxUnStuckDistance = 20; var nearestValid = RouteManager.NearestWalkableXYZ(agent.Position.XYZi, maxUnStuckDistance); if (nearestValid == agent.Position.WorldPosition3i) { agent.NextTick = WorldTime.Seconds + 10f; return(BTResult.Failure()); } if (!nearestValid.IsValid) { // cheating failed? time to die! agent.Kill(); return(BTResult.Success()); } // ignore terrain, path directly to a valid area, but only if noone is around *shy* or if he's really trying if (agent.TryGetMemory(Animal.TriesToUnStuckMemory, out int tries)) { tries += 1; } agent.SetMemory(Animal.TriesToUnStuckMemory, tries); if (!NetObjectManager.GetObjectsWithin(agent.Position.XZ, 20).OfType <Player>().Any() || tries >= 3) { var route = AIUtil.GetRoute(agent.FacingDir, agent.Position, (Vector3)nearestValid, agent.Species.GetTraversalData(true), null, null); if (!route.IsValid) { agent.NextTick = WorldTime.Seconds + 10; return(BTResult.Failure()); } // Proceed with route with time length more than 0 var timeToFinishRoute = agent.SetRoute(route, AnimalState.Wander); if (timeToFinishRoute > float.Epsilon) { agent.RemoveMemory(Animal.TriesToUnStuckMemory); return(BTResult.Success()); } } agent.NextTick = WorldTime.Seconds + 10; return(BTResult.Failure()); }