Beispiel #1
0
        private void OnChunkColumnGen(IServerChunk[] chunks, int chunkX, int chunkZ, ITreeAttribute chunkGenParams = null)
        {
            IntMap climateMap = chunks[0].MapChunk.MapRegion.ClimateMap;
            ushort[] heightMap = chunks[0].MapChunk.RainHeightMap;

            int regionChunkSize = api.WorldManager.RegionSize / chunksize;
            int rlX = chunkX % regionChunkSize;
            int rlZ = chunkZ % regionChunkSize;

            float facC = (float)climateMap.InnerSize / regionChunkSize;
            int climateUpLeft = climateMap.GetUnpaddedInt((int)(rlX * facC), (int)(rlZ * facC));
            int climateUpRight = climateMap.GetUnpaddedInt((int)(rlX * facC + facC), (int)(rlZ * facC));
            int climateBotLeft = climateMap.GetUnpaddedInt((int)(rlX * facC), (int)(rlZ * facC + facC));
            int climateBotRight = climateMap.GetUnpaddedInt((int)(rlX * facC + facC), (int)(rlZ * facC + facC));

            for (int x = 0; x < chunksize; x++)
            {
                for (int z = 0; z < chunksize; z++)
                {
                    int posY = heightMap[z * chunksize + x];

                    int climate = GameMath.BiLerpRgbColor((float)x / chunksize, (float)z / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);

                    float temp = TerraGenConfig.GetScaledAdjustedTemperatureFloat((climate >> 16) & 0xff, posY - TerraGenConfig.seaLevel);

                    int prevY = posY;
                    if (PlaceSnowLayer(x, prevY, z, chunks, temp))
                    {
                        heightMap[z * chunksize + x]++;
                    }
                }
            }
        }
Beispiel #2
0
        void genShrubs(int chunkX, int chunkZ)
        {
            int triesShrubs = (int)treeSupplier.treeGenProps.shrubsPerChunk.nextFloat();

            int   dx, dz, x, z;
            Block block;

            while (triesShrubs > 0)
            {
                triesShrubs--;

                dx = rnd.Next(chunksize);
                dz = rnd.Next(chunksize);
                x  = dx + chunkX * chunksize;
                z  = dz + chunkZ * chunksize;

                int y = heightmap[dz * chunksize + dx];
                if (y <= 0 || y >= worldheight - 15)
                {
                    continue;
                }

                tmpPos.Set(x, y, z);
                block = blockAccessor.GetBlock(tmpPos);
                if (block.Fertility == 0)
                {
                    continue;
                }

                // Place according to forest value
                float treeDensity = GameMath.BiLerp(forestUpLeft, forestUpRight, forestBotLeft, forestBotRight, (float)dx / chunksize, (float)dz / chunksize);
                int   climate     = GameMath.BiLerpRgbColor((float)dx / chunksize, (float)dz / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);
                float shrubChance = GameMath.BiLerp(shrubUpLeft, shrubUpRight, shrubBotLeft, shrubBotRight, (float)dx / chunksize, (float)dz / chunksize);


                if (rnd.NextDouble() > (shrubChance / 255f) * (shrubChance / 255f))
                {
                    continue;
                }
                TreeGenForClimate treegenParams = treeSupplier.GetRandomShrubGenForClimate(climate, (int)shrubChance, y);

                if (treegenParams != null)
                {
                    if (blockAccessor.GetBlock(tmpPos.X, tmpPos.Y, tmpPos.Z).Replaceable >= 6000)
                    {
                        tmpPos.Y--;
                    }

                    treegenParams.treeGen.GrowTree(
                        blockAccessor,
                        tmpPos,
                        treegenParams.size,
                        treegenParams.vinesGrowthChance
                        );
                }
            }
        }
Beispiel #3
0
        public int GetUnpaddedColorLerped(float x, float z)
        {
            int ix = (int)x;
            int iz = (int)z;

            return(GameMath.BiLerpRgbColor(x - ix, z - iz,
                                           Data[(iz + TopLeftPadding) * Size + ix + TopLeftPadding],
                                           Data[(iz + TopLeftPadding) * Size + ix + 1 + TopLeftPadding],
                                           Data[(iz + 1 + TopLeftPadding) * Size + ix + TopLeftPadding],
                                           Data[(iz + 1 + TopLeftPadding) * Size + ix + 1 + TopLeftPadding]
                                           ));
        }
Beispiel #4
0
        private void OnChunkColumnGen(IServerChunk[] chunks, int chunkX, int chunkZ)
        {
            ushort[] heightmap = chunks[0].MapChunk.RainHeightMap;

            IntMap climateMap      = chunks[0].MapChunk.MapRegion.ClimateMap;
            int    regionChunkSize = api.WorldManager.RegionSize / chunksize;
            float  fac             = (float)climateMap.InnerSize / regionChunkSize;
            int    rlX             = chunkX % regionChunkSize;
            int    rlZ             = chunkZ % regionChunkSize;

            climateUpLeft   = climateMap.GetUnpaddedInt((int)(rlX * fac), (int)(rlZ * fac));
            climateUpRight  = climateMap.GetUnpaddedInt((int)(rlX * fac + fac), (int)(rlZ * fac));
            climateBotLeft  = climateMap.GetUnpaddedInt((int)(rlX * fac), (int)(rlZ * fac + fac));
            climateBotRight = climateMap.GetUnpaddedInt((int)(rlX * fac + fac), (int)(rlZ * fac + fac));

            int climateMid = GameMath.BiLerpRgbColor(0.5f, 0.5f, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);

            // 16-23 bits = Red = temperature
            // 8-15 bits = Green = rain
            // 0-7 bits = Blue = humidity

            int rain     = (climateMid >> 8) & 0xff;
            int humidity = climateMid & 0xff;

            // Lake density at chunk center
            float lakeDensity = 4 * (rain + humidity) / 255f;


            float maxTries = lakeDensity * 10;

            int dx, dz;



            while (maxTries-- > 0)
            {
                if (maxTries < 1 && rand.NextDouble() > maxTries)
                {
                    break;
                }

                dx = rand.Next(chunksize);
                dz = rand.Next(chunksize);

                TryPlaceLakeAt(dx, dz, chunkX, chunkZ, heightmap);

                //ushort blockId = blockAccessor.GetBlock(new AssetLocation("creativeblock-37")).BlockId;
                //blockAccessor.SetBlock(blockId, new BlockPos(chunkX * chunksize + dx, heightmap[dz * chunksize + dx] + 1, chunkZ * chunksize + dz));
            }
        }
Beispiel #5
0
        public int GetColorLerpedCorrectly(float x, float z)
        {
            int posXLeft  = (int)Math.Floor(x - 0.5f);
            int posXRight = posXLeft + 1;

            int posZLeft  = (int)Math.Floor(z - 0.5f);
            int posZRight = posZLeft + 1;

            float fx = x - (posXLeft + 0.5f);
            float fz = z - (posZLeft + 0.5f);

            return(GameMath.BiLerpRgbColor(
                       fx, fz,
                       Data[(posZLeft + TopLeftPadding) * Size + posXLeft + TopLeftPadding],
                       Data[(posZLeft + TopLeftPadding) * Size + posXRight + TopLeftPadding],
                       Data[(posZRight + TopLeftPadding) * Size + posXLeft + TopLeftPadding],
                       Data[(posZRight + TopLeftPadding) * Size + posXRight + TopLeftPadding]
                       ));
        }
Beispiel #6
0
        public static Vec3i ToClimateVec(this IntDataMap2D climateMap, int chunkX, int chunkZ, int regionsize, int chunksize, float lx = 0.5f, float ly = 0.5f)
        {
            int   regionChunkSize = regionsize / chunksize;
            float fac             = (float)climateMap.InnerSize / regionChunkSize;
            int   rlX             = chunkX % regionChunkSize;
            int   rlZ             = chunkZ % regionChunkSize;

            int climateUpLeft   = climateMap.GetUnpaddedInt((int)(rlX * fac), (int)(rlZ * fac));
            int climateUpRight  = climateMap.GetUnpaddedInt((int)(rlX * fac + fac), (int)(rlZ * fac));
            int climateBotLeft  = climateMap.GetUnpaddedInt((int)(rlX * fac), (int)(rlZ * fac + fac));
            int climateBotRight = climateMap.GetUnpaddedInt((int)(rlX * fac + fac), (int)(rlZ * fac + fac));

            int climateMid = GameMath.BiLerpRgbColor(lx, ly, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);

            int rain     = (climateMid >> 8) & 0xff;
            int humidity = climateMid & 0xff;
            int temp     = (climateMid >> 16) & 0xff;

            return(new Vec3i(rain, humidity, temp));
        }
