From 567c422f8cd42eba2437f9a8c2522716a1649be7 Mon Sep 17 00:00:00 2001 From: hazel Date: Mon, 26 Jan 2026 22:04:39 +0100 Subject: celesteia archive, last updated april 9th 2024 Signed-off-by: hazel --- source/game/planets/BlockState.cs | 43 ++++ source/game/planets/Chunk.cs | 158 ++++++++++++ source/game/planets/ChunkMap.cs | 99 ++++++++ source/game/planets/GeneratedPlanet.cs | 35 +++ source/game/planets/generation/IWorldGenerator.cs | 18 ++ .../planets/generation/TerranPlanetGenerator.cs | 275 +++++++++++++++++++++ 6 files changed, 628 insertions(+) create mode 100644 source/game/planets/BlockState.cs create mode 100644 source/game/planets/Chunk.cs create mode 100644 source/game/planets/ChunkMap.cs create mode 100644 source/game/planets/GeneratedPlanet.cs create mode 100644 source/game/planets/generation/IWorldGenerator.cs create mode 100644 source/game/planets/generation/TerranPlanetGenerator.cs (limited to 'source/game/planets') diff --git a/source/game/planets/BlockState.cs b/source/game/planets/BlockState.cs new file mode 100644 index 0000000..e7d4be0 --- /dev/null +++ b/source/game/planets/BlockState.cs @@ -0,0 +1,43 @@ +using Celesteia.Game.Components.Items; +using Celesteia.Resources; +using Celesteia.Resources.Sprites; +using Celesteia.Resources.Types; + +namespace Celesteia.Game.Planets { + public struct BlockState { + public static BlockState None = new BlockState() { Empty = true }; + + public bool Empty; + + private byte _id; + public byte BlockID { + get => _id; + set { + _id = value; + Type = ResourceManager.Blocks.GetBlock(BlockID) as BlockType; + + Empty = _id == 0; + Translucent = Type.Translucent; + Frames = Type.Frames; + } + } + + public BlockType Type { get; private set; } + public bool Translucent { get; private set; } + public BlockFrames Frames { get; private set; } + + public int BreakProgress; + + public bool Draw; + public bool HasFrames() => Frames != null; + + public BlockData Data; + public bool HasData() => Data != null; + } + + public class BlockData {} + public class TileEntity : BlockData {} + public class Container : TileEntity { + public Inventory inventory; + } +} \ No newline at end of file diff --git a/source/game/planets/Chunk.cs b/source/game/planets/Chunk.cs new file mode 100644 index 0000000..5fc7618 --- /dev/null +++ b/source/game/planets/Chunk.cs @@ -0,0 +1,158 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Celesteia.Resources; +using Celesteia.Graphics; +using System; +using Celesteia.Resources.Sprites; +using Celesteia.Game.Components.Items; + +namespace Celesteia.Game.Planets { + public class Chunk { + public const int CHUNK_SIZE = 16; + + public Point TruePosition { get; private set; } + private ChunkVector _position; + public ChunkVector Position { + get => _position; + set { _position = value; TruePosition = _position.Resolve(); } + } + + private BlockState[,] foreground; + private BlockState[,] background; + + public Chunk(ChunkVector cv) { + Position = cv; + + foreground = new BlockState[CHUNK_SIZE, CHUNK_SIZE]; + background = new BlockState[CHUNK_SIZE, CHUNK_SIZE]; + } + + public BlockState GetForeground(int x, int y) => foreground[x, y]; + public BlockState GetBackground(int x, int y) => background[x, y]; + + public void SetForeground(int x, int y, byte id) { + foreground[x, y].BlockID = id; + foreground[x, y].BreakProgress = 0; + UpdateDraws(x, y); + } + + public void SetBackground(int x, int y, byte id) { + background[x, y].BlockID = id; + background[x, y].BreakProgress = 0; + UpdateDraws(x, y); + } + + public void UpdateDraws(int x, int y) { + foreground[x, y].Draw = foreground[x, y].HasFrames(); + background[x, y].Draw = background[x, y].HasFrames() && foreground[x, y].Translucent; + } + + private NamespacedKey? dropKey; + public void AddBreakProgress(int x, int y, int power, bool wall, out ItemStack drops) { + dropKey = null; + drops = null; + + if (wall) { + background[x, y].BreakProgress += power; + if (background[x, y].BreakProgress > background[x, y].Type.Strength) { + dropKey = background[x, y].Type.DropKey; + SetBackground(x, y, 0); + } + } else { + foreground[x, y].BreakProgress += power; + if (foreground[x, y].BreakProgress > foreground[x, y].Type.Strength) { + dropKey = foreground[x, y].Type.DropKey; + SetForeground(x, y, 0); + } + } + + if (dropKey.HasValue) drops = new ItemStack(dropKey.Value, 1); + } + + public void Draw(GameTime gameTime, SpriteBatch spriteBatch) { + for (int i = 0; i < CHUNK_SIZE; i++) { + for (int j = 0; j < CHUNK_SIZE; j++) { + DrawAllAt(i, j, gameTime, spriteBatch); + } + } + } + + Vector2 v; + private void DrawAllAt(int x, int y, GameTime gameTime, SpriteBatch spriteBatch) { + v.X = TruePosition.X + x; + v.Y = TruePosition.Y + y; + v.Floor(); + + if (background[x, y].Draw) { + DrawWallTile(background[x, y].Frames.GetFrame(0), spriteBatch, v); + if (background[x, y].BreakProgress > 0) DrawWallTile(ResourceManager.Blocks.BreakAnimation.GetProgressFrame( + // Background block breaking progress. + background[x, y].BreakProgress / (float) background[x, y].Type.Strength + ), spriteBatch, v); + } + if (foreground[x, y].Draw) { + DrawTile(foreground[x, y].Frames.GetFrame(0), spriteBatch, v); + if (foreground[x, y].BreakProgress > 0) DrawTile(ResourceManager.Blocks.BreakAnimation.GetProgressFrame( + // Foreground block breaking progress. + foreground[x, y].BreakProgress / (float) foreground[x, y].Type.Strength + ), spriteBatch, v); + } + } + + public void DrawTile(BlockFrame frame, SpriteBatch spriteBatch, Vector2 v) { + frame.Draw(0, spriteBatch, v, Color.White, 0.4f); + } + + public void DrawWallTile(BlockFrame frame, SpriteBatch spriteBatch, Vector2 v) { + frame.Draw(0, spriteBatch, v, Color.DarkGray, 0.5f); + } + } + + public struct ChunkVector { + public int X; + public int Y; + + public ChunkVector(int x, int y) { + X = x; + Y = y; + } + + public static ChunkVector FromVector2(Vector2 vector) + { + return new ChunkVector( + (int)MathF.Floor(vector.X / Chunk.CHUNK_SIZE), + (int)MathF.Floor(vector.Y / Chunk.CHUNK_SIZE) + ); + } + + public static ChunkVector FromPoint(Point point) + => new ChunkVector(point.X / Chunk.CHUNK_SIZE, point.Y/ Chunk.CHUNK_SIZE); + + public Point Resolve() { + return new Point(X * Chunk.CHUNK_SIZE, Y * Chunk.CHUNK_SIZE); + } + + public static int Distance(ChunkVector a, ChunkVector b) { + return MathHelper.Max(Math.Abs(b.X - a.X), Math.Abs(b.Y - a.Y)); + } + + public static bool operator ==(ChunkVector a, ChunkVector b) { + return a.Equals(b); + } + + public override bool Equals(object obj) + { + if (obj is ChunkVector) return ((ChunkVector)obj).X == this.X && ((ChunkVector)obj).Y == this.Y; + return false; + } + + public static bool operator !=(ChunkVector a, ChunkVector b) { + return !a.Equals(b); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/source/game/planets/ChunkMap.cs b/source/game/planets/ChunkMap.cs new file mode 100644 index 0000000..234d034 --- /dev/null +++ b/source/game/planets/ChunkMap.cs @@ -0,0 +1,99 @@ +using System; +using Celesteia.Game.Components.Items; +using Celesteia.Game.Planets.Generation; +using Celesteia.Resources; +using Microsoft.Xna.Framework; +using MonoGame.Extended; + +namespace Celesteia.Game.Planets { + public class ChunkMap { + public int Width, Height; + public int BlockWidth => Width * Chunk.CHUNK_SIZE; + public int BlockHeight => Height * Chunk.CHUNK_SIZE; + + public Chunk[,] Map; + + public ChunkMap(int w, int h) { + Width = w; + Height = h; + + Map = new Chunk[w, h]; + } + + public Chunk GetChunk(int chunkX, int chunkY) => ChunkIsInMap(chunkX, chunkY) ? Map[chunkX, chunkY] : null; + public Chunk GetChunk(ChunkVector cv) => GetChunk(cv.X, cv.Y); + public Chunk GetChunkAtCoordinates(int x, int y) => GetChunk(x / Chunk.CHUNK_SIZE, y / Chunk.CHUNK_SIZE); + public Chunk GetChunkAtPoint(Point point) => GetChunkAtCoordinates(point.X, point.Y); + + // BACKGROUND MANAGEMENT + public BlockState GetBackground(int blockX, int blockY) { + Chunk c = GetChunkAtCoordinates(blockX, blockY); + if (c != null) return c.GetBackground(blockX % Chunk.CHUNK_SIZE, blockY % Chunk.CHUNK_SIZE); + + return BlockState.None; + } + public BlockState GetBackground(Point point) => GetBackground(point.X, point.Y); + + public void SetBackgroundID(int blockX, int blockY, byte id) { + Chunk c = GetChunkAtCoordinates(blockX, blockY); + if (c != null) c.SetBackground(blockX % Chunk.CHUNK_SIZE, blockY % Chunk.CHUNK_SIZE, id); + } + public void SetBackgroundID(Point point, byte id) => SetBackgroundID(point.X, point.Y, id); + + public void AddBackgroundBreakProgress(int blockX, int blockY, int power, out ItemStack drops) { + drops = null; + Chunk c = GetChunkAtCoordinates(blockX, blockY); + if (c != null) c.AddBreakProgress(blockX % Chunk.CHUNK_SIZE, blockY % Chunk.CHUNK_SIZE, power, true, out drops); + } + public void AddBackgroundBreakProgress(Point point, int power, out ItemStack drops) => AddBackgroundBreakProgress(point.X, point.Y, power, out drops); + + // FOREGROUND MANAGEMENT + public BlockState GetForeground(int blockX, int blockY) { + Chunk c = GetChunkAtCoordinates(blockX, blockY); + if (c != null) return c.GetForeground(blockX % Chunk.CHUNK_SIZE, blockY % Chunk.CHUNK_SIZE); + + return BlockState.None; + } + public BlockState GetForeground(Point point) => GetForeground(point.X, point.Y); + + public void SetForegroundID(int blockX, int blockY, byte id) { + Chunk c = GetChunkAtCoordinates(blockX, blockY); + if (c != null) c.SetForeground(blockX % Chunk.CHUNK_SIZE, blockY % Chunk.CHUNK_SIZE, id); + } + public void SetForegroundID(Point point, byte id) => SetForegroundID(point.X, point.Y, id); + + public void AddForegroundBreakProgress(int blockX, int blockY, int power, out ItemStack drops) { + drops = null; + Chunk c = GetChunkAtCoordinates(blockX, blockY); + if (c != null) c.AddBreakProgress(blockX % Chunk.CHUNK_SIZE, blockY % Chunk.CHUNK_SIZE, power, false, out drops); + } + public void AddForegroundBreakProgress(Point point, int power, out ItemStack drops) => AddForegroundBreakProgress(point.X, point.Y, power, out drops); + + // FOR ADJACENCY CHECKS + public bool GetAny(int blockX, int blockY) { + Chunk c = GetChunkAtCoordinates(blockX, blockY); + return c != null && ( + !c.GetForeground(blockX % Chunk.CHUNK_SIZE, blockY % Chunk.CHUNK_SIZE).Empty || + !c.GetBackground(blockX % Chunk.CHUNK_SIZE, blockY % Chunk.CHUNK_SIZE).Empty + ); + } + + // COLLISION CHECKS + + public RectangleF? TestBoundingBox(int x, int y, RectangleF? box) { + if (!box.HasValue) return null; + + return new RectangleF( + x, y, + box.Value.Width, box.Value.Height + ); + } + public RectangleF? TestBoundingBox(int x, int y) => TestBoundingBox(x, y, GetForeground(x, y).Type.BoundingBox); + + // CHUNK IN MAP CHECKS + public bool ChunkIsInMap(int chunkX, int chunkY) => !(chunkX < 0 || chunkY < 0 || chunkX >= Width || chunkY >= Height); + public bool ChunkIsInMap(ChunkVector cv) => ChunkIsInMap(cv.X, cv.Y); + + public virtual Vector2 GetSpawnpoint() => Vector2.Zero; + } +} \ No newline at end of file diff --git a/source/game/planets/GeneratedPlanet.cs b/source/game/planets/GeneratedPlanet.cs new file mode 100644 index 0000000..c89a6c8 --- /dev/null +++ b/source/game/planets/GeneratedPlanet.cs @@ -0,0 +1,35 @@ +using System; +using Celesteia.Game.Planets.Generation; +using Microsoft.Xna.Framework; + +namespace Celesteia.Game.Planets { + public class GeneratedPlanet : ChunkMap + { + public int Seed { get; private set; } + + private IChunkProvider _provider; + + public GeneratedPlanet(int w, int h, int? seed = null) : base(w, h) + { + Seed = seed.HasValue ? seed.Value : (int) System.DateTime.Now.Ticks; + } + + public void Generate(IChunkProvider provider, Action progressReport = null) { + Action doProgressReport = (s) => { if (progressReport != null) progressReport(s); }; + + _provider = provider; + doProgressReport("Generating chunks..."); + + ChunkVector _cv; + for (_cv.X = 0; _cv.X < Width; _cv.X++) + for (_cv.Y = 0; _cv.Y < Height; _cv.Y++) + provider.ProvideChunk(Map[_cv.X, _cv.Y] = new Chunk(_cv)); + + provider.GenerateStructures(doProgressReport); + + doProgressReport("Planet generated."); + } + + public override Vector2 GetSpawnpoint() => _provider.GetSpawnpoint(); + } +} \ No newline at end of file diff --git a/source/game/planets/generation/IWorldGenerator.cs b/source/game/planets/generation/IWorldGenerator.cs new file mode 100644 index 0000000..92b528b --- /dev/null +++ b/source/game/planets/generation/IWorldGenerator.cs @@ -0,0 +1,18 @@ +using System; +using Microsoft.Xna.Framework; + +namespace Celesteia.Game.Planets.Generation { + public interface IChunkProvider { + // Provide a chunk's tile map. + public void ProvideChunk(Chunk c); + + // Get the natural block and wall at X and Y. + public byte[] GetNaturalBlocks(int x, int y); + + // Get a safe spot to spawn the player. + public Vector2 GetSpawnpoint(); + + // Generate various structures in the world. + public void GenerateStructures(Action progressReport = null); + } +} \ No newline at end of file diff --git a/source/game/planets/generation/TerranPlanetGenerator.cs b/source/game/planets/generation/TerranPlanetGenerator.cs new file mode 100644 index 0000000..9507011 --- /dev/null +++ b/source/game/planets/generation/TerranPlanetGenerator.cs @@ -0,0 +1,275 @@ +using System; +using Microsoft.Xna.Framework; +using Celesteia.Resources; +using System.Collections.Generic; +using System.Linq; + +namespace Celesteia.Game.Planets.Generation { + public class TerranPlanetGenerator : IChunkProvider { + private ChunkMap _chunkMap; + private int _seed; + private FastNoiseLite _noise; + + public TerranPlanetGenerator(GeneratedPlanet chunkMap) { + _chunkMap = chunkMap; + _noise = new FastNoiseLite(_seed = chunkMap.Seed); + + LoadBlockIndices(); + } + + private byte top; + private byte soil; + private byte stone; + private byte deepstone; + private byte log; + private byte leaves; + private byte planks; + private byte coal_ore; + private byte copper_ore; + private byte iron_ore; + private byte[] foliage; + private byte landing_floor; + private byte cc_base; + private byte cc_frame; + private void LoadBlockIndices() { + top = ResourceManager.Blocks.GetResource(NamespacedKey.Base("grown_soil")).GetID(); + soil = ResourceManager.Blocks.GetResource(NamespacedKey.Base("soil")).GetID(); + stone = ResourceManager.Blocks.GetResource(NamespacedKey.Base("stone")).GetID(); + deepstone = ResourceManager.Blocks.GetResource(NamespacedKey.Base("deepstone")).GetID(); + log = ResourceManager.Blocks.GetResource(NamespacedKey.Base("log")).GetID(); + leaves = ResourceManager.Blocks.GetResource(NamespacedKey.Base("leaves")).GetID(); + planks = ResourceManager.Blocks.GetResource(NamespacedKey.Base("wooden_planks")).GetID(); + coal_ore = ResourceManager.Blocks.GetResource(NamespacedKey.Base("coal_ore")).GetID(); + copper_ore = ResourceManager.Blocks.GetResource(NamespacedKey.Base("copper_ore")).GetID(); + iron_ore = ResourceManager.Blocks.GetResource(NamespacedKey.Base("iron_ore")).GetID(); + + foliage = new byte[5] { + 0, + ResourceManager.Blocks.GetResource(NamespacedKey.Base("grass")).GetID(), + ResourceManager.Blocks.GetResource(NamespacedKey.Base("blue_flower")).GetID(), + ResourceManager.Blocks.GetResource(NamespacedKey.Base("red_flower")).GetID(), + ResourceManager.Blocks.GetResource(NamespacedKey.Base("violet_flower")).GetID() + }; + + landing_floor = ResourceManager.Blocks.GetResource(NamespacedKey.Base("scorched_soil")).GetID(); + cc_base = ResourceManager.Blocks.GetResource(NamespacedKey.Base("crashed_capsule_base")).GetID(); + cc_frame = ResourceManager.Blocks.GetResource(NamespacedKey.Base("crashed_capsule_frame")).GetID(); + } + + public void ProvideChunk(Chunk c) { + byte[] natural; + for (int i = 0; i < Chunk.CHUNK_SIZE; i++) + for (int j = 0; j < Chunk.CHUNK_SIZE; j++) { + natural = GetNaturalBlocks(c.TruePosition.X + i, c.TruePosition.Y + j); + c.SetForeground(i, j, natural[0]); + c.SetBackground(i, j, natural[1]); + } + } + + public byte[] GetNaturalBlocks(int x, int y) => ThirdPass(x, y, SecondPass(x, y, FirstPass(x, y))); + + public void GenerateStructures(Action progressReport) { + Random rand = new Random(_seed); + + progressReport("Planting trees..."); + GenerateTrees(rand); + progressReport("Abandoning houses..."); + GenerateAbandonedHomes(rand); + progressReport("Planting foliage..."); + GenerateFoliage(rand); + progressReport("Landing light..."); + GenerateLanding(); + } + + public Vector2 GetSpawnpoint() + { + float x; + return new Vector2( + x = MathF.Floor(_chunkMap.BlockWidth / 2f) + 0.5f, + (_chunkMap.BlockHeight) - GetHeightValue((int)MathF.Floor(x)) - 2f + ); + } + + public byte[] FirstPass(int x, int y) { + if (y > _chunkMap.BlockHeight - 5) return new byte[2] { deepstone, deepstone }; + + byte[] values = new byte[2] { 0, 0 }; + + int h = GetHeightValue(x); + + if (_chunkMap.BlockHeight - y <= h) { + if (_chunkMap.BlockHeight - y == h) { values[0] = top; values[1] = soil; } + else if (_chunkMap.BlockHeight - y >= h - 3) { values[0] = soil; values[1] = soil; } + else { values[0] = stone; values[1] = stone; } + } + + return values; + } + public byte[] SecondPass(int x, int y, byte[] values) { + float threshold = 0.667f; + + if (values[0] == 0 || values[0] == deepstone) return values; + if (values[0] == soil || values[0] == top) threshold += .2f; + + float c = GetCaveValue(x, y); + + if (c > threshold) values[0] = 0; + + return values; + } + + public byte[] ThirdPass(int x, int y, byte[] values) { + if (values[0] != stone) return values; + + float coalValue = GetOreValue(x, y, 498538f, 985898f); + if (coalValue > 0.95f) values[0] = coal_ore; + else { + float copperValue = GetOreValue(x, y, 3089279f, 579486f); + if (copperValue > 0.95f) values[0] = copper_ore; + + else + { + float ironValue = GetOreValue(x, y, 243984f, 223957f); + if (ironValue > 0.95f) values[0] = iron_ore; + } + } + + return values; + } + + private int defaultOffset => _chunkMap.BlockHeight / 3; + public int GetHeightValue(int x) => (int)Math.Round((_noise.GetNoise(x / 1f, 0f) * 24f) + defaultOffset); + public float GetCaveValue(int x, int y) => _noise.GetNoise(x / 0.6f, y / 0.7f); + public float GetOreValue(int x, int y, float offsetX, float offsetY) => (_noise.GetNoise((x + offsetX) * 5f, (y + offsetY) * 5f) + 1) / 2f; + + private int blocksBetweenTrees = 5; + private int treeGrowthSteps = 15; + public void GenerateTrees(Random rand) { + int j = 0; + int randomNumber = 0; + int lastTree = 0; + for (int i = 0; i < _chunkMap.BlockWidth; i++) { + j = _chunkMap.BlockHeight - GetHeightValue(i); + + if (MathF.Abs(i - GetSpawnpoint().X) < 10f) continue; // Don't grow trees too close to spawn. + if (i < 10 || i > _chunkMap.BlockWidth - 10) continue; // Don't grow trees too close to world borders. + if (_chunkMap.GetForeground(i, j).BlockID != top) continue; // Only grow trees on grass. + if (i - lastTree < blocksBetweenTrees) continue; // Force a certain number of blocks between trees. + + lastTree = i; + + randomNumber = rand.Next(0, 6); + + if (randomNumber == 1) GrowTreeRecursively(i, j - 1, treeGrowthSteps - rand.Next(0, 7), rand, false); + } + } + + public void GrowTreeRecursively(int x, int y, int steps, Random rand, bool branch) { + if (steps == 0) { + for (int i = -2; i <= 2; i++) + for (int j = -2; j <= 2; j++) { + if (_chunkMap.GetForeground(x + i, y + j).Empty) _chunkMap.SetForegroundID(x + i, y + j, leaves); + } + return; + } + + if (!_chunkMap.GetForeground(x, y).Empty) return; + + _chunkMap.SetForegroundID(x, y, log); + + if (!branch) GrowTreeRecursively(x, y - 1, steps - 1, rand, false); // Grow upwards. + if (rand.Next(0, 6) > steps) GrowTreeRecursively(x - 1, y, steps - 1, rand, true); // Grow to the left. + if (rand.Next(0, 6) > steps) GrowTreeRecursively(x + 1, y, steps - 1, rand, true); // Grow to the right. + } + + private int blocksBetweenHomes = 150; + public void GenerateAbandonedHomes(Random rand) { + int j = 0; + int randomNumber = 0; + int lastHome = 0; + for (int i = 0; i < _chunkMap.BlockWidth; i++) { + j = _chunkMap.BlockHeight - GetHeightValue(i); + + if (MathF.Abs(i - GetSpawnpoint().X) < 10f) continue; // Don't grow trees too close to spawn. + if (i < 10 || i > _chunkMap.BlockWidth - 10) continue; // Don't grow trees too close to world borders. + if (i - lastHome < blocksBetweenHomes) continue; // Force a certain number of blocks between trees. + + int homeWidth = rand.Next(10, 15); + int homeHeight = rand.Next(6, 10); + int buryAmount = rand.Next(15, 40); + + j -= homeHeight; // Raise the home to be built on the ground first. + j += buryAmount; // Bury the home by a random amount. + + lastHome = i; + + randomNumber = rand.Next(0, 5); + + if (randomNumber == 1) BuildAbandonedHome(i, j, homeWidth, homeHeight, rand); + } + } + + public void BuildAbandonedHome(int originX, int originY, int homeWidth, int homeHeight, Random rand) { + int maxX = originX + homeWidth; + + for (int i = originX; i < maxX; i++) originY = Math.Max(originY, _chunkMap.BlockHeight - GetHeightValue(i)); + int maxY = originY + homeHeight; + + for (int i = originX; i < maxX; i++) + for (int j = originY; j < maxY; j++) { + _chunkMap.SetBackgroundID(i, j, planks); + _chunkMap.SetForegroundID(i, j, 0); + + // Apply some random decay by skipping tiles at random. + if (rand.Next(0, 5) > 3) { + continue; + } + + if (j == originY || j == maxY - 1) _chunkMap.SetForegroundID(i, j, planks); + if (i == originX || i == maxX - 1) _chunkMap.SetForegroundID(i, j, log); + } + } + + private Dictionary foliageDistribution = new Dictionary { + { 0.3, 0 }, + { 0.6, 1 }, + { 0.7, 2 }, + { 0.85, 4 }, + { 0.99, 3 }, + }; + + public void GenerateFoliage(Random rand) { + int j = 0; + + double randomNumber = 0; + int foliageIndex = 0; + for (int i = 0; i < _chunkMap.BlockWidth; i++) { + j = _chunkMap.BlockHeight - GetHeightValue(i); + + if (_chunkMap.GetForeground(i, j).BlockID != top) continue; // If there is anything but foreground grown soil, continue. + if (!_chunkMap.GetForeground(i, j - 1).Empty) continue; // If the foreground is already taken, continue. + + randomNumber = rand.NextDouble(); + for (int f = 0; f < foliageDistribution.Keys.Count; f++) { + if (randomNumber > foliageDistribution.Keys.ElementAt(f)) foliageIndex = foliageDistribution[foliageDistribution.Keys.ElementAt(f)]; + } + + _chunkMap.SetForegroundID(i, j - 1, foliage[foliageIndex]); + } + } + + public void GenerateLanding() { + int x = GetSpawnpoint().ToPoint().X; + int j = _chunkMap.BlockHeight - GetHeightValue(x); + for (int i = -1; i <= 1; i++) { + _chunkMap.SetForegroundID(x + i, j + 1, soil); + _chunkMap.SetForegroundID(x + i, j, landing_floor); + for (int h = 1; h <= 3; h++) { + _chunkMap.SetForegroundID(x + i, j - h, cc_frame); + _chunkMap.SetBackgroundID(x + i, j - h, 0); + } + } + _chunkMap.SetForegroundID(x, j - 1, cc_base); + } + } +} \ No newline at end of file -- cgit v1.2.3