internal static void Generate(Chunk chunk, List <Position> takenPositions) { int clutterCount = Settings.Random.Next(MIN_CLUTTER_PER_CHUNK, MAX_CLUTTER_PER_CHUNK + 1); for (int i = 0; i < clutterCount; i++) { //returns number avoiding upper chunk boundaries ensuring cross chunk placements dont touch each other int xProposedInChunk = Settings.Random.Next(0, Chunk.CHUNK_SIZE - 1); int zProposedInChunk = Settings.Random.Next(0, Chunk.CHUNK_SIZE - 1); int yProposed = chunk.HeightMap[xProposedInChunk, zProposedInChunk]; var block = chunk.Blocks[xProposedInChunk, yProposed, zProposedInChunk]; if (block.Type != Block.BlockType.Grass && block.Type != Block.BlockType.Snow) { continue; } int xProposedInWorld = chunk.Coords.WorldCoordsX + xProposedInChunk; int zProposedInWorld = chunk.Coords.WorldCoordsZ + zProposedInChunk; //ensure clutter is not placed too close to another taken coord, otherwise skip it if (TreeGenerator.IsPositionTaken(takenPositions, xProposedInWorld, zProposedInWorld, DISTANCE_TOLERANCE)) { continue; } //place the clutter var clutterType = (ClutterType)Settings.Random.Next(0, 7); //0-6 takenPositions.Add(new Position(xProposedInWorld, yProposed, zProposedInWorld)); chunk.Clutters.Add(new Clutter(new Coords(xProposedInWorld, yProposed + 1, zProposedInWorld), clutterType)); //add new clutter to the chunk collection } }
public static void Generate() { Debug.WriteLine("Generating new world: " + Settings.WorldFilePath); Debug.WriteLine("World type: {0}, Size {1}x{2}", WorldData.WorldType, WorldData.SizeInChunksX, WorldData.SizeInChunksZ); Settings.Random = string.IsNullOrEmpty(WorldData.RawSeed) ? new Random() : new Random(GetNumericSeed()); WorldData.Chunks = new Chunks(WorldData.SizeInChunksX, WorldData.SizeInChunksZ); const int MIN_SURFACE_HEIGHT = Chunk.CHUNK_HEIGHT / 2 - 40; //max amount below half const int MAX_SURFACE_HEIGHT = Chunk.CHUNK_HEIGHT / 2 + 8; //max amount above half var heightMap = PerlinNoise.GetIntMap(MIN_SURFACE_HEIGHT, MAX_SURFACE_HEIGHT, 8); var mineralMap = PerlinNoise.GetFloatMap(1, MAX_SURFACE_HEIGHT - 5, 2); var chunkCount = 1; foreach (Chunk chunk in WorldData.Chunks) { Settings.Launcher.UpdateProgressInvokable(string.Format("Generating Chunks {0} / {1}", chunkCount, WorldData.SizeInChunksX * WorldData.SizeInChunksZ), chunkCount, WorldData.SizeInChunksX * WorldData.SizeInChunksZ); //bm: we can't run this in parallel or the results will not be deterministic based on our seed. GenerateChunk(WorldData.Chunks[chunk.Coords.X, chunk.Coords.Z], heightMap, mineralMap); chunkCount++; } //loop through chunks again for actions that require the neighboring chunks to be built Debug.WriteLine("Completing growth in chunks and building heightmaps..."); Settings.Launcher.UpdateProgressInvokable("Completing growth in chunks...", 0, 0); foreach (Chunk chunk in WorldData.Chunks) { //build heightmap here only so we know where to place trees/clutter (it will get built again on world load anyway) chunk.BuildHeightMap(); var takenPositions = new List <Position>(); //positions taken by tree or clutter, ensures neither spawn on top of or directly touching another //generate trees if (WorldData.GenerateWithTrees) { TreeGenerator.Generate(chunk, takenPositions); } //generate clutter ClutterGenerator.Generate(chunk, takenPositions); } Settings.Random = new Random(); //reset the random object to ensure the seed is not used for any more random numbers as this could make gameplay predictable Debug.WriteLine("World generation complete."); //default sun to directly overhead in new worlds SkyHost.SunAngleRadians = OpenTK.MathHelper.PiOver2; SkyHost.SunLightStrength = SkyHost.BRIGHTEST_SKYLIGHT_STRENGTH; Debug.WriteLine("New world saving..."); Settings.Launcher.UpdateProgressInvokable("New world saving...", 0, 0); WorldData.SaveToDisk(); Debug.WriteLine("New world save complete."); }