Beispiel #7
0
        private void OnChunkColumnGen(IServerChunk[] chunks, int chunkX, int chunkZ, ITreeAttribute chunkGenParams = null)
        {
            IntMap climateMap      = chunks[0].MapChunk.MapRegion.ClimateMap;
            int    regionChunkSize = api.WorldManager.RegionSize / chunksize;
            float  fac             = (float)climateMap.InnerSize / regionChunkSize;
            int    rlX             = chunkX % regionChunkSize;
            int    rlZ             = chunkZ % regionChunkSize;

            int climateUpLeft   = climateMap.GetUnpaddedInt((int)(rlX * fac), (int)(rlZ * fac));
            int climateUpRight  = climateMap.GetUnpaddedInt((int)(rlX * fac + fac), (int)(rlZ * fac));
            int climateBotLeft  = climateMap.GetUnpaddedInt((int)(rlX * fac), (int)(rlZ * fac + fac));
            int climateBotRight = climateMap.GetUnpaddedInt((int)(rlX * fac + fac), (int)(rlZ * fac + fac));

            int climateMid = GameMath.BiLerpRgbColor(0.5f, 0.5f, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);

            // 16-23 bits = Red = temperature
            // 8-15 bits = Green = rain
            // 0-7 bits = Blue = humidity

            int rain     = (climateMid >> 8) & 0xff;
            int humidity = climateMid & 0xff;
            int temp     = (climateMid >> 16) & 0xff;


            int quantityRivulets = (int)(80 * (rain + humidity) / 255f) * (api.WorldManager.MapSizeY / chunksize) - Math.Max(0, 100 - temp);
            int fx, fy, fz;

            while (quantityRivulets-- > 0)
            {
                int dx = 1 + rnd.Next(chunksize - 2);
                int y  = 1 + rnd.Next(api.WorldManager.MapSizeY - 2);
                int dz = 1 + rnd.Next(chunksize - 2);

                int quantitySolid = 0;
                int quantityAir   = 0;
                for (int i = 0; i < BlockFacing.ALLFACES.Length; i++)
                {
                    BlockFacing facing = BlockFacing.ALLFACES[i];
                    fx = dx + facing.Normali.X;
                    fy = y + facing.Normali.Y;
                    fz = dz + facing.Normali.Z;

                    Block block = api.World.Blocks[
                        chunks[fy / chunksize].Blocks[(chunksize * (fy % chunksize) + fz) * chunksize + fx]
                                  ];

                    bool solid = block.BlockMaterial == EnumBlockMaterial.Stone;
                    quantitySolid += solid ? 1 : 0;
                    quantityAir   += (block.BlockMaterial == EnumBlockMaterial.Air) ? 1 : 0;

                    if (!solid && facing == BlockFacing.UP)
                    {
                        quantitySolid = 0;
                    }
                }

                if (quantitySolid != 5 || quantityAir != 1)
                {
                    continue;
                }

                chunks[y / chunksize].Blocks[(chunksize * (y % chunksize) + dz) * chunksize + dx] = y < 24 ? GlobalConfig.lavaBlockId : GlobalConfig.waterBlockId;

                BlockPos pos = new BlockPos(chunkX * chunksize + dx, y, chunkZ * chunksize + dz);
                blockAccessor.ScheduleBlockUpdate(pos);
            }
        }
Beispiel #8
0
        internal bool TryGenerateAtSurface(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos pos)
        {
            int chunksize = blockAccessor.ChunkSize;
            int climate   = GameMath.BiLerpRgbColor((float)(pos.X % chunksize) / chunksize, (float)(pos.Z % chunksize) / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);

            int num    = rand.NextInt(schematicDatas.Length);
            int orient = rand.NextInt(4);
            BlockSchematicStructure schematic = schematicDatas[num][orient];


            int widthHalf  = (int)Math.Ceiling(schematic.SizeX / 2f);
            int lengthHalf = (int)Math.Ceiling(schematic.SizeZ / 2f);



            // Probe all 4 corners + center if they are on the same height

            int centerY = blockAccessor.GetTerrainMapheightAt(pos);

            tmpPos.Set(pos.X - widthHalf, 0, pos.Z - lengthHalf);
            int topLeftY = blockAccessor.GetTerrainMapheightAt(tmpPos);

            tmpPos.Set(pos.X + widthHalf, 0, pos.Z - lengthHalf);
            int topRightY = blockAccessor.GetTerrainMapheightAt(tmpPos);

            tmpPos.Set(pos.X - widthHalf, 0, pos.Z + lengthHalf);
            int botLeftY = blockAccessor.GetTerrainMapheightAt(tmpPos);

            tmpPos.Set(pos.X + widthHalf, 0, pos.Z + lengthHalf);
            int botRightY = blockAccessor.GetTerrainMapheightAt(tmpPos);


            int diff = GameMath.Max(centerY, topLeftY, topRightY, botLeftY, botRightY) - GameMath.Min(centerY, topLeftY, topRightY, botLeftY, botRightY);

            if (diff != 0)
            {
                return(false);
            }

            pos.Y += centerY - pos.Y + 1 + OffsetY;


            // Ensure not floating on water
            tmpPos.Set(pos.X - widthHalf, pos.Y - 1, pos.Z - lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(false);
            }

            tmpPos.Set(pos.X + widthHalf, pos.Y - 1, pos.Z - lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(false);
            }

            tmpPos.Set(pos.X - widthHalf, pos.Y - 1, pos.Z + lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(false);
            }

            tmpPos.Set(pos.X + widthHalf, pos.Y - 1, pos.Z + lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(false);
            }

            // Ensure not submerged in water
            tmpPos.Set(pos.X - widthHalf, pos.Y, pos.Z - lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(false);
            }

            tmpPos.Set(pos.X + widthHalf, pos.Y, pos.Z - lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(false);
            }

            tmpPos.Set(pos.X - widthHalf, pos.Y, pos.Z + lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(false);
            }

            tmpPos.Set(pos.X + widthHalf, pos.Y, pos.Z + lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(false);
            }



            tmpPos.Set(pos.X - widthHalf, pos.Y + 1, pos.Z - lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(false);
            }

            tmpPos.Set(pos.X + widthHalf, pos.Y + 1, pos.Z - lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(false);
            }

            tmpPos.Set(pos.X - widthHalf, pos.Y + 1, pos.Z + lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(false);
            }

            tmpPos.Set(pos.X + widthHalf, pos.Y + 1, pos.Z + lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(false);
            }


            if (!satisfiesMinDistance(pos, worldForCollectibleResolve))
            {
                return(false);
            }
            if (isStructureAt(pos, worldForCollectibleResolve))
            {
                return(false);
            }

            LastPlacedSchematicLocation.Set(pos.X, pos.Y, pos.Z, pos.X + schematic.SizeX, pos.Y + schematic.SizeY, pos.Z + schematic.SizeZ);
            LastPlacedSchematic = schematic;
            schematic.PlaceRespectingBlockLayers(blockAccessor, worldForCollectibleResolve, pos, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight, replaceblockids);
            return(true);
        }
        void genTrees(int chunkX, int chunkZ)
        {
            int   triesTrees = (int)treeSupplier.treeGenProps.treesPerChunk.nextFloat();
            int   dx, dz, x, z;
            Block block;

            while (triesTrees > 0)
            {
                triesTrees--;

                dx = rnd.Next(chunksize);
                dz = rnd.Next(chunksize);
                x  = dx + chunkX * chunksize;
                z  = dz + chunkZ * chunksize;

                int y = heightmap[dz * chunksize + dx];
                if (y <= 0 || y >= worldheight - 15)
                {
                    continue;
                }

                tmpPos.Set(x, y, z);
                block = blockAccessor.GetBlock(tmpPos);
                if (block.Fertility == 0)
                {
                    continue;
                }

                // Place according to forest value
                float treeDensity = GameMath.BiLerp(forestUpLeft, forestUpRight, forestBotLeft, forestBotRight, (float)dx / chunksize, (float)dz / chunksize);
                int   climate     = GameMath.BiLerpRgbColor((float)dx / chunksize, (float)dz / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);
                float shrubChance = GameMath.BiLerp(shrubUpLeft, shrubUpRight, shrubBotLeft, shrubBotRight, (float)dx / chunksize, (float)dz / chunksize);

                float treeDensityNormalized = treeDensity / 255f;


                // 1 in 400 chance to always spawn a tree
                // otherwise go by tree density using a quadratic drop off to create clearer forest edges
                if (rnd.NextDouble() > Math.Max(0.0025, treeDensityNormalized * treeDensityNormalized))
                {
                    continue;
                }
                TreeGenForClimate treegenParams = treeSupplier.GetRandomTreeGenForClimate(climate, (int)treeDensity, y);

                if (treegenParams != null)
                {
                    bool canGen = true;
                    for (int i = 0; i < structuresIntersectingChunk.Count; i++)
                    {
                        if (structuresIntersectingChunk[i].Location.Contains(tmpPos))
                        {
                            canGen = false; break;
                        }
                    }
                    if (!canGen)
                    {
                        continue;
                    }

                    if (blockAccessor.GetBlock(tmpPos.X, tmpPos.Y, tmpPos.Z).Replaceable >= 6000)
                    {
                        tmpPos.Y--;
                    }

                    treegenParams.treeGen.GrowTree(
                        blockAccessor,
                        tmpPos,
                        treegenParams.size,
                        treegenParams.vinesGrowthChance,
                        treeDensityNormalized
                        );
                }
            }
        }
