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")); }