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
|
-- a Map is a 2d array of chunks
-- it handles loading and unloading of chunks
-- each slot in the Map is either a Chunk object (if that chunk is loaded),
-- nil (if not loaded), or false (if 'loading'). on the client 'loading' means
-- the request for the chunk is currently in flight. on the server it might one day
-- mean that terrain generation for that chunk is currently in progress.
-- to test whether a chunk is loaded you can do if map:chunk(cp) then ... end
-- to test whether a chunk needs to be loaded you do if map:chunk(cp) == nil then ... end.
-- it will probably also do things relating to entities and multiblock things
local class = require"common.class"
local chunk = require"common.chunk"
local coords = require"common.coords"
local CHUNK_SIZE = require"common.constants".CHUNK_SIZE
local Map = class()
function Map.make(cls)
return setmetatable({chunks={}},cls)
end
function Map._set_chunk(self,cp,value)
if not self.chunks[cp.u] then self.chunks[cp.u] = {} end
self.chunks[cp.u][cp.v] = value
end
function Map.add_chunk(self,the_chunk)
local cp = the_chunk.cp
print("adding chunk",cp)
self:_set_chunk(cp,the_chunk)
end
function Map.mark_chunk_loading(self,cp)
print("marking chunk as loading",cp)
self:_set_chunk(cp,false)
end
function Map.remove_chunk(self,cp)
print("removing chunk",cp)
if not self.chunks[cp.u] then return end
self.chunks[cp.u][cp.v] = nil
-- remove list if empty
if next(self.chunks[cp.u]) == nil then self.chunks[cp.u] = nil end
end
function Map.chunk(self,cp)
-- return chunk at chunk coord cp
-- if that chunk isn't loaded return nil
return self:_chunkuv(cp.u,cp.v)
end
function Map._chunkuv(self,u,v)
-- same as above but with numbers instead of objects
-- to avoid allocations inside loop
return self.chunks[u] and self.chunks[u][v]
end
function Map.at(self,hpos)
-- returns tile at world coord hpos
-- if that tile's containing chunk isn't loaded, return nil
-- not using the methods for doing this, in order to avoid lots of allocations
-- inside the main drawing loop
local cpu,cpv = math.floor(hpos.q/CHUNK_SIZE),math.floor(hpos.r/CHUNK_SIZE)
local hoffq,hoffr = hpos.q-(cpu*CHUNK_SIZE), hpos.r-(cpv*CHUNK_SIZE)
local ch = self:_chunkuv(cpu,cpv)
if not ch then return nil end
return ch:_atqr(hoffq,hoffr)
end
function Map.set_at(self,hpos,tile)
local cp,hoffs = hpos:chunk_and_offset()
local ch = self:chunk(cp)
-- setting a tile in an unloaded chunk is silently ignored
-- this might change one day
if not ch then return nil end
ch:set_at(hoffs,tile)
end
function Map.iter_chunks(self)
-- iterates through all cp,chunk pairs
-- chunk might be false
-- not guaranteed to be in any particular order
return coroutine.wrap(function()
for u,t in pairs(self.chunks) do
for v,ch in pairs(t) do
-- if ch is false, won't have a .cp
local cp = coords.ChunkPos:make(u,v)
coroutine.yield(cp,ch)
end
end
end)
end
return {Map=Map}
|