Beispiel #10
0
        private void OnChunkColumnGeneration(IServerChunk[] chunks, int chunkX, int chunkZ)
        {
            IntMap forestMap  = chunks[0].MapChunk.MapRegion.ForestMap;
            IntMap climateMap = chunks[0].MapChunk.MapRegion.ClimateMap;

            ushort[] heightMap = chunks[0].MapChunk.RainHeightMap;

            int regionChunkSize = api.WorldManager.RegionSize / chunksize;
            int rlX             = chunkX % regionChunkSize;
            int rlZ             = chunkZ % regionChunkSize;

            // "Pixels per chunk"
            float facF = (float)forestMap.InnerSize / regionChunkSize;

            // Retrieves the map data on the chunk edges
            int forestUpLeft   = forestMap.GetUnpaddedInt((int)(rlX * facF), (int)(rlZ * facF));
            int forestUpRight  = forestMap.GetUnpaddedInt((int)(rlX * facF + facF), (int)(rlZ * facF));
            int forestBotLeft  = forestMap.GetUnpaddedInt((int)(rlX * facF), (int)(rlZ * facF + facF));
            int forestBotRight = forestMap.GetUnpaddedInt((int)(rlX * facF + facF), (int)(rlZ * facF + facF));

            float facC            = (float)climateMap.InnerSize / regionChunkSize;
            int   climateUpLeft   = climateMap.GetUnpaddedInt((int)(rlX * facC), (int)(rlZ * facC));
            int   climateUpRight  = climateMap.GetUnpaddedInt((int)(rlX * facC + facC), (int)(rlZ * facC));
            int   climateBotLeft  = climateMap.GetUnpaddedInt((int)(rlX * facC), (int)(rlZ * facC + facC));
            int   climateBotRight = climateMap.GetUnpaddedInt((int)(rlX * facC + facC), (int)(rlZ * facC + facC));



            for (int x = 0; x < chunksize; x++)
            {
                for (int z = 0; z < chunksize; z++)
                {
                    int posY = heightMap[z * chunksize + x];

                    int   climate      = GameMath.BiLerpRgbColor((float)x / chunksize, (float)z / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);
                    int   tempUnscaled = (climate >> 16) & 0xff;
                    float temp         = TerraGenConfig.GetScaledAdjustedTemperatureFloat(tempUnscaled, posY - TerraGenConfig.seaLevel);
                    float rainRel      = TerraGenConfig.GetRainFall((climate >> 8) & 0xff, posY) / 255f;
                    float forestRel    = GameMath.BiLerp(forestUpLeft, forestUpRight, forestBotLeft, forestBotRight, (float)x / chunksize, (float)z / chunksize) / 255f;

                    int prevY = posY;

                    posY = PutLayers(x, posY, z, chunks, rainRel, temp, tempUnscaled, heightMap);
                    PlaceTallGrass(x, prevY, z, chunks, rainRel, temp, forestRel);


                    // Try again to put layers if above sealevel and we found over 10 air blocks
                    int foundAir = 0;
                    while (posY >= TerraGenConfig.seaLevel - 1)
                    {
                        int    chunkY  = posY / chunksize;
                        int    lY      = posY % chunksize;
                        int    index3d = (chunksize * lY + z) * chunksize + x;
                        ushort blockId = chunks[chunkY].Blocks[index3d];

                        if (blockId == 0)
                        {
                            foundAir++;
                        }
                        else
                        {
                            if (foundAir >= 8)
                            {
                                temp    = TerraGenConfig.GetScaledAdjustedTemperatureFloat(tempUnscaled, posY - TerraGenConfig.seaLevel);
                                rainRel = TerraGenConfig.GetRainFall((climate >> 8) & 0xff, posY) / 255f;

                                PutLayers(x, posY, z, chunks, rainRel, temp, tempUnscaled, null);
                                break;
                            }
                            else
                            {
                                foundAir = 0;
                            }
                        }

                        posY--;
                    }
                }
            }
        }
Beispiel #11
0
        void genPatches(int chunkX, int chunkZ)
        {
            int   dx, dz, x, z;
            Block block;

            for (int i = 0; i < bpc.Patches.Length; i++)
            {
                BlockPatch blockPatch = bpc.Patches[i];
                if (!blockPatch.PrePass)
                {
                    continue;
                }

                float chance = blockPatch.Chance * bpc.ChanceMultiplier.nextFloat();

                while (chance-- > rnd.NextDouble())
                {
                    dx = rnd.Next(chunksize);
                    dz = rnd.Next(chunksize);
                    x  = dx + chunkX * chunksize;
                    z  = dz + chunkZ * chunksize;

                    int y = heightmap[dz * chunksize + dx];
                    if (y <= 0 || y >= worldheight - 15)
                    {
                        continue;
                    }

                    tmpPos.Set(x, y, z);
                    block = blockAccessor.GetBlock(tmpPos);

                    // Place according to forest value
                    int climate = GameMath.BiLerpRgbColor((float)dx / chunksize, (float)dz / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);

                    if (bpc.IsPatchSuitableAt(blockPatch, block, api.WorldManager, climate, y, 0))
                    {
                        int  firstBlockId = 0;
                        bool found        = true;

                        if (blockPatch.BlocksByRockType != null)
                        {
                            found = false;
                            int dy = 1;
                            while (dy < 5 && y - dy > 0)
                            {
                                string lastCodePart = blockAccessor.GetBlock(x, y - dy, z).LastCodePart();
                                if (RockBlockIdsByType.TryGetValue(lastCodePart, out firstBlockId))
                                {
                                    found = true; break;
                                }
                                dy++;
                            }
                        }

                        if (found)
                        {
                            blockPatch.Generate(blockAccessor, rnd, x, y, z, firstBlockId);
                        }
                    }
                }
            }
        }
