Esempio n. 1
0
        public static IEnumerator <BTResult> FindAndEatSomething(Animal agent, Behavior <Animal> noNearbyFoodBehavior, Func <IEnumerable <Vector3> > getFoodSources, float hungerRestoredPerEat)
        {
            var lastFoodSearchPos = Vector3.Zero;
            var foodSources       = new Queue <Vector3>();

            //While we're still hungry and don't have prey
            while (agent.Hunger > Brain.HungerToStopEating)
            {
                foodSources.Clear();
                while (foodSources.Count == 0)
                {
                    //Look for food sources, if we havent checked already and found it empty or just killed an enemy animal
                    if (Vector3.WrappedDistance(lastFoodSearchPos, agent.Position) > 10 || (agent.TryGetMemory(Animal.IsAnimalKilledMemory, out bool isKilled) && isKilled))
                    {
                        foodSources.AddRange(getFoodSources());
                        lastFoodSearchPos = agent.Position;
                        agent.RemoveMemory(Animal.IsAnimalKilledMemory);
                    }
                    else //None found, get outta here
                    {
                        yield return(new BTResult(noNearbyFoodBehavior.Do(agent), "No food in 10m radius of last query. Moving till we're > 10m from last query."));
                    }
                }

                //Food sources available, find paths to them.
                var routeProps = new RouteProperties()
                {
                    MaxTargetLocationHeightDelta = agent.Species.ClimbHeight
                };
                while (foodSources.Count > 0 && Vector3.WrappedDistance(lastFoodSearchPos, agent.Position) < 10)
                {
                    // Low-effort search for the first option or any other option visited while trying to hit the first with randomizing it in that area
                    var route            = AIUtil.GetRouteToAny(agent.FacingDir, agent.Position, foodSources, agent.Species.GetTraversalData(true), out var targetPosition, 100, 20, agent.Species.HeadDistance, routeProps);
                    var target           = targetPosition.Round;
                    var distanceToTarget = Vector3.WrappedDistanceSq(agent.Position, target);

                    // Move to target if it's far away
                    if (distanceToTarget > 2f)
                    {
                        if (!route.HasValue)
                        {
                            break;
                        }

                        agent.SetRoute(route.Value, AnimalState.LookingForFood, route.Value.EndPosition.Round);
                        yield return(BTResult.RunningChanged("walking to food"));

                        // Stop walking to food if the distance to it has changed or it disappeared
                        var agentTarget = agent.Target;
                        if (agentTarget == null || Vector3.WrappedDistanceSq(agentTarget.Value, target) > 2f)
                        {
                            break;
                        }
                    }

                    // Start eating food if it's close to us
                    if (Vector3.WrappedDistanceSq(target, agent.Position) <= 2f)
                    {
                        agent.ClearRoute();
                        agent.LookAt(target);
                        agent.ChangeState(AnimalState.Eating, 5, false);
                        agent.Hunger -= hungerRestoredPerEat;
                        yield return(BTResult.RunningChanged("chowing down"));

                        agent.LookAt(null);
                        lastFoodSearchPos = Vector3.Zero;
                        //Don't actually destroy the plant, because that's handled in the world layer simulation.
                        //var plant = EcoSim.PlantSim.GetPlant(targetPlantPosition.WorldPosition3i + Vector3i.Up);
                        //if (plant != null) EcoSim.PlantSim.DestroyPlant(plant, DeathType.EatenByAnimal);
                    }
                }
            }
        }