public static IEnumerator <BTStatus> Hunt(Animal agent, Func <Animal, BTStatus> noNearbyFoodBehavior) { while (agent.Hunger > Brain.HungerSatiated) { var agentRegion = RouteRegions.GetRegion(agent.Position.WorldPosition3i); var nearPrey = NetObjectManager.GetObjectsWithin(agent.Position.XZ, agent.DetectionRange).OfType <Animal>() .Where(x => agent.ShouldFleeUs(x) && RouteRegions.GetRegion(x.Position.WorldPosition3i) == agentRegion) .OrderBy(x => Vector3.WrappedDistanceSq(x.Position, agent.Position) + (x.AnimationState == AnimalAnimationState.Sleeping ? -200 : 0)); foreach (var prey in nearPrey) { var i = 0; while (prey.Active) { Eco.Simulation.ExternalInputs.PredatorTracker.AddPredator(agent); var route = AIUtilities.GetRoute(agent.Position, prey.Position, prey.Alertness < 50 ? agent.Species.WanderingSpeed : agent.Species.Speed); agent.Target.SetPath(route); agent.Target.Set(prey); agent.NextTick = Math.Min(agent.Target.TargetTime, WorldTime.Seconds + 3); i++; yield return(BTStatus.Running); if (Vector3.WrappedDistance(agent.Position, prey.Position) < 2f || i > 4) { // making this look good will require a lot of work, for now just let it go agent.Hunger = 0; Eco.Simulation.ExternalInputs.PredatorTracker.RemovePredator(agent); yield break; } } } yield return(noNearbyFoodBehavior(agent)); } }
private IEnumerable <Vector3> GetFoodSources(Animal agent) { var agentRegion = RouteRegions.GetRegion(agent.GroundPosition.WorldPosition3i); return(EcoSim.PlantSim.PlantsWithinRange(agent.Position, 10, plant => agent.Species.Eats(plant.Species) && RouteRegions.GetRegion(plant.Position.WorldPosition3i.Down()) == agentRegion) .Shuffle().Select(plant => plant.Position + Vector3.Down)); }
public static IEnumerator <BTStatus> FoodFinder(Animal agent, Func <Animal, BTStatus> noNearbyFoodBehavior, float hungerRestored) { var lastPos = Vector3.Zero; Queue <Vector3> foodSources = new Queue <Vector3>(); while (agent.Hunger > Brain.HungerSatiated) { foodSources.Clear(); while (foodSources.Count == 0) { if (Vector3.WrappedDistanceSq(lastPos, agent.Position) > 100) { var agentRegion = RouteRegions.GetRegion(agent.Position.WorldPosition3i); foodSources.AddRange(EcoSim.PlantSim.PlantsWithinRange(agent.Position, 10, plant => agent.Species.Eats(plant.Species) && RouteRegions.GetRegion(plant.Position.WorldPosition3i.Down()) == agentRegion).Shuffle().Select(plant => plant.Position + Vector3.Down)); lastPos = agent.Position; } else { yield return(noNearbyFoodBehavior(agent)); } } while (foodSources.Count > 0 && agent.Hunger > Brain.HungerSatiated && Vector3.WrappedDistanceSq(lastPos, agent.Position) < 100) { // low-effort search for the first option or any other option visited while trying to hit the first Vector3 targetPlantPosition; PiecewiseLinearFunction route = AIUtilities.GetRouteToAny(agent.Position, foodSources, agent.Species.WanderingSpeed, out targetPlantPosition, 100, 20, agent.Species.HeadDistance); if (route == null) { break; } agent.Target.SetPath(route); agent.Target.LookPos = targetPlantPosition; agent.AnimationState = AnimalAnimationState.LookingForFood; var target = route.EndPosition; // just in case something interrupts the path while (agent.Target.TargetTime > WorldTime.Seconds && agent.Target.TargetPosition == target) { agent.NextTick = agent.Target.TargetTime; yield return(BTStatus.Running); } if (Vector3.WrappedDistanceSq(target, agent.Position) < 4) { agent.AnimationState = AnimalAnimationState.Eating; agent.NextTick = WorldTime.Seconds + 10; agent.Hunger -= hungerRestored; agent.Target.LookPos = targetPlantPosition; yield return(BTStatus.Running); } // something interrupted eating, probably need to find new food if (agent.Target.TargetPosition != target) { break; } } } }
// Groups form and dissolve along with animal visibility public static Animal GetLeader(Animal agent) { float checkTime; if (agent.TryGetMemory <float>(IsAlone, out checkTime)) { if (TimeUtil.Seconds < checkTime) { return(agent); } agent.RemoveMemory(IsAlone); } List <Animal> followers; if (agent.TryGetMemory <List <Animal> >(IsLeader, out followers)) { followers.RemoveAll(x => x == null || x.Dead); // this animal is a leader as long as it has followers if (followers.Count > 0) { return(agent); } else { agent.RemoveMemory(IsLeader); } } Animal leader; if (!agent.TryGetMemory <Animal>(IsFollower, out leader)) { var agentRegion = RouteRegions.GetRegion(agent.Position.WorldPosition3i); var nearAnimals = NetObjectManager.GetObjectsWithin(agent.Position.XZ, agent.Species.VoxelsPerEntry).OfType <Animal>() .Where(x => !x.Dead && x.Visible && x.Species == agent.Species && x != agent && HaveAdjacentHomePositions(agent, x) && // don't join a herd far from home RouteRegions.GetRegion(x.Position.WorldPosition3i) == agentRegion); if (!nearAnimals.Any()) { agent.SetMemory(IsAlone, TimeUtil.Seconds + TimeBetweenHerdChecks); return(agent); } List <Animal> stragglers = new List <Animal>(); foreach (var nearAnimal in nearAnimals) { Animal tmpLeader; if (nearAnimal.TryGetMemory <Animal>(IsFollower, out tmpLeader)) { if (leader == null && tmpLeader.Visible && !tmpLeader.Dead && HaveAdjacentHomePositions(tmpLeader, agent)) { leader = tmpLeader; } } else if (nearAnimal.HasMemory(IsLeader)) { if (leader == null) { leader = nearAnimal; } } else { stragglers.Add(nearAnimal); } } if (leader == null) { // become the leader leader = agent; agent.SetMemory(IsLeader, stragglers); } else { // join the pack along w/ any other stragglers stragglers.Add(agent); if (!leader.TryGetMemory <List <Animal> >(IsLeader, out followers)) { followers = new List <Animal>(); leader.SetMemory(IsLeader, followers); } foreach (var newMember in stragglers) { followers.Add(newMember); } } foreach (var newMember in stragglers) { newMember.SetMemory(IsFollower, leader); } } else { if (leader == null || !leader.Visible || leader.Dead) { agent.RemoveMemory(IsFollower); return(agent); } } return(leader); }