Beispiel #12
0
        private void OnChunkColumnGen(IServerChunk[] chunks, int chunkX, int chunkZ, ITreeAttribute chunkGenParams = null)
        {
            rand.InitPositionSeed(chunkX, chunkZ);

            ushort[] heightmap = chunks[0].MapChunk.RainHeightMap;

            IntDataMap2D climateMap      = chunks[0].MapChunk.MapRegion.ClimateMap;
            int          regionChunkSize = api.WorldManager.RegionSize / chunksize;
            float        fac             = (float)climateMap.InnerSize / regionChunkSize;
            int          rlX             = chunkX % regionChunkSize;
            int          rlZ             = chunkZ % regionChunkSize;

            climateUpLeft   = climateMap.GetUnpaddedInt((int)(rlX * fac), (int)(rlZ * fac));
            climateUpRight  = climateMap.GetUnpaddedInt((int)(rlX * fac + fac), (int)(rlZ * fac));
            climateBotLeft  = climateMap.GetUnpaddedInt((int)(rlX * fac), (int)(rlZ * fac + fac));
            climateBotRight = climateMap.GetUnpaddedInt((int)(rlX * fac + fac), (int)(rlZ * fac + fac));

            int climateMid = GameMath.BiLerpRgbColor(0.5f, 0.5f, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);

            // 16-23 bits = Red = temperature
            // 8-15 bits = Green = rain
            // 0-7 bits = Blue = humidity

            int rain     = (climateMid >> 8) & 0xff;
            int humidity = climateMid & 0xff;
            int temp     = (climateMid >> 16) & 0xff;

            // Lake density at chunk center
            float pondDensity = 4 * (rain + humidity) / 255f;

            float sealeveltemp = TerraGenConfig.GetScaledAdjustedTemperatureFloat(temp, 0);

            // Less lakes where its below -5 degrees
            pondDensity -= Math.Max(0, 5 - sealeveltemp);

            float maxTries = pondDensity * 10;

            int dx, dz;

            int baseX = chunkX * chunksize;
            int baseZ = chunkZ * chunksize;


            while (maxTries-- > 0)
            {
                if (maxTries < 1 && rand.NextDouble() > maxTries)
                {
                    break;
                }

                dx = rand.NextInt(chunksize);
                dz = rand.NextInt(chunksize);

                pondYPos = heightmap[dz * chunksize + dx] + 1;
                if (pondYPos <= 0 || pondYPos >= mapheight - 1)
                {
                    return;
                }

                TryPlacePondAt(dx, pondYPos, dz, chunkX, chunkZ);
            }

            maxTries = 600;
            while (maxTries-- > 0)
            {
                if (maxTries < 1 && rand.NextDouble() > maxTries)
                {
                    break;
                }

                dx = rand.NextInt(chunksize);
                dz = rand.NextInt(chunksize);

                pondYPos = (int)(rand.NextDouble() * heightmap[dz * chunksize + dx]);
                if (pondYPos <= 0 || pondYPos >= mapheight - 1)
                {
                    return;
                }

                int chunkY  = pondYPos / chunksize;
                int dy      = pondYPos % chunksize;
                int blockID = chunks[chunkY].Blocks[(dy * chunksize + dz) * chunksize + dx];

                while (blockID == 0 && pondYPos > 20)
                {
                    pondYPos--;

                    chunkY  = pondYPos / chunksize;
                    dy      = pondYPos % chunksize;
                    blockID = chunks[chunkY].Blocks[(dy * chunksize + dz) * chunksize + dx];

                    if (blockID != 0)
                    {
                        //blockAccessor.SetBlock(63, new BlockPos(dx + baseX, pondYPos, dz + baseZ));
                        TryPlacePondAt(dx, pondYPos, dz, chunkX, chunkZ);
                    }
                }
            }
        }
        /// <summary>
        /// For placement of ruins during worldgen, replaces the topsoil with the area specific soil (e.g. sand)
        /// </summary>
        /// <param name="blockAccessor"></param>
        /// <param name="blocks"></param>
        /// <param name="startPos"></param>
        /// <param name="climateUpLeft"></param>
        /// <param name="climateUpRight"></param>
        /// <param name="climateBotLeft"></param>
        /// <param name="climateBotRight"></param>
        /// <param name="replaceblockids"></param>
        /// <returns></returns>
        public int PlaceRespectingBlockLayers(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos startPos, int climateUpLeft, int climateUpRight, int climateBotLeft, int climateBotRight, int[] replaceblockids, bool replaceMetaBlocks = true)
        {
            BlockPos curPos    = new BlockPos();
            int      placed    = 0;
            int      chunksize = blockAccessor.ChunkSize;


            for (int x = 0; x < SizeX; x++)
            {
                for (int z = 0; z < SizeZ; z++)
                {
                    curPos.Set(x + startPos.X, startPos.Y, z + startPos.Z);
                    IMapChunk mapchunk    = blockAccessor.GetMapChunkAtBlockPos(curPos);
                    int       rockblockid = mapchunk.TopRockIdMap[(curPos.Z % chunksize) * chunksize + curPos.X % chunksize];
                    int       depth       = 0;

                    int maxY = -1;

                    for (int y = SizeY - 1; y >= 0; y--)
                    {
                        curPos.Set(x + startPos.X, y + startPos.Y, z + startPos.Z);
                        Block newBlock = blocksByPos[x, y, z];
                        if (newBlock == null)
                        {
                            continue;
                        }

                        if (replaceMetaBlocks && newBlock == undergroundBlock)
                        {
                            continue;
                        }


                        if (newBlock.Replaceable < 1000)
                        {
                            if (replaceblockids.Length > depth && newBlock.BlockId == replaceblockids[depth])
                            {
                                int climate = GameMath.BiLerpRgbColor((float)x / chunksize, (float)z / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);

                                newBlock = GetBlockLayerBlock((climate >> 8) & 0xff, (climate >> 16) & 0xff, startPos.Y, rockblockid, depth, newBlock, worldForCollectibleResolve.Blocks);
                            }
                            depth++;
                        }

                        Block oldBlock = blockAccessor.GetBlock(curPos);
                        int   p        = handler(blockAccessor, curPos, oldBlock, newBlock);
                        placed += p;

                        if (p > 0 && !newBlock.RainPermeable)
                        {
                            if (newBlock == fillerBlock || newBlock == pathwayBlock)
                            {
                                int lx = curPos.X % chunksize;
                                int lz = curPos.Z % chunksize;
                                if (mapchunk.RainHeightMap[lz * chunksize + lx] == curPos.Y)
                                {
                                    mapchunk.RainHeightMap[lz * chunksize + lx]--;
                                }
                            }
                            else
                            {
                                maxY = Math.Max(curPos.Y, maxY);
                            }
                        }


                        byte[] lightHsv = newBlock.GetLightHsv(blockAccessor, curPos);

                        if (lightHsv[2] > 0 && blockAccessor is IWorldGenBlockAccessor)
                        {
                            ((IWorldGenBlockAccessor)blockAccessor).ScheduleBlockLightUpdate(curPos.Copy(), oldBlock.BlockId, newBlock.BlockId);
                        }
                    }

                    // In the post pass the rain map does not update, so let's set it ourselves
                    if (maxY >= 0)
                    {
                        int lx = curPos.X % chunksize;
                        int lz = curPos.Z % chunksize;
                        int y  = mapchunk.RainHeightMap[lz * chunksize + lx];
                        mapchunk.RainHeightMap[lz * chunksize + lx] = (ushort)Math.Max(y, maxY);
                    }
                }
            }

            PlaceEntitiesAndBlockEntities(blockAccessor, worldForCollectibleResolve, startPos);

            return(placed);
        }
