コード例 #1
0
        public static BTResult Swim(Animal agent, Vector2 direction, bool wandering, AnimalState state, bool surface, int tries = 10)
        {
            var targetPos = AIUtil.FindTargetSwimPosition(agent.Position, 5.0f, 20.0f, direction, 90, 360, tries, surface).XYZi;

            if (targetPos == agent.Position)
            {
                return(BTResult.Failure("target position is current position."));
            }

            // Avoid fish swimming too close to coast line
            // TODO: Avoid building routes near coast, cache available points far away from coast
            if (!agent.Species.CanSwimNearCoast)
            {
                var waterHeight = World.World.GetWaterHeight(targetPos.XZ);
                var isNearCoast = ((WorldPosition3i)targetPos).SpiralOutXZIter(3).Any(groundPos => World.World.GetBlock((WrappedWorldPosition3i)groundPos.X_Z(waterHeight)).Is <Solid>());
                if (isNearCoast)
                {
                    return(BTResult.Failure("target position is too close to coast line"));
                }
            }

            var targetBlock = World.World.GetBlock(targetPos);

            // If an animal can't float on water surface - move it a block below highest water block TODO: make them move on underwater ground
            // TODO: Remove after pathfinder improvements
            if (!agent.Species.FloatOnSurface && targetBlock is WaterBlock && World.World.GetBlock(targetPos + Vector3i.Up).Is <Empty>())
            {
                targetPos += Vector3i.Down;
            }
            //if (targetBlock.Is<Solid>()) targetPos += Vector3i.Up;
            if (targetBlock.Is <Empty>())
            {
                targetPos += Vector3i.Down;
                // Fail if target position is too shallow
                if (World.World.GetBlock(targetPos).Is <Solid>())
                {
                    return(BTResult.Failure("target position is too thin"));
                }
            }
            // Clamp current position to ground or water, if can't float on water surface - stay below water height TODO: make them move on underwater ground
            var pos = World.World.ClampToWaterHeight(agent.Position.XYZi);

            // TODO: Remove after pathfinder improvements
            if (!agent.Species.FloatOnSurface && pos.y == World.World.GetWaterHeight(agent.Position.XZi))
            {
                pos += Vector3i.Down;
            }

            var route     = Route.Basic(agent.Species.GetTraversalData(wandering), agent.FacingDir, pos + Vector3i.Down, targetPos); //For fish, we need to compensate, since route is built from positions of the ground below
            var routeTime = route.IsValid ? agent.SetRoute(route, state, null) : 0f;

            return(routeTime < float.Epsilon ? BTResult.Failure("route not set") : BTResult.Success($"swimming path"));
        }
コード例 #2
0
        // 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());
        }
コード例 #3
0
        static BTResult DoSleepNearLeader(Behavior <Animal> beh, Animal agent)
        {
            var leader = GetLeader(agent);

            if (leader == null)
            {
                return(BTResult.Failure("No leader"));
            }
            if (leader == agent)
            {
                return(BTResult.Failure("We are leader"));
            }

            //If the leader is sleeping...
            if (leader.State == AnimalState.Sleeping)
            {
                // and we're already close enough, sleep now.
                if (Vector3.WrappedDistance(agent.Position, leader.Position) < agent.Species.HeadDistance * 3)
                {
                    return(agent.ChangeState(AnimalState.LyingDown, 60f, true));
                }

                //Otherwise, move to them to sleep.
                // TODO: avoid overlapping with other herd members, make the leader pick a spot to sleep that can accommodate the herd
                var routeProps = new RouteProperties {
                    MaxTargetLocationHeightDelta = agent.Species.ClimbHeight
                };
                var route = AIUtil.GetRouteFacingTarget(agent.FacingDir, agent.Position, leader.Position, agent.Species.GetTraversalData(true), agent.Species.HeadDistance * 2, routeProps: routeProps);
                if (!route.IsValid)
                {
                    return(BTResult.Failure("can't get route to leader"));
                }
                agent.SetRoute(route, AnimalState.Wander);
                return(BTResult.Success("walking to sleep near leader"));
            }
            return(BTResult.Failure("leader not sleeping"));
        }
