-- 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 -- note that the Map never creates any Chunks itself, which means it should be agnostic -- to whatever actual Chunk class is being used (ChunkC or ChunkS or whatever) local class = require"common.class" 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 self:_set_chunk(cp,the_chunk) end function Map.mark_chunk_loading(self,cp) self:_set_chunk(cp,false) end function Map.remove_chunk(self,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}