Beispiel #14
0
        public void TryPlacePondAt(int dx, int pondYPos, int dz, int chunkX, int chunkZ, int depth = 0)
        {
            searchPositionsDeltas.Clear();
            pondPositions.Clear();

            // Clear Array
            for (int i = 0; i < didCheckPosition.Length; i++)
            {
                didCheckPosition[i] = false;
            }


            int   basePosX = chunkX * chunksize;
            int   basePosZ = chunkZ * chunksize;
            Vec2i tmp      = new Vec2i();


            searchPositionsDeltas.Enqueue(new Vec2i(dx, dz));
            pondPositions.Enqueue(new Vec2i(basePosX + dx, basePosZ + dz));
            didCheckPosition[(dz + mapOffset) * searchSize + dx + mapOffset] = true;


            while (searchPositionsDeltas.Count > 0)
            {
                Vec2i p = searchPositionsDeltas.Dequeue();

                foreach (BlockFacing facing in BlockFacing.HORIZONTALS)
                {
                    ndx = p.X + facing.Normali.X;
                    ndz = p.Y + facing.Normali.Z;

                    tmp.Set(chunkX * chunksize + ndx, chunkZ * chunksize + ndz);

                    Block belowBlock = blockAccessor.GetBlock(tmp.X, pondYPos - 1, tmp.Y);

                    bool inBoundary = ndx > minBoundary && ndz > minBoundary && ndx < maxBoundary && ndz < maxBoundary;

                    // Only continue when within our 3x3 chunk search area and having a more or less solid block below (or water)
                    if (inBoundary && (belowBlock.Replaceable < 6000 || belowBlock.BlockId == GlobalConfig.waterBlockId))
                    {
                        int arrayIndex = (ndz + mapOffset) * searchSize + ndx + mapOffset;

                        // Already checked or did we reach a pond border?
                        if (!didCheckPosition[arrayIndex] && blockAccessor.GetBlock(tmp.X, pondYPos, tmp.Y).Replaceable >= 6000)
                        {
                            searchPositionsDeltas.Enqueue(new Vec2i(ndx, ndz));
                            pondPositions.Enqueue(tmp.Copy());

                            didCheckPosition[arrayIndex] = true;
                        }
                    }
                    else
                    {
                        pondPositions.Clear();
                        searchPositionsDeltas.Clear();
                        return;
                    }
                }
            }

            if (pondPositions.Count == 0)
            {
                return;
            }


            int          curChunkX, curChunkZ;
            int          prevChunkX = -1, prevChunkZ = -1;
            int          regionChunkSize    = api.WorldManager.RegionSize / chunksize;
            IMapChunk    mapchunk           = null;
            IServerChunk chunk              = null;
            IServerChunk chunkOneBlockBelow = null;

            int ly = GameMath.Mod(pondYPos, chunksize);

            bool extraPondDepth = rand.NextDouble() > 0.5;
            bool withSeabed     = extraPondDepth || pondPositions.Count > 16;

            foreach (Vec2i p in pondPositions)
            {
                curChunkX = p.X / chunksize;
                curChunkZ = p.Y / chunksize;

                int lx = GameMath.Mod(p.X, chunksize);
                int lz = GameMath.Mod(p.Y, chunksize);

                // Get correct chunk and correct climate data if we don't have it already
                if (curChunkX != prevChunkX || curChunkZ != prevChunkZ)
                {
                    chunk = (IServerChunk)blockAccessor.GetChunk(curChunkX, pondYPos / chunksize, curChunkZ);
                    if (chunk == null)
                    {
                        chunk = api.WorldManager.GetChunk(curChunkX, pondYPos / chunksize, curChunkZ);
                    }
                    chunk.Unpack();

                    if (ly == 0)
                    {
                        chunkOneBlockBelow = ((IServerChunk)blockAccessor.GetChunk(curChunkX, (pondYPos - 1) / chunksize, curChunkZ));
                        if (chunkOneBlockBelow == null)
                        {
                            return;
                        }
                        chunkOneBlockBelow.Unpack();
                    }
                    else
                    {
                        chunkOneBlockBelow = chunk;
                    }

                    mapchunk = chunk.MapChunk;
                    IntDataMap2D climateMap = mapchunk.MapRegion.ClimateMap;

                    float fac = (float)climateMap.InnerSize / regionChunkSize;
                    int   rlX = curChunkX % regionChunkSize;
                    int   rlZ = curChunkZ % regionChunkSize;

                    climateUpLeft   = climateMap.GetUnpaddedInt((int)(rlX * fac), (int)(rlZ * fac));
                    climateUpRight  = climateMap.GetUnpaddedInt((int)(rlX * fac + fac), (int)(rlZ * fac));
                    climateBotLeft  = climateMap.GetUnpaddedInt((int)(rlX * fac), (int)(rlZ * fac + fac));
                    climateBotRight = climateMap.GetUnpaddedInt((int)(rlX * fac + fac), (int)(rlZ * fac + fac));

                    prevChunkX = curChunkX;
                    prevChunkZ = curChunkZ;

                    chunkOneBlockBelow.MarkModified();
                    chunk.MarkModified();
                }


                // Raise heightmap by 1
                mapchunk.RainHeightMap[lz * chunksize + lx] = Math.Max(mapchunk.RainHeightMap[lz * chunksize + lx], (ushort)pondYPos);

                // Identify correct climate at this position
                int   climate = GameMath.BiLerpRgbColor((float)lx / chunksize, (float)lz / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);
                float temp    = TerraGenConfig.GetScaledAdjustedTemperatureFloat((climate >> 16) & 0xff, pondYPos - TerraGenConfig.seaLevel);


                // 1. Place water or ice block
                chunk.Blocks[(ly * chunksize + lz) * chunksize + lx] = temp < -5 ? GlobalConfig.lakeIceBlockId : GlobalConfig.waterBlockId;


                // 2. Let's make a nice muddy gravely sea bed
                if (!withSeabed)
                {
                    continue;
                }

                // Need to check the block below first
                int index = ly == 0 ?
                            ((31 * chunksize + lz) * chunksize + lx) :
                            (((ly - 1) * chunksize + lz) * chunksize + lx)
                ;

                Block belowBlock = api.World.Blocks[chunkOneBlockBelow.Blocks[index]];

                // Water below? Seabed already placed
                if (belowBlock.IsLiquid())
                {
                    continue;
                }

                float rainRel     = TerraGenConfig.GetRainFall((climate >> 8) & 0xff, pondYPos) / 255f;
                int   rockBlockId = mapchunk.TopRockIdMap[lz * chunksize + lx];
                if (rockBlockId == 0)
                {
                    continue;
                }

                for (int i = 0; i < lakebedLayerConfig.BlockCodeByMin.Length; i++)
                {
                    if (lakebedLayerConfig.BlockCodeByMin[i].Suitable(temp, rainRel, (float)pondYPos / mapheight, rand))
                    {
                        chunkOneBlockBelow.Blocks[index] = lakebedLayerConfig.BlockCodeByMin[i].GetBlockForMotherRock(rockBlockId);
                        break;
                    }
                }
            }


            if (pondPositions.Count > 0 && extraPondDepth)
            {
                TryPlacePondAt(dx, pondYPos + 1, dz, chunkX, chunkZ, depth + 1);
            }
        }
        protected BlockSchematicStructure GetGeneratableStructure(VillageSchematic schem, IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos pos)
        {
            int chunksize = blockAccessor.ChunkSize;
            int climate   = GameMath.BiLerpRgbColor((float)(pos.X % chunksize) / chunksize, (float)(pos.Z % chunksize) / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);


            int num = rand.NextInt(schem.Structures.Length);
            BlockSchematicStructure schematic = schem.Structures[num];

            int widthHalf  = (int)Math.Ceiling(schematic.SizeX / 2f);
            int lengthHalf = (int)Math.Ceiling(schematic.SizeZ / 2f);

            pos.Y += schem.OffsetY;

            // Probe all 4 corners + center if they are on the same height

            int centerY = blockAccessor.GetTerrainMapheightAt(pos);

            tmpPos.Set(pos.X - widthHalf, 0, pos.Z - lengthHalf);
            int topLeftY = blockAccessor.GetTerrainMapheightAt(tmpPos);

            tmpPos.Set(pos.X + widthHalf, 0, pos.Z - lengthHalf);
            int topRightY = blockAccessor.GetTerrainMapheightAt(tmpPos);

            tmpPos.Set(pos.X - widthHalf, 0, pos.Z + lengthHalf);
            int botLeftY = blockAccessor.GetTerrainMapheightAt(tmpPos);

            tmpPos.Set(pos.X + widthHalf, 0, pos.Z + lengthHalf);
            int botRightY = blockAccessor.GetTerrainMapheightAt(tmpPos);


            int diff = GameMath.Max(centerY, topLeftY, topRightY, botLeftY, botRightY) - GameMath.Min(centerY, topLeftY, topRightY, botLeftY, botRightY);

            if (diff > 2)
            {
                return(null);
            }

            pos.Y += centerY - pos.Y + 1 + schem.OffsetY;
            if (pos.Y <= 0)
            {
                return(null);
            }

            // Ensure not floating on water
            tmpPos.Set(pos.X - widthHalf, pos.Y - 1, pos.Z - lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(null);
            }

            tmpPos.Set(pos.X + widthHalf, pos.Y - 1, pos.Z - lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(null);
            }

            tmpPos.Set(pos.X - widthHalf, pos.Y - 1, pos.Z + lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(null);
            }

            tmpPos.Set(pos.X + widthHalf, pos.Y - 1, pos.Z + lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(null);
            }

            // Ensure not submerged in water
            tmpPos.Set(pos.X - widthHalf, pos.Y, pos.Z - lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(null);
            }

            tmpPos.Set(pos.X + widthHalf, pos.Y, pos.Z - lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(null);
            }

            tmpPos.Set(pos.X - widthHalf, pos.Y, pos.Z + lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(null);
            }

            tmpPos.Set(pos.X + widthHalf, pos.Y, pos.Z + lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(null);
            }



            tmpPos.Set(pos.X - widthHalf, pos.Y + 1, pos.Z - lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(null);
            }

            tmpPos.Set(pos.X + widthHalf, pos.Y + 1, pos.Z - lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(null);
            }

            tmpPos.Set(pos.X - widthHalf, pos.Y + 1, pos.Z + lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(null);
            }

            tmpPos.Set(pos.X + widthHalf, pos.Y + 1, pos.Z + lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(null);
            }

            if (!TestUndergroundCheckPositions(blockAccessor, pos, schematic.UndergroundCheckPositions))
            {
                return(null);
            }

            if (isStructureAt(pos, worldForCollectibleResolve))
            {
                return(null);
            }

            return(schematic);
        }
Beispiel #16
0
        /// <summary>
        /// For placement of ruins during worldgen, replaces the topsoil with the area specific soil (e.g. sand)
        /// </summary>
        /// <param name="blockAccessor"></param>
        /// <param name="blocks"></param>
        /// <param name="startPos"></param>
        /// <param name="climateUpLeft"></param>
        /// <param name="climateUpRight"></param>
        /// <param name="climateBotLeft"></param>
        /// <param name="climateBotRight"></param>
        /// <param name="replaceblockids"></param>
        /// <returns></returns>
        public int PlaceRespectingBlockLayers(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos startPos, int climateUpLeft, int climateUpRight, int climateBotLeft, int climateBotRight, ushort[] replaceblockids)
        {
            BlockPos curPos    = new BlockPos();
            int      placed    = 0;
            int      chunksize = blockAccessor.ChunkSize;


            for (int x = 0; x < SizeX; x++)
            {
                for (int z = 0; z < SizeZ; z++)
                {
                    curPos.Set(x + startPos.X, startPos.Y, z + startPos.Z);
                    ushort rockblockid = blockAccessor.GetMapChunkAtBlockPos(curPos).TopRockIdMap[(curPos.Z % chunksize) * chunksize + curPos.X % chunksize];
                    int    depth       = 0;

                    for (int y = SizeY - 1; y >= 0; y--)
                    {
                        curPos.Set(x + startPos.X, y + startPos.Y, z + startPos.Z);
                        Block newBlock = blocksByPos[x, y, z];
                        if (newBlock == null)
                        {
                            continue;
                        }

                        if (newBlock.Replaceable < 1000)
                        {
                            if (replaceblockids.Length > depth && newBlock.BlockId == replaceblockids[depth])
                            {
                                int climate = GameMath.BiLerpRgbColor((float)x / chunksize, (float)z / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);

                                newBlock = GetBlockLayerBlock((climate >> 8) & 0xff, (climate >> 16) & 0xff, startPos.Y, rockblockid, depth, newBlock, worldForCollectibleResolve.Blocks);
                            }
                            depth++;
                        }

                        Block oldBlock = blockAccessor.GetBlock(curPos);
                        placed += handler(blockAccessor, curPos, oldBlock, newBlock);

                        byte[] lightHsv = newBlock.GetLightHsv(blockAccessor, curPos);

                        if (lightHsv[2] > 0 && blockAccessor is IWorldGenBlockAccessor)
                        {
                            int chunkSize = blockAccessor.ChunkSize;
                            ((IWorldGenBlockAccessor)blockAccessor).ScheduleBlockLightUpdate(curPos.Copy(), oldBlock.BlockId, newBlock.BlockId);
                        }
                    }
                }
            }

            foreach (var val in BlockEntities)
            {
                uint index = val.Key;
                int  dx    = (int)(index & 0x1ff);
                int  dy    = (int)((index >> 20) & 0x1ff);
                int  dz    = (int)((index >> 10) & 0x1ff);

                curPos.Set(startPos.X + dx, startPos.Y + dy, startPos.Z + dz);

                BlockEntity be = blockAccessor.GetBlockEntity(curPos);
                if (be != null)
                {
                    be.FromTreeAtributes(DecodeBlockEntityData(val.Value), worldForCollectibleResolve);
                    be.OnLoadCollectibleMappings(worldForCollectibleResolve, BlockCodes, ItemCodes);
                    be.pos = curPos.Copy();
                }
            }

            return(placed);
        }
