public virtual void PathingThreadAction(PathingManager.PathingContext context) { int maxRadius = ServerManager.ServerSettings.Banner.MaximumZombieSpawnRadius; QueuedSpawn spawn; while (QueuedSpawns.TryDequeue(out spawn)) { Vector3Int position; QueuedSpawnResult result; result.Payload = spawn.Payload; result.path = null; var getSpawnResult = TryGetSpawnLocation(context, spawn.goalPosition, spawn.goalSafeRadius, maxRadius, spawn.maxWalkDistance, out position); switch (getSpawnResult) { case ESpawnResult.Success: { Path path; if (context.Pathing.TryFindPath(ref context, position, spawn.goalPosition, out path, 2 * 1000 * 1000) == PathFinder.EPathFindingResult.Success) { result.path = path; result.result = QueuedSpawnResult.EResult.SuccessPath; } else { result.result = QueuedSpawnResult.EResult.FailPath; } } break; case ESpawnResult.NotLoaded: case ESpawnResult.Impossible: result.result = QueuedSpawnResult.EResult.GoodFailSpawn; break; default: case ESpawnResult.Fail: result.result = QueuedSpawnResult.EResult.BadFailSpawn; break; } QueuedSpawnResults.Enqueue(result); } isQueuedForPathing = false; }
public ESpawnResult TryGetSpawnLocation(PathingManager.PathingContext context, Vector3Int bannerPosition, int minSpawnRadius, int maxSpawnRadius, float maxSpawnWalkDistance, out Vector3Int positionFinal) { int maxWalkDistance = Math.RoundToInt(maxSpawnWalkDistance); maxSpawnRadius = Math.Min(maxWalkDistance, maxSpawnRadius); if (minSpawnRadius >= maxSpawnRadius) { positionFinal = Vector3Int.invalidPos; return(ESpawnResult.Impossible); } int foundPathPosButSafes = 0; lock (pathingRandomSource) { for (int spawnTry = 0; spawnTry < MAX_SPAWN_TRIES_PER_BANNER; spawnTry++) { if (TrySamplePosition(true, out Vector3Int possiblePosition)) { if (TestPositionFinal(possiblePosition, out positionFinal)) { return(ESpawnResult.Success); } } } if (foundPathPosButSafes > 3) { // so we did find multiple position we can spawn at, but they were blocked by other banners than the one we're spawning around // in that case retry but without limiting the walking distance in a 'diamond' pattern, just take the axis aligned max spawn radius for (int spawnTry = 0; spawnTry < MAX_SPAWN_TRIES_PER_BANNER; spawnTry++) { if (TrySamplePosition(false, out Vector3Int possiblePosition)) { if (TestPositionFinal(possiblePosition, out positionFinal)) { return(ESpawnResult.Success); } } } } } positionFinal = Vector3Int.invalidPos; return(ESpawnResult.Fail); bool TestPositionFinal(Vector3Int possiblePosition, out Vector3Int positionFinalTest) { if (context.NavWorld.TryGetClosestAIPosition( possiblePosition, NavWorld.EAIClosestPositionSearchType.ChunkAndDirectNeighbours, out positionFinalTest )) { if (ServerManager.BlockEntityTracker.BannerTracker.IsSafeZone(positionFinalTest, out Vector3Int foundBanner)) { if (foundBanner != bannerPosition) { foundPathPosButSafes++; } } else { return(true); } } return(false); } bool TrySamplePosition(bool checkWalkingDistance, out Vector3Int position) { for (int i = 0; i < 1000; i++) { Vector3Int offset = new Vector3Int { x = pathingRandomSource.Next(-maxSpawnRadius, maxSpawnRadius), y = pathingRandomSource.Next(-maxSpawnRadius, maxSpawnRadius), z = pathingRandomSource.Next(-maxSpawnRadius, maxSpawnRadius) }; if (offset.MaxPartAbs <= minSpawnRadius) { continue; // fais safe zone check } if (checkWalkingDistance && Math.Abs(offset.x) + Math.Abs(offset.z) > maxWalkDistance) { continue; // fails walk check } position = bannerPosition + offset; return(true); } position = default; return(false); } }