Ejemplo n.º 1
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"));
        }