Beispiel #17
0
        private void TrySpawnGroupAt(BlockPos origin, Vec3d posAsVec, EntityProperties entityType, EntityProperties[] grouptypes)
        {
            BlockPos pos = origin.Copy();

            float xRel = (float)(posAsVec.X % chunksize) / chunksize;
            float zRel = (float)(posAsVec.Z % chunksize) / chunksize;

            int   climate       = GameMath.BiLerpRgbColor(xRel, zRel, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);
            float temp          = TerraGenConfig.GetScaledAdjustedTemperatureFloat((climate >> 16) & 0xff, (int)posAsVec.Y - TerraGenConfig.seaLevel);
            float rain          = ((climate >> 8) & 0xff) / 255f;
            float forestDensity = GameMath.BiLerp(forestUpLeft, forestUpRight, forestBotLeft, forestBotRight, xRel, zRel);
            float shrubDensity  = GameMath.BiLerp(shrubsUpLeft, shrubsUpRight, shrubsBotLeft, shrubsBotRight, xRel, zRel);

            int spawned = 0;

            WorldGenSpawnConditions sc = entityType.Server.SpawnConditions.Worldgen;
            bool hasCompanions         = sc.Companions != null && sc.Companions.Length > 0;

            spawnPositions.Clear();

            int nextGroupSize = 0;
            int tries         = 10;

            while (nextGroupSize <= 0 && tries-- > 0)
            {
                float val = sc.GroupSize.nextFloat();
                nextGroupSize = (int)val + ((val - (int)val) > rnd.NextDouble() ? 1 : 0);
            }


            for (int i = 0; i < nextGroupSize * 4 + 5; i++)
            {
                if (spawned >= nextGroupSize)
                {
                    break;
                }

                EntityProperties typeToSpawn = entityType;

                // First entity 80% chance to spawn the dominant creature, every subsequent only 20% chance for males (or even lower if more than 5 companion types)
                double dominantChance = i == 0 ? 0.8 : Math.Min(0.2, 1f / grouptypes.Length);

                if (grouptypes.Length > 1 && rnd.NextDouble() > dominantChance)
                {
                    typeToSpawn = grouptypes[1 + rnd.Next(grouptypes.Length - 1)];
                }

                posAsVec.Set(pos.X + 0.5, pos.Y + 0.005, pos.Z + 0.5);

                IBlockAccessor blockAccesssor = wgenBlockAccessor.GetChunkAtBlockPos(pos.X, pos.Y, pos.Z) == null ? api.World.BlockAccessor : wgenBlockAccessor;

                IMapChunk mapchunk = blockAccesssor.GetMapChunkAtBlockPos(pos);
                if (mapchunk != null)
                {
                    ushort[] heightMap = mapchunk.WorldGenTerrainHeightMap;

                    pos.Y =
                        sc.TryOnlySurface ?
                        heightMap[((int)pos.Z % chunksize) * chunksize + ((int)pos.X % chunksize)] + 1 :
                        pos.Y
                    ;

                    xRel = (float)(posAsVec.X % chunksize) / chunksize;
                    zRel = (float)(posAsVec.Z % chunksize) / chunksize;

                    climate       = GameMath.BiLerpRgbColor(xRel, zRel, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);
                    temp          = TerraGenConfig.GetScaledAdjustedTemperatureFloat((climate >> 16) & 0xff, (int)posAsVec.Y - TerraGenConfig.seaLevel);
                    rain          = ((climate >> 8) & 0xff) / 255f;
                    forestDensity = GameMath.BiLerp(forestUpLeft, forestUpRight, forestBotLeft, forestBotRight, xRel, zRel) / 255f;
                    shrubDensity  = GameMath.BiLerp(shrubsUpLeft, shrubsUpRight, shrubsBotLeft, shrubsBotRight, xRel, zRel) / 255f;


                    if (CanSpawnAt(blockAccesssor, typeToSpawn, pos, posAsVec, sc, rain, temp, forestDensity, shrubDensity))
                    {
                        spawnPositions.Add(new SpawnOppurtunity()
                        {
                            ForType = typeToSpawn, Pos = posAsVec.Clone()
                        });
                        spawned++;
                    }
                }

                pos.X = origin.X + ((rnd.Next(11) - 5) + (rnd.Next(11) - 5)) / 2;
                pos.Z = origin.Z + ((rnd.Next(11) - 5) + (rnd.Next(11) - 5)) / 2;
            }


            // Only spawn if the group reached the minimum group size
            if (spawnPositions.Count >= nextGroupSize)
            {
                long herdId = api.WorldManager.GetNextHerdId();

                foreach (SpawnOppurtunity so in spawnPositions)
                {
                    Entity ent = CreateEntity(so.ForType, so.Pos);
                    if (ent is EntityAgent)
                    {
                        (ent as EntityAgent).HerdId = herdId;
                    }

                    if (wgenBlockAccessor.GetChunkAtBlockPos(pos.X, pos.Y, pos.Z) == null)
                    {
                        api.World.SpawnEntity(ent);
                    }
                    else
                    {
                        wgenBlockAccessor.AddEntity(ent);
                    }
                }

                //Console.WriteLine("Spawn a group of {0}x{1} at {2}", spawnPositions.Count, entityType.Code, origin);
            }
        }
        void genPatches(int chunkX, int chunkZ, bool postPass)
        {
            int   dx, dz, x, z;
            Block block;
            int   mapsizeY = blockAccessor.MapSizeY;

            var mapregion = sapi?.WorldManager.GetMapRegion((chunkX * chunksize) / regionSize, (chunkZ * chunksize) / regionSize);


            for (int i = 0; i < bpc.PatchesNonTree.Length; i++)
            {
                BlockPatch blockPatch = bpc.PatchesNonTree[i];
                if (blockPatch.PostPass != postPass)
                {
                    continue;
                }

                float chance = blockPatch.Chance * bpc.ChanceMultiplier.nextFloat();

                while (chance-- > rnd.NextDouble())
                {
                    dx = rnd.NextInt(chunksize);
                    dz = rnd.NextInt(chunksize);
                    x  = dx + chunkX * chunksize;
                    z  = dz + chunkZ * chunksize;

                    int y = heightmap[dz * chunksize + dx];
                    if (y <= 0 || y >= worldheight - 15)
                    {
                        continue;
                    }

                    tmpPos.Set(x, y, z);
                    block = blockAccessor.GetBlock(tmpPos);

                    // Place according to forest value
                    float forestRel = GameMath.BiLerp(forestUpLeft, forestUpRight, forestBotLeft, forestBotRight, (float)dx / chunksize, (float)dz / chunksize) / 255f;
                    forestRel = GameMath.Clamp(forestRel + forestMod, 0, 1);

                    float shrubRel = GameMath.BiLerp(shrubUpLeft, shrubUpRight, shrubBotLeft, shrubBotRight, (float)dx / chunksize, (float)dz / chunksize) / 255f;
                    shrubRel = GameMath.Clamp(shrubRel + shrubMod, 0, 1);

                    int climate = GameMath.BiLerpRgbColor((float)dx / chunksize, (float)dz / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);

                    if (bpc.IsPatchSuitableAt(blockPatch, block, mapsizeY, climate, y, forestRel, shrubRel))
                    {
                        if (blockPatch.MapCode != null && rnd.NextInt(255) > GetPatchDensity(blockPatch.MapCode, x, z, mapregion))
                        {
                            continue;
                        }

                        int  firstBlockId = 0;
                        bool found        = true;

                        if (blockPatch.BlocksByRockType != null)
                        {
                            found = false;
                            int dy = 1;
                            while (dy < 5 && y - dy > 0)
                            {
                                string lastCodePart = blockAccessor.GetBlock(x, y - dy, z).LastCodePart();
                                if (RockBlockIdsByType.TryGetValue(lastCodePart, out firstBlockId))
                                {
                                    found = true; break;
                                }
                                dy++;
                            }
                        }

                        if (found)
                        {
                            blockPatch.Generate(blockAccessor, rnd, x, y, z, firstBlockId);
                        }
                    }
                }
            }
        }
        private void OnChunkColumnGen(IServerChunk[] chunks, int chunkX, int chunkZ, ITreeAttribute chunkGenParams = null)
        {
            landforms = NoiseLandforms.landforms;
            IMapChunk mapchunk = chunks[0].MapChunk;

            int climateUpLeft;
            int climateUpRight;
            int climateBotLeft;
            int climateBotRight;

            IntMap climateMap      = chunks[0].MapChunk.MapRegion.ClimateMap;
            int    regionChunkSize = api.WorldManager.RegionSize / chunksize;
            float  fac             = (float)climateMap.InnerSize / regionChunkSize;
            int    rlX             = chunkX % regionChunkSize;
            int    rlZ             = chunkZ % regionChunkSize;

            climateUpLeft   = climateMap.GetUnpaddedInt((int)(rlX * fac), (int)(rlZ * fac));
            climateUpRight  = climateMap.GetUnpaddedInt((int)(rlX * fac + fac), (int)(rlZ * fac));
            climateBotLeft  = climateMap.GetUnpaddedInt((int)(rlX * fac), (int)(rlZ * fac + fac));
            climateBotRight = climateMap.GetUnpaddedInt((int)(rlX * fac + fac), (int)(rlZ * fac + fac));

            int freezingTemp = TerraGenConfig.DescaleTemperature(-17);


            IntMap landformMap = mapchunk.MapRegion.LandformMap;
            // Amount of pixels for each chunk (probably 1, 2, or 4) in the land form map
            float chunkPixelSize = landformMap.InnerSize / regionChunkSize;
            // Adjusted lerp for the noiseWidth
            float chunkPixelStep = chunkPixelSize / noiseWidth;
            // Start coordinates for the chunk in the region map
            float baseX = (chunkX % regionChunkSize) * chunkPixelSize;
            float baseZ = (chunkZ % regionChunkSize) * chunkPixelSize;


            LerpedWeightedIndex2DMap landLerpMap = GetOrLoadLerpedLandformMap(chunks[0].MapChunk, chunkX / regionChunkSize, chunkZ / regionChunkSize);

            // Terrain octaves
            double[] octNoiseX0, octNoiseX1, octNoiseX2, octNoiseX3;
            double[] octThX0, octThX1, octThX2, octThX3;

            // So it seems we have some kind of off-by-one error here?
            // When the slope of a mountain goes up (in positive z or x direction), particularly at large word heights (512+)
            // then the last blocks (again in postive x/z dir) are below of where they should be?
            // I have no idea why, but this offset seems to greatly mitigate the issue
            float weirdOffset = 0.25f;

            chunkPixelSize += weirdOffset;

            GetInterpolatedOctaves(landLerpMap[baseX, baseZ], out octNoiseX0, out octThX0);
            GetInterpolatedOctaves(landLerpMap[baseX + chunkPixelSize, baseZ], out octNoiseX1, out octThX1);
            GetInterpolatedOctaves(landLerpMap[baseX, baseZ + chunkPixelSize], out octNoiseX2, out octThX2);
            GetInterpolatedOctaves(landLerpMap[baseX + chunkPixelSize, baseZ + chunkPixelSize], out octNoiseX3, out octThX3);


            double[] terrainNoise3d = GetTerrainNoise3D(octNoiseX0, octNoiseX1, octNoiseX2, octNoiseX3, octThX0, octThX1, octThX2, octThX3, chunkX * noiseWidth, 0, chunkZ * noiseWidth);

            // Store heightmap in the map chunk
            ushort[] rainheightmap    = chunks[0].MapChunk.RainHeightMap;
            ushort[] terrainheightmap = chunks[0].MapChunk.WorldGenTerrainHeightMap;


            // Terrain thresholds
            double tnoiseY0;
            double tnoiseY1;
            double tnoiseY2;
            double tnoiseY3;
            double tnoiseGainY0;
            double tnoiseGainY1;
            double tnoiseGainY2;
            double tnoiseGainY3;


            double thNoiseX0;
            double thNoiseX1;
            double thNoiseGainX0;
            double thNoiseGainX1;
            double thNoiseGainZ0;
            double thNoiseZ0;

            float[] terrainThresholdsX0 = new float[api.WorldManager.MapSizeY];
            float[] terrainThresholdsX1 = new float[api.WorldManager.MapSizeY];
            float[] terrainThresholdsX2 = new float[api.WorldManager.MapSizeY];
            float[] terrainThresholdsX3 = new float[api.WorldManager.MapSizeY];



            for (int xN = 0; xN < noiseWidth; xN++)
            {
                for (int zN = 0; zN < noiseWidth; zN++)
                {
                    // Landform thresholds
                    LoadInterpolatedThresholds(landLerpMap[baseX + xN * chunkPixelStep, baseZ + zN * chunkPixelStep], terrainThresholdsX0);
                    LoadInterpolatedThresholds(landLerpMap[baseX + (xN + 1) * chunkPixelStep, baseZ + zN * chunkPixelStep], terrainThresholdsX1);
                    LoadInterpolatedThresholds(landLerpMap[baseX + xN * chunkPixelStep, baseZ + (zN + 1) * chunkPixelStep], terrainThresholdsX2);
                    LoadInterpolatedThresholds(landLerpMap[baseX + (xN + 1) * chunkPixelStep, baseZ + (zN + 1) * chunkPixelStep], terrainThresholdsX3);

                    for (int yN = 0; yN < noiseHeight; yN++)
                    {
                        // Terrain noise
                        tnoiseY0 = terrainNoise3d[NoiseIndex3d(xN, yN, zN)];
                        tnoiseY1 = terrainNoise3d[NoiseIndex3d(xN, yN, zN + 1)];
                        tnoiseY2 = terrainNoise3d[NoiseIndex3d(xN + 1, yN, zN)];
                        tnoiseY3 = terrainNoise3d[NoiseIndex3d(xN + 1, yN, zN + 1)];

                        tnoiseGainY0 = (terrainNoise3d[NoiseIndex3d(xN, yN + 1, zN)] - tnoiseY0) * lerpDeltaVert;
                        tnoiseGainY1 = (terrainNoise3d[NoiseIndex3d(xN, yN + 1, zN + 1)] - tnoiseY1) * lerpDeltaVert;
                        tnoiseGainY2 = (terrainNoise3d[NoiseIndex3d(xN + 1, yN + 1, zN)] - tnoiseY2) * lerpDeltaVert;
                        tnoiseGainY3 = (terrainNoise3d[NoiseIndex3d(xN + 1, yN + 1, zN + 1)] - tnoiseY3) * lerpDeltaVert;



                        for (int y = 0; y < lerpVer; y++)
                        {
                            int posY   = yN * lerpVer + y;
                            int chunkY = posY / chunksize;
                            int localY = posY % chunksize;

                            // For Terrain noise
                            double tnoiseX0 = tnoiseY0;
                            double tnoiseX1 = tnoiseY1;

                            double tnoiseGainX0 = (tnoiseY2 - tnoiseY0) * lerpDeltaHor;
                            double tnoiseGainX1 = (tnoiseY3 - tnoiseY1) * lerpDeltaHor;

                            // Landform thresholds lerp
                            thNoiseX0 = terrainThresholdsX0[posY];
                            thNoiseX1 = terrainThresholdsX2[posY];

                            thNoiseGainX0 = (terrainThresholdsX1[posY] - thNoiseX0) * lerpDeltaHor;
                            thNoiseGainX1 = (terrainThresholdsX3[posY] - thNoiseX1) * lerpDeltaHor;


                            for (int x = 0; x < lerpHor; x++)
                            {
                                // For terrain noise
                                double tnoiseZ0     = tnoiseX0;
                                double tnoiseGainZ0 = (tnoiseX1 - tnoiseX0) * lerpDeltaHor;

                                // Landform
                                thNoiseZ0     = thNoiseX0;
                                thNoiseGainZ0 = (thNoiseX1 - thNoiseX0) * lerpDeltaHor;

                                for (int z = 0; z < lerpHor; z++)
                                {
                                    int lX = xN * lerpHor + x;
                                    int lZ = zN * lerpHor + z;

                                    int mapIndex   = ChunkIndex2d(lX, lZ);
                                    int chunkIndex = ChunkIndex3d(lX, localY, lZ);

                                    chunks[chunkY].Blocks[chunkIndex] = 0;

                                    if (posY == 0)
                                    {
                                        chunks[chunkY].Blocks[chunkIndex] = GlobalConfig.mantleBlockId;
                                        continue;
                                    }


                                    if (tnoiseZ0 > thNoiseZ0)
                                    {
                                        terrainheightmap[mapIndex] = rainheightmap[mapIndex] = (ushort)Math.Max(rainheightmap[mapIndex], posY);

                                        chunks[chunkY].Blocks[chunkIndex] = GlobalConfig.defaultRockId;
                                    }
                                    else
                                    {
                                        if (posY < TerraGenConfig.seaLevel)
                                        {
                                            terrainheightmap[mapIndex] = rainheightmap[mapIndex] = (ushort)Math.Max(rainheightmap[mapIndex], posY);

                                            if (posY == TerraGenConfig.seaLevel - 1)
                                            {
                                                int   temp    = (GameMath.BiLerpRgbColor(((float)lX) / chunksize, ((float)lZ) / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight) >> 16) & 0xff;
                                                float distort = (float)distort2dx.Noise(chunkX * chunksize + lX, chunkZ * chunksize + lZ) / 20f;
                                                float tempf   = TerraGenConfig.GetScaledAdjustedTemperatureFloat(temp, 0) + distort;

                                                // dominionsmod
                                                chunks[chunkY].Blocks[chunkIndex] = (tempf < -17) ? GlobalConfig.lakeIceBlockId : saltWater;
                                            }
                                            else
                                            {
                                                // dominionsmod
                                                chunks[chunkY].Blocks[chunkIndex] = saltWater;
                                            }
                                        }
                                        else
                                        {
                                            chunks[chunkY].Blocks[chunkIndex] = 0;
                                        }
                                    }

                                    tnoiseZ0  += tnoiseGainZ0;
                                    thNoiseZ0 += thNoiseGainZ0;
                                }

                                tnoiseX0 += tnoiseGainX0;
                                tnoiseX1 += tnoiseGainX1;

                                thNoiseX0 += thNoiseGainX0;
                                thNoiseX1 += thNoiseGainX1;
                            }

                            tnoiseY0 += tnoiseGainY0;
                            tnoiseY1 += tnoiseGainY1;
                            tnoiseY2 += tnoiseGainY2;
                            tnoiseY3 += tnoiseGainY3;
                        }
                    }
                }
            }

            int ymax = 0;

            for (int i = 0; i < rainheightmap.Length; i++)
            {
                ymax = Math.Max(ymax, rainheightmap[i]);
            }
            chunks[0].MapChunk.YMax = (ushort)ymax;
        }
