-- a Map is a 2d array of chunks -- it handles loading and unloading of chunks -- each slot in the Map is either a Chunk 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, to avoid many allocations -- inside the main drawing loop local cpu = math.floor(hpos.q/CHUNK_SIZE) local cpv = math.floor(hpos.r/CHUNK_SIZE) local hoffq = hpos.q - (cpu*CHUNK_SIZE) local hoffr = 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}