From 1ebd7d9b7b62c8e05d527611a1944ed1a876b890 Mon Sep 17 00:00:00 2001 From: ubq323 Date: Fri, 3 Feb 2023 19:37:28 +0000 Subject: debug drawing change, zoom clamping, partial refactoring of class mechanisms to allow inheritance, minor refactor of noise generator, changes to temp worldgen, rework of class constructor mechanism --- client/camera.lua | 16 ++++++++-------- client/drawing.lua | 19 ++++++++++++++++--- client/main.lua | 22 ++++++++++++---------- common/.map.lua.kak.Xd2mKc | 5 +++++ common/chunk.lua | 15 ++++++++------- common/class.lua | 23 +++++++++++++++++++++++ common/coords.lua | 34 +++++++++++++++++----------------- common/map.lua | 6 ++++++ server/noise.lua | 34 +++++++++++++--------------------- server/server.lua | 41 ++++++++++++++++++++++++++++++++--------- 10 files changed, 140 insertions(+), 75 deletions(-) create mode 100644 common/.map.lua.kak.Xd2mKc create mode 100644 common/class.lua create mode 100644 common/map.lua diff --git a/client/camera.lua b/client/camera.lua index 6309e38..528b45b 100644 --- a/client/camera.lua +++ b/client/camera.lua @@ -1,15 +1,15 @@ local coords = require"common.coords" +local class = require"common.class" -- in screen units local screen_width, screen_height = love.graphics.getDimensions() -- zoom is screen pixels per world unit -local Camera = {} -Camera.__index = Camera -function Camera.make(pos,zoom) - pos = pos or coords.Pos.make(0,0) +local Camera = class() +function Camera.make(cls,pos,zoom) + pos = pos or coords.Pos:make(0,0) zoom = zoom or 30 - return setmetatable({pos=pos,zoom=zoom},Camera) + return setmetatable({pos=pos,zoom=zoom},cls) end function Camera.apply_trans(self) love.graphics.origin() @@ -21,7 +21,7 @@ function Camera.apply_trans(self) love.graphics.translate(-self.pos.x,-self.pos.y) end -local screen_offset = coords.Pos.make(screen_width/2,screen_height/2) +local screen_offset = coords.Pos:make(screen_width/2,screen_height/2) function Camera.screen_to_world(self,pos) return (pos-screen_offset)/self.zoom + self.pos end @@ -31,8 +31,8 @@ end function Camera.extents(self) -- returns top left and bottom right pos's in world coords - return self:screen_to_world(coords.Pos.make(0,0)), - self:screen_to_world(coords.Pos.make(screen_width,screen_height)) + return self:screen_to_world(coords.Pos:make(0,0)), + self:screen_to_world(coords.Pos:make(screen_width,screen_height)) end diff --git a/client/drawing.lua b/client/drawing.lua index 111b2e2..bbcf03a 100644 --- a/client/drawing.lua +++ b/client/drawing.lua @@ -33,7 +33,6 @@ local colors = { c(255,128,0), -- orange c(192,192,64), -- yellow c(0,192,0), -- green - c(0,192,192), -- teal c(64,64,255), -- blue c(192,0,192), -- purple @@ -67,14 +66,14 @@ end local function draw_chunk(camera,the_chunk) local tl,br = camera:extents() local tlh,brh = tl:to_hex():round(), br:to_hex():round() - local trh = coords.Pos.make(br.x,tl.y):to_hex():round() + local trh = coords.Pos:make(br.x,tl.y):to_hex():round() for r = tlh.r-1,brh.r+1 do local rowidx = r-tlh.r local minq = tlh.q - math.floor((rowidx+1)/2) local maxq = minq+(trh.q-tlh.q)+1 for q = minq,maxq do - local h = coords.Hex.make(q,r) + local h = coords.Hex:make(q,r) local t = the_chunk:at(h) if type(t) == "number" then draw_hex(h:to_pos(),colors[t],camera.zoom) @@ -84,6 +83,20 @@ local function draw_chunk(camera,the_chunk) end end + if _G.debugmode then + love.graphics.setColor(0,1,0) + + local function p(q,r) return coords.Hex:make(q,r):to_pos() end + local h = chunk.SIZE-0.5 + local c00 = p(-0.5,-0.5) + local c01 = p(-0.5,h) + local c10 = p(h,-0.5) + local c11 = p(h,h) + + love.graphics.polygon("line", + c00.x,c00.y, c01.x,c01.y, c11.x,c11.y, c10.x, c10.y) + end + end diff --git a/client/main.lua b/client/main.lua index 2fad49e..7af1bd4 100644 --- a/client/main.lua +++ b/client/main.lua @@ -10,7 +10,7 @@ local local_player = nil local drawing = require"drawing" local coords = require"common.coords" local Pos = coords.Pos -local camera = require"camera".Camera.make() +local camera = require"camera".Camera:make() local Chunk = require"common.chunk".Chunk local util = require"util" @@ -24,10 +24,10 @@ local host,peer local chunk -local f3mode = false +_G.debugmode = false function love.keypressed(key,scancode,isrepeat) - if scancode == "f3" then f3mode = not f3mode end + if scancode == "f3" then _G.debugmode = not _G.debugmode end end local function draw_player(pl,islocal) @@ -45,7 +45,8 @@ end local remote_players = {} local function update_local_player(pl,dt) - local SPEED = 10 -- pixels/sec + local SPEED = 10 -- units/sec + if love.keyboard.isScancodeDown("lshift") then SPEED = 100 end local function kd(code) if love.keyboard.isScancodeDown(code) then return 1 else return 0 end end @@ -85,10 +86,11 @@ function love.update(dt) update_local_player(local_player,dt) if love.keyboard.isScancodeDown"q" then camera.zoom = camera.zoom*1.05 end if love.keyboard.isScancodeDown"e" then camera.zoom = camera.zoom/1.05 end + camera.zoom = math.max(2.25,math.min(50,camera.zoom)) sync_local_player(local_player,peer) end if chunk then - local mh = camera:screen_to_world(Pos.make(love.mouse.getPosition())):to_hex():round() + local mh = camera:screen_to_world(Pos:make(love.mouse.getPosition())):to_hex():round() if false== chunk:at(mh) and love.mouse.isDown(1) then chunk:set_at(mh,true) -- print(mh,true) @@ -108,7 +110,7 @@ function love.update(dt) local op = j.t if op == "join" then local pl = j.pl - remote_players[pl.id] = {pos=coords.Pos.make(pl.x,pl.y),color=pl.color,id=pl.id} + remote_players[pl.id] = {pos=coords.Pos:make(pl.x,pl.y),color=pl.color,id=pl.id} elseif op == "leave" then local id = j.id remote_players[id]=nil @@ -119,11 +121,11 @@ function love.update(dt) remote_players[id].pos.y = y elseif op == "you" then local pl = j.pl - local_player = {pos=coords.Pos.make(pl.x,pl.y),color=pl.color,id=pl.id} + local_player = {pos=coords.Pos:make(pl.x,pl.y),color=pl.color,id=pl.id} elseif op == "chunk" then chunk = Chunk.from_packet_data(j) elseif op == "settile" then - local h = coords.Hex.make(j.q,j.r) + local h = coords.Hex:make(j.q,j.r) chunk:set_at(h,j.tile) end end @@ -152,12 +154,12 @@ function love.draw() love.graphics.setColor(1,0,0) love.graphics.rectangle("fill",0,0,1,1) - local sm = Pos.make(love.mouse.getPosition()) + local sm = Pos:make(love.mouse.getPosition()) local wm = camera:screen_to_world(sm) local hm = wm:to_hex() love.graphics.origin() - if f3mode and local_player then + if _G.debugmode and local_player then util.print_good({ "ms "..tostring(sm), "mw "..tostring(wm), diff --git a/common/.map.lua.kak.Xd2mKc b/common/.map.lua.kak.Xd2mKc new file mode 100644 index 0000000..aa2bfb0 --- /dev/null +++ b/common/.map.lua.kak.Xd2mKc @@ -0,0 +1,5 @@ +-- a Map is a 2d array of chunks +-- it handles loading and unloading of chunks +-- the specifics of which are then implemented separately for client and server +-- it will probably also do things relating to entities and multiblock things + diff --git a/common/chunk.lua b/common/chunk.lua index 8e2ec40..1d3faa6 100644 --- a/common/chunk.lua +++ b/common/chunk.lua @@ -1,6 +1,7 @@ local json = require"common.dkjson" +local class = require"common.class" -local CHUNK_SIZE = 128 +local CHUNK_SIZE = 64 -- for now tiles shall be booleans @@ -15,10 +16,9 @@ local function index(offq,offr) return CHUNK_SIZE*offq + offr + 1 end -local Chunk = {} -Chunk.__index = Chunk -function Chunk.make(tiles) - return setmetatable({tiles=tiles},Chunk) +local Chunk = class() +function Chunk.make(cls,tiles) + return setmetatable({tiles=tiles},cls) end function Chunk.at(self,hoffs) if not index_ok(hoffs.q,hoffs.r) then return nil end @@ -35,11 +35,12 @@ end function Chunk.from_packet_data(packet) -- assuming packet has already been json.decoded -- since otherwise how would we know it's a chunk packet - return Chunk.make(packet.tiles) + return Chunk:make(packet.tiles) end return { Chunk=Chunk, SIZE=CHUNK_SIZE, - index=index + index=index, + index_ok=index_ok, } diff --git a/common/class.lua b/common/class.lua new file mode 100644 index 0000000..37cf7bd --- /dev/null +++ b/common/class.lua @@ -0,0 +1,23 @@ +local function class() + local T = {} + T.__index = T + return T +end + +local function extend(Base) + local T = {} + T.__index = T + for k,v in pairs(Base) do + if k:sub(1,2) == "__" and k~="__index" then + T[k]=v + end + end + setmetatable(T,{__index=Base}) +end + +return setmetatable({ + class=class, + extend=extend +},{__call=class}) + + diff --git a/common/coords.lua b/common/coords.lua index 2399e43..351aa53 100644 --- a/common/coords.lua +++ b/common/coords.lua @@ -1,3 +1,5 @@ +local class = require"common.class" + -- Hex: q,r,s. invariant that q+r+s=0 -- add, subtract -- constructor takes 3 positions and rounds to closest hex centre. @@ -15,12 +17,11 @@ local Pos, Hex local SR3 = math.sqrt(3) -Hex={} -Hex.__index = Hex -function Hex.make(q,r,s) +local Hex = class() +function Hex.make(cls,q,r,s) s=s or -q-r assert(q+r+s==0,"hex coord doesn't meet invariant") - return setmetatable({q=q,r=r,s=s},Hex) + return setmetatable({q=q,r=r,s=s},cls) end function Hex.round(self) -- return a new Hex rounded to integer coordinates @@ -40,32 +41,31 @@ function Hex.round(self) rs = -rq-rr end - return Hex.make(rq,rr,rs) + return Hex:make(rq,rr,rs) end function Hex.to_pos(self) local x = self.q*SR3 + self.r*(SR3/2) local y = self.r*(3/2) - return Pos.make(x,y) + return Pos:make(x,y) end -function Hex.__add(self,other) return Hex.make(self.q+other.q, self.r+other.r, self.s+other.s) end -function Hex.__sub(self,other) return Hex.make(self.q-other.q, self.r-other.r, self.s-other.s) end +function Hex.__add(self,other) return Hex:make(self.q+other.q, self.r+other.r, self.s+other.s) end +function Hex.__sub(self,other) return Hex:make(self.q-other.q, self.r-other.r, self.s-other.s) end function Hex.__tostring(self) return string.format("H(%.2f,%.2f)",self.q,self.r) end -Pos = {} -Pos.__index=Pos -function Pos.make(x,y) - return setmetatable({x=x,y=y},Pos) +Pos = class() +function Pos.make(cls,x,y) + return setmetatable({x=x,y=y},cls) end -function Pos.__add(self,other) return Pos.make(self.x+other.x,self.y+other.y) end -function Pos.__sub(self,other) return Pos.make(self.x-other.x,self.y-other.y) end +function Pos.__add(self,other) return Pos:make(self.x+other.x,self.y+other.y) end +function Pos.__sub(self,other) return Pos:make(self.x-other.x,self.y-other.y) end function Pos.__mul(a,b) if type(a) == "number" then - return Pos.make(a*b.x,a*b.y) + return Pos:make(a*b.x,a*b.y) elseif type(b) == "number" then - return Pos.make(a.x*b,a.y*b) + return Pos:make(a.x*b,a.y*b) else error("can only multiply Pos by scalar") end @@ -81,7 +81,7 @@ function Pos.dot(self,other) return self.x*other.x + self.y*other.y end function Pos.to_hex(self) local q = self.x*(SR3/3) - self.y*(1/3) local r = (2/3)*self.y - return Hex.make(q,r,-q-r) + return Hex:make(q,r,-q-r) end function Pos.__tostring(self) return string.format("(%.2f,%.2f)",self.x,self.y) end diff --git a/common/map.lua b/common/map.lua new file mode 100644 index 0000000..fe4b9e0 --- /dev/null +++ b/common/map.lua @@ -0,0 +1,6 @@ +-- a Map is a 2d array of chunks +-- it handles loading and unloading of chunks +-- the specifics of which are then implemented separately for client and server +-- it will probably also do things relating to entities and multiblock things + + diff --git a/server/noise.lua b/server/noise.lua index 1480448..8a83b60 100644 --- a/server/noise.lua +++ b/server/noise.lua @@ -1,11 +1,12 @@ local Pos = require"common.coords".Pos +local class = require"common.class" local tau = 2*math.pi math.randomseed(os.time()) local function random_unit_vec() local theta = math.random()*tau - return Pos.make(math.cos(theta),math.sin(theta)) + return Pos:make(math.cos(theta),math.sin(theta)) end local function lerp(a,b,t) return (1-t)*a + t*b end local function smoothstep(x) @@ -15,14 +16,13 @@ local function smoothstep(x) end local function slerp(a,b,t) return lerp(a,b,smoothstep(t)) end -local NoiseGen = {} -NoiseGen.__index = NoiseGen -function NoiseGen.make() +local PerlinNoise = class() +function PerlinNoise.make(cls) local grid = {} setmetatable(grid,{__index=function(t,k) t[k] = {} return t[k] end}) - return setmetatable({grid=grid},NoiseGen) + return setmetatable({grid=grid},cls) end -function NoiseGen.vertex(self,ix,iy) +function PerlinNoise.vertex(self,ix,iy) local v = self.grid[ix][iy] if v then return v end vv = random_unit_vec() @@ -30,7 +30,7 @@ function NoiseGen.vertex(self,ix,iy) return vv end -function NoiseGen.at(self,x,y) +function PerlinNoise.at(self,x,y) local x0 = math.floor(x) local y0 = math.floor(y) local x1 = x0 + 1 @@ -41,7 +41,7 @@ function NoiseGen.at(self,x,y) local v10 = self:vertex(x1,y0) local v11 = self:vertex(x1,y1) - local p = Pos.make + local p = function(...) return Pos:make(...) end local d00 = v00:dot(p(x-x0,y-y0)) local d01 = v01:dot(p(x-x0,y-y1)) local d10 = v10:dot(p(x-x1,y-y0)) @@ -53,17 +53,9 @@ function NoiseGen.at(self,x,y) return z end -local NoiseAgg = {} -NoiseAgg.__index = NoiseAgg -function NoiseAgg.make(things) - return setmetatable({things=things or {}},NoiseAgg) -end -function NoiseAgg.make_perlin_octaves(nocts) - local things = {} - for i=1,nocts do - table.insert(things,{amp=2^i,scale=2^(nocts-i),gen=NoiseGen.make()}) - end - return NoiseAgg.make(things) +local NoiseAgg = class() +function NoiseAgg.make(cls,things) + return setmetatable({things=things or {}},cls) end function NoiseAgg.at(self,x,y) local n = 0 @@ -72,7 +64,7 @@ function NoiseAgg.at(self,x,y) for _,thing in ipairs(self.things) do local gen,scale,amp = thing.gen,thing.scale,thing.amp n = n + amp - t = t + gen:at(x*scale,y*scale)*amp + t = t + gen:at(x/scale,y/scale)*amp end return t/n end @@ -93,4 +85,4 @@ end -- io.write("\n") -- end -return {NoiseGen=NoiseGen,NoiseAgg=NoiseAgg} +return {PerlinNoise=PerlinNoise,NoiseAgg=NoiseAgg} diff --git a/server/server.lua b/server/server.lua index 67860ea..08f75c0 100644 --- a/server/server.lua +++ b/server/server.lua @@ -60,19 +60,42 @@ local function player_move_packet(player,x,y) return json.encode{t="move",id=player.id,x=x,y=y} end + +-- worldgen local the_tiles = {} -local ng = noise.NoiseAgg.make_perlin_octaves(4) -for q = 1,chunk.SIZE-1 do - for r = 1,chunk.SIZE-1 do - local p = coords.Hex.make(q,r):to_pos() - local nv = ng:at(p.x/20,p.y/20) - assert(nv ~= 1,"oopsy") - the_tiles[chunk.index(q,r)] = nv > 0 and 1+math.floor(math.sqrt(nv)*8) or false +local function p(amp,scale) return {scale=scale,amp=amp,gen=noise.PerlinNoise:make()} end +local ng = noise.NoiseAgg:make{ + p(1,20), + -- p(0.7,2), + p(0.5,15), + --p(2,200), +} +local ng2 = noise.NoiseAgg:make{p(1,20),p(0.5,15)} + +for q = 0,chunk.SIZE-1 do + for r = 0,chunk.SIZE-1 do + local p = coords.Hex:make(q,r):to_pos() + local ix = chunk.index(q,r) + local nv = ng:at(p.x,p.y) + if nv <= 0 then + the_tiles[ix] = false + else + local nv2 = ng2:at(p.x,p.y) + nv2 = math.max(-0.9999999,math.min(0.9999999,nv2*2.5)) + nv2 = (nv2+1)/2 + print(nv2) + + local tv = 1+math.floor(nv2*8) + assert(1<=tv and tv<=8,"oopsy woopsy") + the_tiles[ix] = tv + end end end -local the_chunk = Chunk.make(the_tiles) +local the_chunk = Chunk:make(the_tiles) print"generated chunk" + + while true do local ev = host:service(100) if ev then @@ -115,7 +138,7 @@ while true do end end elseif op == "settile" then - local h = coords.Hex.make(j.q,j.r) + local h = coords.Hex:make(j.q,j.r) the_chunk:set_at(h,j.tile) -- print(player.id,"settile",h,j.tile) for i,otherplayer in ipairs(playerlist) do -- cgit v1.2.3