1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
local G = love.graphics
local common = require 'common'
local Camera = require'r.camera'
local Pos = require"r.pos"
local Rect = require'r.rect'
local class = require'r.class'
local enet = require'enet'
local json = require'dkjson'
local rle = require'r.rle'
local print_good = require'r.print_good'
local rmath = require'r.math'
local msgbox = require'r.msgbox'
local utf8 = require'utf8'
local M = {}
local SPEED = 8 -- tiles per second
local colors = {[true]={141/255,128/255,22/255}, [false]={71/255,50/255,122/255}}
local players = {}
local chunks = common.ChunkMap()
local lp = { pos=Pos(0,0), movetimer=0, dir=nil } -- local player
local cam = Camera(nil, 20)
local host, conn
function M.load(_host,_conn,j) host=_host conn=_conn
lp.pos=Pos(j.pos.x,j.pos.y) lp.name=j.name lp.color=j.color end
function M.quit() conn:disconnect() host:flush() end
local show={ui=true,debug=false}
local chatmsg = nil
local chatmsg_text = G.newText(G.getFont())
local function chat_key(k,s,r)
if k == 'escape' then chatmsg = nil
elseif k == 'return' then if #chatmsg>0 then
msgbox.add(lp.name..': '..chatmsg)
conn:send(json.encode{type='chat',msg=chatmsg}) end
chatmsg = nil
elseif k == 'backspace' and #chatmsg>0 then
chatmsg = chatmsg:sub(1,utf8.offset(chatmsg,-1)-1) end end
function M.textinput(str) if chatmsg then chatmsg = chatmsg..str end end
local t = 0
local function smoovement(player) if not player.oldpos then return player.pos end
return rmath.clerp(player.oldpos,player.pos,(t-player.at)*16) end
local function moveplayer(player,newpos)
player.oldpos = player.pos
player.pos = newpos
player.at = t end
local directions = {w=Pos(0,-1),a=Pos(-1,0),s=Pos(0,1),d=Pos(1,0)}
function M.keypressed(k,s,r) if chatmsg then chat_key(k,s,r) else
if directions[s] then
if not love.keyboard.isScancodeDown('lshift') then lp.dir = s
else local tgt = lp.pos + directions[s]
local val = not chunks:tile(tgt); chunks:set_tile(tgt,val)
conn:send(json.encode{type='tile',pos=tgt,tile=val}) end
elseif k=='space' then cam.zoom = cam.zoom == 10 and 20 or 10
elseif k=='tab' then show.ui = not show.ui
elseif k=='return' then chatmsg = '' lp.dir = nil
elseif k=='f3' then show.debug = not show.debug end end end
function M.keyreleased(k,s) if s==lp.dir then lp.dir=nil end end
function lp.update(dt)
lp.movetimer = lp.movetimer - dt
if lp.movetimer <= 0 and lp.dir then
local d = directions[lp.dir] local newpos = lp.pos+d
if chunks:tile(newpos) == false then
conn:send(json.encode{type='move',pos=newpos})
moveplayer(lp,newpos) lp.movetimer = 1 / SPEED end end end
local function draw_player(player,no_label)
local effpos = smoovement(player)
G.setColor(player.color) G.circle('fill',effpos.x,effpos.y,0.3)
if show.ui and not no_label then local f = G.getFont()
local txtw,h = f:getWidth(player.name), f:getHeight()
local centre = cam:world_to_screen(effpos-Pos(0,.4))-Pos(0,h/2)
local bb = Rect:from_centre_dims(centre,txtw,h)
G.push() G.origin() G.setColor(0,0,0,0.4) bb:draw'fill'
G.setColor(1,1,1) G.print(player.name,bb.tl:floor():vals()) G.pop() end end
function lp.draw() draw_player(lp,true) end
function M.update(dt)
t = t + dt lp.update(dt) msgbox.update(dt)
local ev = host and host:service() while ev do
if ev.type == 'receive' then local j = json.decode(ev.data)
local pos if j.pos then pos = Pos(j.pos.x,j.pos.y) end
if j.type == 'player' then
players[j.name] = {name=j.name,pos=pos,color=j.color,at=t}
if t > 1 then msgbox.add(j.name..' joined') end
elseif j.type == 'unplayer' then players[j.from] = nil msgbox.add(j.from..' left')
elseif j.type == 'move' then moveplayer(players[j.from],pos)
elseif j.type == 'chunk' then chunks:add(pos,{d=rle.decode(j.d)})
elseif j.type == 'unchunk' then chunks:remove(pos)
elseif j.type == 'tile' then chunks:set_tile(pos,j.tile)
elseif j.type == 'chat' then msgbox.add(j.from..': '..j.msg)
end
end
ev = host and host:service()
end
end
function M.draw()
G.clear(1,1,1); G.origin()
cam.pos = smoovement(lp); cam:apply_trans()
local tl,br = cam:extents()
tl = tl:floor() br = br:ceil()
for x=tl.x,br.x do for y=tl.y,br.y do
local t = chunks:tile(Pos(x,y))
if t ~= nil then G.setColor(colors[t~=(x<0)]) G.rectangle('fill',x-0.5,y-0.5,1,1) end end end
lp.draw()
local playernames = {}
for _,player in pairs(players) do draw_player(player) table.insert(playernames,player.name) end
table.sort(playernames) playernames = table.concat(playernames,'\n')
G.origin()
if show.ui then msgbox.draw() print_good(playernames,'end',0) end
if chatmsg then
chatmsg_text:setFont(G.getFont()) chatmsg_text:set(lp.name..': '..chatmsg)
local W,H = G.getDimensions() local tw,th = chatmsg_text:getDimensions()
local y = H-th-30
G.setColor(0,0,0,0.8) G.rectangle('fill',0,y,W,th)
G.setColor(1,1,1) G.draw(chatmsg_text,0,y)
G.setColor(.8,.8,.8) G.line(tw,y,tw,y+th)
end
if show.debug then
G.setColor(1,0,0)
G.print(tostring(lp.pos),100,100)
G.setColor(0,0,0,0.8)
G.rectangle('fill',0,0,100,100)
G.setColor(1,1,1)
for cx=-7,7 do for cy=-7,7 do local p=Pos(cx,cy) if chunks:get(p) then
p = (p + Pos(7,7))*10
G.setColor(1,1,1) G.rectangle('fill',p.x,p.y,10,10)
G.setColor(0,0,1) G.rectangle('line',p.x,p.y,10,10)
end end end
local p = (lp.pos/common.SIZE+Pos(7,7))*10
G.setColor(1,0,0) G.rectangle('fill',p.x-1,p.y-1,2,2)
end
end
return M
|