local zprite = require("zprite") local tex = love.graphics.newImage("atlas.png") tex:setFilter("nearest", "nearest") local gx = 0 local gy = 0 local angle = 0 local scale = 0.35 local dist = 5 local atlas = { water = love.graphics.newQuad(1, 1, 8, 8, tex), sand = love.graphics.newQuad(11, 1, 8, 8, tex), dirt = love.graphics.newQuad(21, 1, 8, 8, tex), grass = love.graphics.newQuad(31, 1, 8, 8, tex), stone = love.graphics.newQuad(1, 11, 8, 8, tex), snow = love.graphics.newQuad(11, 11, 8, 8, tex), } local alpha = 0.75 local shadow_alpha = 0.15 local colors = {} function colors.water(layer) if layer % 2 == 0 then return 0, 0, 0, shadow_alpha end layer = layer / 2 return 0.1, layer / 16, layer / 8, alpha end function colors.sand(layer) if layer % 2 == 0 then return 0, 0, 0, shadow_alpha end layer = layer / 2 return layer / 16 + 0.2, layer / 16 + 0.15, 0.4, alpha end function colors.dirt(layer) if layer % 2 == 0 then return 0, 0, 0, shadow_alpha end layer = layer / 2 if layer < 16 then return layer / 40, layer / 80, 0.1, alpha else return 0.1, layer / 50 + 0.1, 0.1, alpha end end function colors.stone(layer) if layer % 2 == 0 then return 0, 0, 0, shadow_alpha end layer = layer / 2 local value = layer / 48 - 0.2 return value / 1.1, value, value * 1.1, alpha end function colors.snow(layer) if layer % 2 == 0 then return 0, 0, 0, shadow_alpha end layer = layer / 2 local value = layer / 48 + 0.25 return value / 1.1, value, value * 1.1, alpha end local function offset_map(layer) if layer % 2 == 0 then return 1, 7 else return 0, 7 end end local z = zprite.zchunk.new(tex, 16 * 8, 80, dist * 2 + 1) local function layered_noise(x, y, layers, persistence) local freq = 1 local ampl = 1 local maxval = 0 local val = 0 for _ = 1, layers do val = val + love.math.simplexNoise(x * freq, y * freq) * ampl maxval = maxval + ampl ampl = ampl * persistence freq = freq * 2 end return val / maxval end local function ease(n) return math.min((1.1 * n + 1 - math.cos(n * math.pi / 2)) / 2, 1) end local function heightmap(x, y) return ease(layered_noise(x * 0.0005, y * 0.0005, 4, 0.8)) end local function dirtgrass_sprite(layer) if layer < 20 then return atlas.dirt end return atlas.grass end local function pick_from_atlas(height) if height < 0.25 then return atlas.water end if height < 0.3 then return atlas.sand end if height < 0.7 then return dirtgrass_sprite end if height < 0.9 then return atlas.stone end return atlas.snow end local function pick_color(height) if height < 0.25 then return colors.water end if height < 0.3 then return colors.sand end if height < 0.67 then return colors.dirt end if height < 0.85 then return colors.stone end return colors.snow end local function generate(x, y) local height = heightmap(x, y) z:put(x, y, atlas.stone, math.ceil(height * 80), pick_color(height), offset_map) end local generated = {} local function generate_chunk(cx, cy) local idx = z:_chunk_index(cx * z._chunk_size, cy * z._chunk_size) if generated[idx] then return end generated[idx] = { cx, cy } z:clear(cx * z._chunk_size, cy * z._chunk_size) for i = 1, z._chunk_size, 8 do for j = 1, z._chunk_size, 8 do local x = cx * z._chunk_size + i - 1 local y = cy * z._chunk_size + j - 1 generate(x, y) end end end function love.update(dt) local speed = 512 if love.keyboard.isDown("d") then local dx = speed * math.cos(-angle) local dy = speed * math.sin(-angle) gx = gx - dx * dt gy = gy - dy * dt end if love.keyboard.isDown("a") then local dx = speed * math.cos(-angle) local dy = speed * math.sin(-angle) gx = gx + dx * dt gy = gy + dy * dt end if love.keyboard.isDown("s") then local dx = speed * math.cos(-angle + math.pi / 2) local dy = speed * math.sin(-angle + math.pi / 2) gx = gx - dx * dt gy = gy - dy * dt end if love.keyboard.isDown("w") then local dx = speed * math.cos(-angle + math.pi / 2) local dy = speed * math.sin(-angle + math.pi / 2) gx = gx + dx * dt gy = gy + dy * dt end if love.keyboard.isDown("q") then angle = angle + dt end if love.keyboard.isDown("e") then angle = angle - dt end if love.keyboard.isDown("f") then scale = scale * (1 - dt) if scale < 0.2 then scale = 0.2 end end if love.keyboard.isDown("r") then scale = scale * (1 + dt) if scale > 5 then scale = 5 end end local center_cx = -math.floor(gx / z._chunk_size) local center_cy = -math.floor(gy / z._chunk_size) for key, gen in pairs(generated) do if math.abs(gen[1] - center_cx) > dist or math.abs(gen[2] - center_cy) > dist then generated[key] = nil end end for cx = center_cx - dist, center_cx + dist do for cy = center_cy - dist, center_cy + dist do generate_chunk(cx, cy) end end end local t = love.math.newTransform() function love.draw() z._height_scale = scale * 1.4 t:reset() t:translate(love.graphics.getWidth() / 2, love.graphics.getHeight() / 2) t:scale(scale, scale) t:rotate(angle) t:translate(gx - z._chunk_size / 2, gy - z._chunk_size / 2) z:draw(t) end