/// <summary> /// forceInitialPosY is for subdeposits /// </summary> /// <param name="chunks"></param> /// <param name="chunkX"></param> /// <param name="chunkZ"></param> /// <param name="offsetX"></param> /// <param name="offsetZ"></param> /// <param name="variant"></param> /// <param name="forceInitialPosY"></param> /// <returns></returns> Dictionary <Vec3i, DepositVariant> GenDeposit(IServerChunk[] chunks, int chunkX, int chunkZ, int offsetX, int offsetZ, DepositVariant variant, int?forceInitialPosY = null) { Dictionary <Vec3i, DepositVariant> SubDepositsToPlace = new Dictionary <Vec3i, DepositVariant>(); IMapChunk mapchunk = chunks[0].MapChunk; int radius = Math.Min(64, (int)variant.Radius.nextFloat(1, depositRand)); if (radius <= 0) { return(SubDepositsToPlace); } // Let's deform that perfect circle a bit (+/- 25%) float deform = GameMath.Clamp(depositRand.NextFloat() - 0.5f, -0.25f, 0.25f); int radiusX = radius - (int)(radius * deform); int radiusZ = radius + (int)(radius * deform); int posY; // No need to caluclate further if this deposit won't be part of this chunk if (radiusX + offsetX < 0 || radiusZ + offsetZ < 0 || offsetX - radiusX >= chunksize || offsetZ - radiusZ >= chunksize) { return(SubDepositsToPlace); } IMapChunk originMapchunk = null; int origPosY = 0; int lx = GameMath.Mod(offsetX, chunksize); int lz = GameMath.Mod(offsetZ, chunksize); if (variant.MaxY < 1 || variant.CheckClimate) { originMapchunk = api.WorldManager.GetMapChunk((chunkX * chunksize + offsetX) / chunksize, (chunkZ * chunksize + offsetZ) / chunksize); if (originMapchunk == null) { return(SubDepositsToPlace); // argh >.< } origPosY = originMapchunk.RainHeightMap[lz * chunksize + lx]; if ((float)origPosY / api.World.BlockAccessor.MapSizeY > variant.MaxY) { return(SubDepositsToPlace); } } // Check if suited for this area, climate wise if (variant.CheckClimate) { IntMap climateMap = api.World.BlockAccessor.GetMapRegion((chunkX * chunksize + offsetX) / regionSize, (chunkZ * chunksize + offsetZ) / regionSize).ClimateMap; float posXInRegionClimate = ((float)lx / regionSize - lx / regionSize) * noiseSizeClimate; float posZInRegionClimate = ((float)lz / regionSize - lz / regionSize) * noiseSizeClimate; int climate = climateMap.GetUnpaddedColorLerped(posXInRegionClimate, posZInRegionClimate); float temp = TerraGenConfig.GetScaledAdjustedTemperatureFloat((climate >> 16) & 0xff, origPosY - TerraGenConfig.seaLevel); float rainRel = TerraGenConfig.GetRainFall((climate >> 8) & 0xff, origPosY) / 255f; if (rainRel < variant.MinRain || rainRel > variant.MaxRain || temp < variant.MinTemp || temp > variant.MaxTemp) { return(SubDepositsToPlace); } } // Ok generate float th = variant.Thickness.nextFloat(1, depositRand); int thickness = (int)th + (depositRand.NextFloat() < th - (int)th ? 1 : 0); float xRadSqInv = 1f / (radiusX * radiusX); float zRadSqInv = 1f / (radiusZ * radiusZ); int blockIndex = 0; bool parentBlockOk = false; float depthf; bool shouldGenSurfaceDeposit = depositRand.NextFloat() > 0.35f && variant.SurfaceBlockCode != null; if (forceInitialPosY != null) { depthf = (float)forceInitialPosY / mapchunk.WorldGenTerrainHeightMap[offsetX * chunksize + offsetZ]; } else { depthf = variant.Depth.nextFloat(1, depositRand); } int depthi = (int)depthf; int topLeft = 2 * depositRand.NextInt(radiusX + 1) - radiusX; int topRight = 2 * depositRand.NextInt(radiusZ + 1) - radiusZ; int botLeft = 2 * depositRand.NextInt(radiusX + 1) - radiusX; int botRight = 2 * depositRand.NextInt(radiusZ + 1) - radiusZ; int yOff = 0; // Only generate inside this current chunk column int minx = GameMath.Clamp(offsetX - radiusX, 0, chunksize); int maxx = GameMath.Clamp(offsetX + radiusX, 0, chunksize); int minz = GameMath.Clamp(offsetZ - radiusZ, 0, chunksize); int maxz = GameMath.Clamp(offsetZ + radiusZ, 0, chunksize); float invChunkAreaSize = 1f / (chunksize * chunksize); for (int x = minx; x < maxx; x++) { float xSq = (x - offsetX) * (x - offsetX) * xRadSqInv; for (int z = minz; z < maxz; z++) { if (xSq + (z - offsetZ) * (z - offsetZ) * zRadSqInv > 1) { continue; } if (variant.Placement == EnumDepositPlacement.FollowSurfaceBelow) { posY = mapchunk.WorldGenTerrainHeightMap[z * chunksize + x] - depthi; } else if (variant.Placement == EnumDepositPlacement.FollowSurface) { yOff = (int)GameMath.BiLerp(topLeft, topRight, botLeft, botRight, (x - offsetX + radiusX) / (2f * radiusX), (z - offsetZ + radiusZ) / (2f * radiusZ)); posY = (int)(depthf * mapchunk.WorldGenTerrainHeightMap[z * chunksize + x]) + yOff / 2; } else if (variant.Placement == EnumDepositPlacement.Straight) { posY = (int)(depthf * mapchunk.WorldGenTerrainHeightMap[z * chunksize + x]); } else { yOff = (int)GameMath.BiLerp(topLeft, topRight, botLeft, botRight, (x - offsetX + radiusX) / (2f * radiusX), (z - offsetZ + radiusZ) / (2f * radiusZ)); posY = depthi + yOff; } // Some deposits may not appear all over cliffs if (variant.CheckClimate && Math.Abs(origPosY - posY) > variant.MaxYRoughness) { continue; } for (int y = 0; y < thickness; y++) { if (posY <= 1 || posY >= worldheight) { continue; } long index3d = ((posY % chunksize) * chunksize + z) * chunksize + x; ushort blockId = chunks[posY / chunksize].Blocks[index3d]; // Check if we are in mother material, but only if it has changed since last iteration (should reduce amount of these checks by 50-100%) parentBlockOk = false; for (int i = 0; i < variant.ParentBlockIds.Length; i++) { if (variant.ParentBlockIds[i] == blockId) { parentBlockOk = true; blockIndex = i; break; } } if (parentBlockOk) { if (variant.WithBlockCallback) { tmpPos.Set(chunkX * chunksize + x, posY, chunkZ * chunksize + z); blockTypes[variant.BlockIds[blockIndex]].TryPlaceBlockForWorldGen(blockAccessor, tmpPos, BlockFacing.UP); } else { chunks[posY / chunksize].Blocks[index3d] = variant.BlockIds[blockIndex]; } for (int i = 0; i < variant.ChildDeposits.Length; i++) { float rndVal = depositRand.NextFloat(); float quantity = variant.ChildDeposits[i].Quantity * invChunkAreaSize; if (quantity > rndVal) { Vec3i pos = new Vec3i(x, posY, z); if (ShouldPlaceAdjustedForOreMap(variant.ChildDeposits[i], chunkX * chunksize + x, chunkZ * chunksize + z, quantity, rndVal)) { SubDepositsToPlace[pos] = variant.ChildDeposits[i]; } } } if (shouldGenSurfaceDeposit) { int surfaceY = mapchunk.RainHeightMap[z * chunksize + x]; int depth = surfaceY - posY; float chance = variant.SurfaceBlockChance * Math.Max(0, 1 - depth / 8f); if (depositRand.NextFloat() < chance) { index3d = (((surfaceY + 1) % chunksize) * chunksize + z) * chunksize + x; Block belowBlock = api.World.Blocks[chunks[surfaceY / chunksize].Blocks[((surfaceY % chunksize) * chunksize + z) * chunksize + x]]; if (belowBlock.SideSolid[BlockFacing.UP.Index] && chunks[(surfaceY + 1) / chunksize].Blocks[index3d] == 0) { chunks[(surfaceY + 1) / chunksize].Blocks[index3d] = variant.SurfaceBlockIds[blockIndex]; } } } } posY--; } } } return(SubDepositsToPlace); }
private void CarveTunnel(IServerChunk[] chunks, int chunkX, int chunkZ, double posX, double posY, double posZ, float horAngle, float vertAngle, float horizontalSize, float verticalSize, int currentIteration, int maxIterations, int branchLevel) { blockId = airBlockId; ushort[] heightmap = chunks[0].MapChunk.WorldGenTerrainHeightMap; float horAngleChange = 0; float vertAngleChange = 0; float horRadiusChange = 0; float horRadiusChangeAccum = 0; float relPos; while (currentIteration++ < maxIterations) { relPos = (float)currentIteration / maxIterations; float horRadius = 1.5f + GameMath.FastSin(relPos * GameMath.PI) * horizontalSize + horRadiusChangeAccum; float vertRadius = (horRadius - horRadiusChangeAccum / 2) * verticalSize; float advanceHor = GameMath.FastCos(vertAngle); float advanceVer = GameMath.FastSin(vertAngle); posX += GameMath.FastCos(horAngle) * advanceHor; posY += advanceVer; posZ += GameMath.FastSin(horAngle) * advanceHor; vertAngle *= 0.8f; if (caveRand.NextInt(50) == 0) { horRadiusChange = caveRand.NextFloat() * caveRand.NextFloat() * 7; } horRadiusChangeAccum = Math.Max(0, horRadiusChangeAccum + horRadiusChange * 0.15f); horRadiusChange -= 0.45f; horAngle += 0.1f * horAngleChange; vertAngle += 0.1f * vertAngleChange; vertAngleChange = 0.9f * vertAngleChange + (caveRand.NextFloat() - caveRand.NextFloat()) * caveRand.NextFloat() * 3; horAngleChange = 0.9f * horAngleChange + (caveRand.NextFloat() - caveRand.NextFloat()) * caveRand.NextFloat() * 1; // Horizontal branch if (caveRand.NextInt(25 * (branchLevel + 1)) == 0 && branchLevel < 3) { CarveTunnel( chunks, chunkX, chunkZ, posX, posY, posZ, horAngle + (caveRand.NextFloat() + caveRand.NextFloat() - 1) + GameMath.PI, vertAngle + (caveRand.NextFloat() - 0.5f) * (caveRand.NextFloat() - 0.5f), horizontalSize, verticalSize, currentIteration, maxIterations - (int)(caveRand.NextFloat() * 0.5 * maxIterations), branchLevel + 1 ); } // Vertical branch if (horRadius > 3 && posY > 60 && caveRand.NextInt(40) == 0 && branchLevel < 1) { CarveShaft( chunks, chunkX, chunkZ, posX, posY, posZ, horAngle + (caveRand.NextFloat() + caveRand.NextFloat() - 1) + GameMath.PI, -GameMath.PI / 2 - 0.1f + 0.2f * caveRand.NextFloat(), Math.Min(3.5f, horRadius - 1), verticalSize, currentIteration, maxIterations - (int)(caveRand.NextFloat() * 0.5 * maxIterations) + (int)((posY / 5) * (0.5f + 0.5f * caveRand.NextFloat())), branchLevel ); branchLevel++; } // Lake /* bool end = currentIteration == maxIterations; * * if (end && caveRand.NextInt(4) == 0) * { * CarveLake(chunks, horRadius, vertRadius, posX, posY, posZ, heightmap, chunkX, chunkZ); * continue; * }*/ if (caveRand.NextInt(5) == 0 && horRadius >= 2) { continue; } // Check just to prevent unnecessary calculations // As long as we are outside the currently generating chunk, we don't need to generate anything if (posX <= -horRadius * 2 || posX >= chunksize + horRadius * 2 || posZ <= -horRadius * 2 || posZ >= chunksize + horRadius * 2) { continue; } SetBlocks(chunks, horRadius, vertRadius, posX, posY, posZ, heightmap, chunkX, chunkZ); } }