require "map"
local description = [[
Spawnable Points
Sectorizes the map into regions that can be accessed from the spawn point. This will prevent arbitrary spawning.
Usage:
sector = Sector:new("de_cs2d.map",{40,11},{5,11})
sector:visualize() -- Prints out a neat map of the spawnable sectors
sector.solutions -- An array of discrete tiles that can be spawned on
Random Spawn example
sector = Sector:new("maps/"+map("name")+".map", {40, 11}, {-1, 100})
spawntile(player, unpack(sector.solutions[math.random(#sector.solutions)]))
]]
--Utility fn
local kpairs = function (tab, f)
local keys = {}
for key in pairs(tab) do table.insert(keys, key) end
keys = table.sort(keys, f)
local index = 0
local iter = function()
index = index + 1
if keys[index] == nil then
return nil
else
return keys[index], tab[keys[index]]
end
end
return iter
end
--[[Class]] Sector = { --
gradient = {-1,0},
surroundings = {{0,1},{1,0},{0,-1},{-1,0}},
depth = 1000,
once = false,
new = function(self, map, start, gradient, depth, once)
local timeit = os.clock()
local __members__ = {
file = map,
map = Map:new(map),
metadata = {},
start = start,
depth = depth,
once = once,
gradient = gradient,
solutions = {},
obstacles = {},
}
local __meta__ = {__metatable = self, __index = self}
local _return_ = setmetatable(__members__, __meta__)
_return_.obstacles = _return_.map:obstacles() -- Matrix of obstacles
_return_:__init__()
_return_:solve(_return_.start, _return_.depth)
_return_.metadata.loadtime = (os.clock() - timeit) -- Profiling
return _return_
end
,
vadd = function(self,a,b)
return {a[1]+b[1],a[2]+b[2]}
end
,
__init__ = function(self)
if not self.depth then self.depth = Sector.depth end
if not self.once then self.once = Sector.once end
if not self.gradient then self.gradient = Sector.gradient end
end
,
check_tile = function(self, tile)
return self.obstacles[tile[1]][tile[2]]
end
,
probable = function(self, vector)
local _ret = {}
for _,gradient in ipairs(self.surroundings) do
local _vec = self:vadd(vector,gradient)
if self:check_tile(_vec) == 0 then
if not table.ftable(self.solutions, _vec) then
table.insert(_ret,gradient)
end
end
end
return _ret
end
,
project = function(self, current, vector)
local _next = current
while true do
local __next = self:vadd(_next,vector)
if self:check_tile(__next)>0 then break end
_next = self:vadd(_next,vector)
end
return _next
end
,
projections = function(self, current, vectors)
local _ret = {}
for _,vector in ipairs(vectors) do
table.insert(_ret,{self:project(current,vector),vector})
end
return _ret
end
,
dist = function(o,x)
return math.sqrt((o[1]-x[1])^2+(o[2]-x[2])^2)
end
,
weigh = function(self,vectors)
local _ret = {}
for _,vector in ipairs(vectors) do
_ret[self.dist(self.gradient, vector[1])] = vector[2]
end
return _ret
end
,
solve = function(self, current, depth)
if depth < 1 then return end
table.insert(self.solutions, current)
local weight = self:weigh(self:projections(current,self:probable(current)))
for _,vector in kpairs(weight) do
local solution = self:solve(self:vadd(current,vector), depth-1)
if self.once then
break
end
end
end
,
visualize = function(self, mark)
self.map:marks(self.solutions, mark)
end
}
Class(Sector) --[[Class]]--