// If the animal is stuck somewhere it shouldn't be help it escape or die if its very far from walkable terrain public static BTStatus LandAnimalUnStuckOrDie(Animal agent, float speed, AnimalAnimationState state) { if (!RouteManager.Ready) { // don't kill everything if pathfinding is resetting agent.LockNextTick(WorldTime.Seconds + 20f); return BTStatus.Success; } const int MaxUnStuckDistance = 20; var nearestValid = RouteManager.NearestWalkableXYZ(agent.Position.Round, MaxUnStuckDistance); if (nearestValid == agent.Position.WorldPosition3i) return BTStatus.Failure; if (!nearestValid.IsValid) { // cheating failed? time to die! agent.Kill(DeathType.Construction); return BTStatus.Success; } // ignore terrain, path directly to a valid area agent.Target.Set(agent.Position, (Vector3)nearestValid, speed); agent.LockNextTick(agent.Target.TargetTime); agent.AnimationState = state; return BTStatus.Success; }
public static BTStatus RouteToWater(Animal agent, float speed, AnimalAnimationState state) { var start = agent.Position.WorldPosition3i; // only works for surface start points if (World.World.IsUnderwater(start) || start.y != World.World.MaxYCache[start] || RouteManager.RouteToSeaMap == null) return BTStatus.Failure; if (start.IsValid) { agent.AnimationState = state; // get a destination a decent var current = start; for (int i = 0; i < 10; i++) { var next = RouteManager.RouteToSeaMap[current]; if (next == current) break; current = next; if (WorldPosition3i.Distance(current, start) > 10) break; } var target = ((Vector2)(WorldPosition3i.GetDelta(current, start)).XZ).Rotate(RandomUtil.Range(-20, 20)); return AmphibiousMovement(agent, target, speed, state, 10, 20); } return BTStatus.Failure; }
public static BTStatus Swim(Animal agent, Vector2 direction, float speed, AnimalAnimationState state, bool surface, int tries = 10) { var targetPos = AIUtilities.FindTargetSwimPosition(agent.Position, 5.0f, 20.0f, direction, 90, 360, tries, surface); if (targetPos == agent.Position) return BTStatus.Failure; agent.AnimationState = state; agent.Target.Set(agent.Position, targetPos, speed); agent.NextTick = agent.Target.TargetTime; return BTStatus.Success; }
public static BTStatus AmphibiousMovement(Animal agent, Vector2 generalDirection, float speed, AnimalAnimationState state, float minDistance = 2f, float maxDistance = 20f) { var start = agent.Position.WorldPosition3i; if (!World.World.IsUnderwater(start)) start = RouteManager.NearestWalkableY(agent.Position.WorldPosition3i); if (!start.IsValid) return LandMovement(agent, generalDirection, speed, state, minDistance, maxDistance); if (generalDirection == Vector2.zero) generalDirection = Vector2.right.Rotate(RandomUtil.Range(0f, 360)); else generalDirection = generalDirection.Normalized; var target = (agent.Position + (generalDirection * RandomUtil.Range(minDistance, maxDistance)).X_Z()).WorldPosition3i; if (World.World.IsUnderwater(target)) target.y = World.World.MaxWaterHeight[target]; else target = RouteManager.NearestWalkableXYZ(target, 5); // 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 allowWaterSearch = new AStarSearch(RouteCacheData.NeighborsIncludeWater, start, target, 30); if (allowWaterSearch.Status != SearchStatus.PathFound) { target = allowWaterSearch.Nodes.Last().Key; allowWaterSearch.GetPath(target); } if (allowWaterSearch.Path.Count < 2) return BTStatus.Failure; else if (allowWaterSearch.Status == SearchStatus.Unpathable && allowWaterSearch.Nodes.Count < RouteRegions.MinimumRegionSize && !World.World.IsUnderwater(agent.Position.WorldPosition3i)) { // 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, speed, state, minDistance, maxDistance); } var smoothed = allowWaterSearch.LineOfSightSmooth(agent.Position); PiecewiseLinearFunction route = AIUtilities.ProjectRoute(smoothed, speed); agent.AnimationState = state; agent.NextTick = WorldTime.Seconds + route.EndTime; agent.Target.SetPath(route); return BTStatus.Success; }
public static BTStatus LandMovement(Animal agent, Vector2 direction, float speed, AnimalAnimationState state, float minDistance = 2f, float maxDistance = 20f, float minDirectionOffsetDegrees = 0, float maxDirectionOffsetDegrees = 360, int tryCount = 10) { var search = AIUtilities.FindRoute(agent.Position, 2f, 20f, direction); if (search != null) { var smoothed = search.LineOfSightSmooth(agent.Position); PiecewiseLinearFunction route = AIUtilities.ProjectRoute(smoothed, speed); agent.NextTick = WorldTime.Seconds + route.EndTime; agent.Target.SetPath(route); agent.AnimationState = state; return BTStatus.Success; } else return LandAnimalUnStuckOrDie(agent, speed, state); }