Beispiel #20
0
        void genShrubs(int chunkX, int chunkZ)
        {
            int triesShrubs = (int)treeSupplier.treeGenProps.shrubsPerChunk.nextFloat();

            int   dx, dz, x, z;
            Block block;

            while (triesShrubs > 0)
            {
                triesShrubs--;

                dx = rnd.NextInt(chunksize);
                dz = rnd.NextInt(chunksize);
                x  = dx + chunkX * chunksize;
                z  = dz + chunkZ * chunksize;

                int y = heightmap[dz * chunksize + dx];
                if (y <= 0 || y >= worldheight - 15)
                {
                    continue;
                }

                tmpPos.Set(x, y, z);
                block = blockAccessor.GetBlock(tmpPos);
                if (block.Fertility == 0)
                {
                    continue;
                }

                // Place according to forest value
                int   climate     = GameMath.BiLerpRgbColor((float)dx / chunksize, (float)dz / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);
                float shrubChance = GameMath.BiLerp(shrubUpLeft, shrubUpRight, shrubBotLeft, shrubBotRight, (float)dx / chunksize, (float)dz / chunksize);
                shrubChance = GameMath.Clamp(shrubChance + 255 * forestMod, 0, 255);

                if (rnd.NextDouble() > (shrubChance / 255f) * (shrubChance / 255f))
                {
                    continue;
                }
                TreeGenForClimate treegenParams = treeSupplier.GetRandomShrubGenForClimate(climate, (int)shrubChance, y);

                if (treegenParams != null)
                {
                    bool canGen = true;
                    for (int i = 0; i < structuresIntersectingChunk.Count; i++)
                    {
                        if (structuresIntersectingChunk[i].Location.Contains(tmpPos))
                        {
                            canGen = false; break;
                        }
                    }
                    if (!canGen)
                    {
                        continue;
                    }

                    if (blockAccessor.GetBlock(tmpPos.X, tmpPos.Y, tmpPos.Z).Replaceable >= 6000)
                    {
                        tmpPos.Y--;
                    }

                    treegenParams.treeGen.GrowTree(
                        blockAccessor,
                        tmpPos,
                        treegenParams.size,
                        treegenParams.vinesGrowthChance
                        );
                }
            }
        }