コード例 #4
0
        public static BTResult AmphibiousMovement(Animal agent, Vector2 generalDirection, bool wandering, AnimalState state, float minDistance = 2f, float maxDistance = 20f)
        {
            var startPathPos = agent.Position.WorldPosition3i;

            //Plop us into water if we're above it.
            if (World.World.GetBlock((Vector3i)startPathPos.Down()).GetType() == typeof(WaterBlock))
            {
                startPathPos = startPathPos.Down();
            }

            if (!World.World.IsUnderwater(startPathPos))
            {
                startPathPos = RouteManager.NearestWalkablePathPos(agent.Position.WorldPosition3i); //Boot us down/up to the surface if we're on land.
            }
            if (!startPathPos.IsValid)
            {
                return(LandMovement(agent, generalDirection, wandering, state, minDistance, maxDistance));
            }

            generalDirection = generalDirection == Vector2.zero ? Vector2.right.Rotate(RandomUtil.Range(0f, 360)) : generalDirection.Normalized;

            // Take random ground position in the given direction
            var targetGround = (agent.Position + (generalDirection * RandomUtil.Range(minDistance, maxDistance)).X_Z()).WorldPosition3i;

            // Floating animals will stick to water surface or land, non floating - land or return to swimming state
            if (World.World.IsUnderwater(targetGround) && agent.Species.FloatOnSurface)
            {
                targetGround.y = World.World.MaxWaterHeight[targetGround];
            }
            else
            {
                targetGround = RouteManager.NearestWalkableXYZ(targetGround, 5);
                if (!agent.Species.FloatOnSurface && !targetGround.IsValid)
                {
                    agent.Floating = false;
                    return(BTResult.Failure("Can't float, continue swimming"));
                }
            }

            // This is a low-effort search that includes water surface and should occasionally fail, just pick a semi-random node that was visited when it fails
            var routeProps = new RouteProperties()
            {
                MaxTargetLocationHeightDelta = agent.Species.ClimbHeight
            };
            var allowWaterSearch = new AStarSearch(RouteCacheData.NeighborsIncludeWater, agent.FacingDir, startPathPos, targetGround, 30, routeProps, false);

            if (allowWaterSearch.Status != SearchStatus.PathFound)
            {
                targetGround = allowWaterSearch.GroundNodes.Last().Key;
                allowWaterSearch.GetPathToWaterPos(targetGround);
            }

            // Apply land movement only for land positions
            if (!World.World.IsUnderwater(agent.Position.WorldPosition3i))
            {
                if (allowWaterSearch.GroundPath.Count < 2)
                {
                    return(LandMovement(agent, generalDirection, wandering, state, minDistance, maxDistance, skipRouteProperties: true));
                }
                if (allowWaterSearch.Status == SearchStatus.Unpathable && allowWaterSearch.GroundNodes.Count < RouteRegions.MinimumRegionSize)
                {
                    // Search region was unexpectedly small and agent is on land, might be trapped by player construction.
                    // Try regular land movement so region checks can apply & the agent can get unstuck (or die)
                    return(LandMovement(agent, generalDirection, wandering, state, minDistance, maxDistance));
                }
            }

            var smoothed = allowWaterSearch.LineOfSightSmoothWaterPosition(agent.GroundPosition);
            var route    = new Route(agent.Species.GetTraversalData(wandering), agent.FacingDir, smoothed);

            if (!route.IsValid)
            {
                route = Route.Basic(agent.Species.GetTraversalData(wandering), agent.FacingDir, agent.GroundPosition, route.EndPosition);
            }
            var routeTime = agent.SetRoute(route, state, null);

            return(routeTime < float.Epsilon ? BTResult.Failure("route not set") : BTResult.Success($"swimming path"));
        }