Made a fun little script because somebody I know wanted a way to get rid of all of the water in the game. It works fast enough, although due to size constraints it can take a fair amount of time for large areas of terrain (using Roblox’s terrain generation tool at a size of 512 takes around 2 minutes to remove all water from, using a size of 3000x2000x3000 studs). Only requires setting the (approximate) center of terrain and an approximate size the terrain encompasses. So it’s technically not all water in the game, but you can set the size of the area you want to modify and go from there.
It wasn’t too complicated, but the Terrain API isn’t really helpful for doing stuff like this.
Could also easily modify the script so that, rather than replacing water with air, it replaces any material you want with another material. With a little bit more work, could make it to, say, lower the level of water by 1 or something like that.
Code
local Terrain = workspace.Terrain
local processBlockSize = 640
local WATER = Enum.Material.Water
local AIR = Enum.Material.Air
local floor = math.floor
function isInRegion3(region, point)
--function by buildthomas
--https://devforum.roblox.com/t/see-if-vector3-is-in-region3/118457/7
local relative = (point - region.CFrame.p) / region.Size
return -0.5 <= relative.X and relative.X <= 0.5
and -0.5 <= relative.Y and relative.Y <= 0.5
and -0.5 <= relative.Z and relative.Z <= 0.5
end
local function round(num)
return math.floor(num + .5)
end
local function getAlignedPosition(pos)
--Function from https://wiki.roblox.com/index.php?title=Smooth_terrain#Flood_Fill
local x = round(pos.X)
x = x - x%4 + 2
local y = round(pos.Y)
y = y - y%4 + 2
local z = round(pos.Z)
z = z - z%4 + 2
return Vector3.new(x,y,z)
end
local function comma_value(n) --http://lua-users.org/wiki/FormattingNumbers
local left,num,right = string.match(n,'^([^%d]*%d)(%d*)(.-)$')
return left..(num:reverse():gsub('(%d%d%d)','%1,'):reverse())..right
end
local function removeWater(voxelPos, worldSize)
local functionStart = tick()
voxelPos = getAlignedPosition(voxelPos)
local material = nil
local occupancy = nil
local boundaryRegion = Region3.new(voxelPos-worldSize/2, voxelPos+worldSize/2)
boundaryRegion:ExpandToGrid(4)
local minPoint = boundaryRegion.CFrame.p-boundaryRegion.Size/2
print("Starting")
local partsProcessed = 0
local regions = {} --Stores all of the regions to read/write to
local start = getAlignedPosition(minPoint)
local xOffset = 0
local yOffset = 0
local zOffset = 0
local done = false
repeat
local startPos = start+Vector3.new(xOffset,yOffset,zOffset)
local size = boundaryRegion.CFrame.p+boundaryRegion.Size/2-startPos
local x,y,z = size.X, size.Y, size.Z
if x > processBlockSize then
x = processBlockSize
end
if y > processBlockSize then
y = processBlockSize
end
if z > processBlockSize then
z = processBlockSize
end
size = Vector3.new(x,y,z)
local region = Region3.new(startPos, startPos+size)
region = region:ExpandToGrid(4)
table.insert(regions, region)
zOffset = zOffset + processBlockSize
if zOffset >= boundaryRegion.Size.Z then
zOffset = 0
yOffset = yOffset + processBlockSize
end
if yOffset >= boundaryRegion.Size.Y then
yOffset = 0
xOffset = xOffset + processBlockSize
end
if xOffset >= boundaryRegion.Size.X then
done = true
end
until done
print("Writing to ", #regions, " regions!")
print("Removing water...")
local totalRemoved = 0
local totalProcessed = 0
local totalVolume = worldSize.X*worldSize.Y*worldSize.Z
local changed,materials,occupancy,size,matsX,occX,matsY,occY,p
for index, region in pairs(regions) do
--[[
--Uncomment this if you want parts to display where each chunk is
p = Instance.new("Part")
p.Anchored = true
p.CanCollide = false
p.Transparency = 0.9
--p.BrickColor = BrickColor.new(index%255)
p.Parent = workspace
p.Size = region.Size
p.CFrame = region.CFrame
]]
changed = 0
materials, occupancy = Terrain:ReadVoxels(region, 4)
size = materials.Size
for x = 1, size.X do
matsX = materials[x] --Creating variables to reduce amount of searching
occX = occupancy[x]
for y = 1, size.Y do
matsY = matsX[y]
occY = occX[y]
for z = 1, size.Z do
totalProcessed = totalProcessed + 1
if matsY[z] == WATER then
matsY[z] = AIR
occY[z] = 0
changed = changed + 1
end
end
end
--[[
--Use this and comment out the wait below if you want constant progress updates
if x%10 == 0 then
print((floor((index-1+x/size.X)/#regions*1000+0.5)/10).."% complete!")
wait(0)
end
]]
end
if changed > 0 then --No need to write if there is no changes
Terrain:WriteVoxels(region, 4, materials, occupancy)
end
--Comment the following block out if you use the above wait ability.
if index%4 == 0 then
print((floor(index/#regions*1000+0.5)/10).."% complete!")
wait(0)
end
totalRemoved = totalRemoved + changed
end
print("Total time elapsed: ", (tick()-functionStart), " seconds!")
print("Total water blocks removed: ", totalRemoved)
print("Total cells processed: ", comma_value(totalProcessed))
print("Region/Chunks: ", #regions)
print("Total Volume: ", totalVolume, "cubic studs")
print("Done processing")
end
--Use this function as pass it the origin the removing should take place, and the size
--of the area to check for water.
--removeWater(origin, size)
removeWater(Vector3.new(0,0,0), Vector3.new(3000,2000,3000))
Also attaching the place file used (and hey, rounds to 666 KB, wasn’t even planning for that). I was planning on making it a plugin, but I felt it’d take too much work learning how to use plugins and implement the various settings (origin, size, which material to which, etc). But if anybody was willing, that’d be sweet.
clearTerrainWater.rbxl (665.7 KB)
@Elmuowo
I don’t suppose you still need something like this?