summaryrefslogtreecommitdiff
path: root/common/map.lua
blob: 67e2834c6aad798e863c7a5640618f70ef7d0d2c (plain)
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
-- 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}