Beispiel #21
0
        void genTrees(int chunkX, int chunkZ)
        {
            int   climate = GameMath.BiLerpRgbColor((float)0.5f, (float)0.5f, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);
            float wetrel  = TerraGenConfig.GetRainFall((climate >> 8) & 0xff, heightmap[(chunksize / 2) * chunksize + chunksize / 2]) / 255f;
            float dryrel  = 1 - wetrel;

            float drypenalty = 1 - GameMath.Clamp(2f * (dryrel - 0.5f), 0, 0.8f); // Reduce tree generation by up to 70% in low rain places
            float wetboost   = 1 + 3 * Math.Max(0, wetrel - 0.75f);

            int   triesTrees = (int)(treeSupplier.treeGenProps.treesPerChunk.nextFloat() * drypenalty * wetboost);
            int   dx, dz, x, z;
            Block block;

            while (triesTrees > 0)
            {
                triesTrees--;

                dx = rnd.NextInt(chunksize);
                dz = rnd.NextInt(chunksize);
                x  = dx + chunkX * chunksize;
                z  = dz + chunkZ * chunksize;

                int y = heightmap[dz * chunksize + dx];
                if (y <= 0 || y >= worldheight - 15)
                {
                    continue;
                }

                tmpPos.Set(x, y, z);
                block = blockAccessor.GetBlock(tmpPos);
                if (block.Fertility == 0)
                {
                    continue;
                }

                // Place according to forest value
                float treeDensity = GameMath.BiLerp(forestUpLeft, forestUpRight, forestBotLeft, forestBotRight, (float)dx / chunksize, (float)dz / chunksize);
                climate = GameMath.BiLerpRgbColor((float)dx / chunksize, (float)dz / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);
                //float shrubChance = GameMath.BiLerp(shrubUpLeft, shrubUpRight, shrubBotLeft, shrubBotRight, (float)dx / chunksize, (float)dz / chunksize);

                treeDensity = GameMath.Clamp(treeDensity + forestMod * 255, 0, 255);

                float treeDensityNormalized = treeDensity / 255f;



                // 1 in 400 chance to always spawn a tree
                // otherwise go by tree density using a quadratic drop off to create clearer forest edges
                if (rnd.NextDouble() > Math.Max(0.0025, treeDensityNormalized * treeDensityNormalized) || forestMod <= -1)
                {
                    continue;
                }
                TreeGenForClimate treegenParams = treeSupplier.GetRandomTreeGenForClimate(climate, (int)treeDensity, y);

                if (treegenParams != null)
                {
                    bool canGen = true;
                    for (int i = 0; i < structuresIntersectingChunk.Count; i++)
                    {
                        if (structuresIntersectingChunk[i].Location.Contains(tmpPos))
                        {
                            canGen = false; break;
                        }
                    }
                    if (!canGen)
                    {
                        continue;
                    }

                    if (blockAccessor.GetBlock(tmpPos.X, tmpPos.Y, tmpPos.Z).Replaceable >= 6000)
                    {
                        tmpPos.Y--;
                    }

                    treegenParams.treeGen.GrowTree(
                        blockAccessor,
                        tmpPos,
                        treegenParams.size,
                        treegenParams.vinesGrowthChance
                        );
                }
            }
        }
        internal bool TryGenerateAtSurface(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos pos)
        {
            int chunksize = blockAccessor.ChunkSize;
            int climate   = GameMath.BiLerpRgbColor((float)(pos.X % chunksize) / chunksize, (float)(pos.Z % chunksize) / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight);


            int num    = rand.Next(schematicDatas.Length);
            int orient = rand.Next(4);
            BlockSchematicStructure schematic = schematicDatas[num][orient];


            int widthHalf  = (int)Math.Ceiling(schematic.SizeX / 2f);
            int lengthHalf = (int)Math.Ceiling(schematic.SizeZ / 2f);

            // Ensure not submerged in water

            tmpPos.Set(pos.X - widthHalf, pos.Y + OffsetY, pos.Z - lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(false);
            }

            tmpPos.Set(pos.X + widthHalf, pos.Y + OffsetY, pos.Z - lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(false);
            }

            tmpPos.Set(pos.X - widthHalf, pos.Y + OffsetY, pos.Z + lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(false);
            }

            tmpPos.Set(pos.X + widthHalf, pos.Y + OffsetY, pos.Z + lengthHalf);
            if (blockAccessor.GetBlock(tmpPos).IsLiquid())
            {
                return(false);
            }



            // Probe all 4 corners + center if they either touch the surface or are sightly below ground

            int centerDiff = blockAccessor.GetTerrainMapheightAt(pos) - pos.Y;


            tmpPos.Set(pos.X - widthHalf, 0, pos.Z - lengthHalf);
            int topLeftDiff = blockAccessor.GetTerrainMapheightAt(tmpPos) - pos.Y;

            tmpPos.Set(pos.X + widthHalf, 0, pos.Z - lengthHalf);
            int topRightDiff = blockAccessor.GetTerrainMapheightAt(tmpPos) - pos.Y;

            tmpPos.Set(pos.X - widthHalf, 0, pos.Z + lengthHalf);
            int botLeftDiff = blockAccessor.GetTerrainMapheightAt(tmpPos) - pos.Y;

            tmpPos.Set(pos.X + widthHalf, 0, pos.Z + lengthHalf);
            int botRightDiff = blockAccessor.GetTerrainMapheightAt(tmpPos) - pos.Y;

            bool ok =
                centerDiff >= -3 && centerDiff <= 1 &&
                topLeftDiff >= -3 && topLeftDiff <= 1 &&
                topRightDiff >= -3 && topRightDiff <= 1 &&
                botLeftDiff >= -3 && botLeftDiff <= 1 &&
                botRightDiff >= -3 && botRightDiff <= 1
            ;


            if (ok)
            {
                schematic.PlaceRespectingBlockLayers(blockAccessor, worldForCollectibleResolve, pos, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight, replaceblockids);
            }

            return(ok);
        }