Map:
Map = class()
-- creates map of width w, height h, texture, and res (strip width)
-- texture isn't required
function Map:init(w, h, texture, res)
self.w = w
self.h = h
self.texture = texture
-- if texture was given, split into columns to make drawing easier
if texture ~= nil then
self.sprites = {}
for i = 1, texture.width do
self.sprites[i] = col(texture, i)
end
end
self.res = res
-- initialize data, create a default wall around edge of map
-- d contains 1 for wall, else 0
-- bodies contains corresponding physical bodies for collision
self.d = {}
self.bodies = {}
for i = 1, w do
self.d[i] = {}
self.bodies[i] = {}
for j = 1, h do
if i == 1 or i == w or j == 1 or j == h then
self.d[i][j] = 1
self.bodies[i][j] = physics.body(POLYGON, vec2(0, 0), vec2(0, 1), vec2(1, 1), vec2(1, 0))
self.bodies[i][j].type = STATIC
self.bodies[i][j].x = i - 1
self.bodies[i][j].y = j - 1
else
self.d[i][j] = 0
end
end
end
self.cam = nil
end
-- sets d[x][y] to b
function Map:set(x, y, b)
if x > 0 and y > 0 and x <= self.w and y <= self.h then
-- there wasn't a wall here before we have to make body
if b == 1 and self.d[x][y] == 0 then
self.d[x][y] = 1
self.bodies[x][y] = physics.body(POLYGON, vec2(0, 0), vec2(0, 1), vec2(1, 1), vec2(1, 0))
self.bodies[x][y].type = STATIC
self.bodies[x][y].x = x - 1
self.bodies[x][y].y = y - 1
else
-- if there was a wall here before we have to destroy body
if b == 0 and self.d[x][y] == 1 and self.bodies[x][y] then
self.bodies[x][y]:destroy()
self.bodies[x][y] = nil
end
self.d[x][y] = b
end
end
end
-- sets a rectangular section of the map to b
function Map:setRect(x, y, w, h, b)
for i = x, x + w - 1 do
for j = y, y + h - 1 do
self:set(i, j, b)
end
end
end
-- sets map data from image, image must be same size as map,
-- black pixel = 0, white pixel = 1
function Map:setFromImage(i)
for x = 1, self.w do
for y = 1, self.h do
r = i:get(x, y)
if r > 0 then
self:set(x, y, 1)
else
self:set(x, y, 0)
end
end
end
end
-- DOESN'T WORK
-- eventually, will generate a dungeon randomly
-- right now, just uses setRect on random sections
function Map:generate()
newMap = {}
for x = 2, self.w - 1 do
newMap[x] = {}
for y =2, self.h - 1 do
newMap[x][y] = 1
end
end
newMap[2][2] = 0
for i = 1, 200 do
cellX = math.random(2, self.w - 2)
cellY = math.random(2, self.h - 2)
cellWidth = math.random(0, self.w - cellX)
cellHeight = math.random(0, self.h - cellY)
type = math.random(0, 1)
for x = cellX, cellX + cellWidth - 1 do
for y = cellY, cellY + cellHeight - 1 do
newMap[x][y] = type
end
end
end
for x = 2, self.w - 1 do
for y = 2, self.h - 1 do
self:set(x, y, newMap[x][y])
end
end
end
-- sets the active camera
function Map:setCamera(cam)
self.cam = cam
end
-- draws the map
function Map:draw()
fill(255, 255, 255, 255)
noSmooth()
-- iterate through columns of pixels of width self.res
for x = 1, WIDTH, self.res do
-- calculate angle based on column
angle = self.cam.angle + math.atan2(WIDTH / 2 - x, WIDTH / 2)
-- cast ray, t is the relative location of the ray's hit, used
-- to determine what part of the texture should be drawn
dist, wall, t = self:castRay(angle)
-- calculates component of ray's displacement that is perpendicular
-- to view plane to correct fisheye
dist = dist * math.cos(angle - self.cam.angle)
-- calculate height of strip in pixels
height = 0.5 / dist * HEIGHT
if self.texture == nil then
-- if no texture then draw rectangle
-- color based on which wall, for shading
if wall == 1 then fill(255, 255, 255, 255) end
if wall == 2 or wall == 4 then fill(176, 176, 176, 255) end
if wall == 3 then fill(97, 97, 97, 255) end
rect(x, HEIGHT / 2 - height / 2, self.res, height)
else
-- uses t to calculate which column of pixels we should use from texture
-- t is between 0 and 1, 0 if ray hit very edge of wall, 1 if hit other edge
pixelX = math.floor(t * table.getn(self.sprites) + 1)
-- tint based on which wall, for shading
if wall == 1 then tint(255, 255, 255, 255) end
if wall == 2 or wall == 4 then tint(185, 185, 185, 255) end
if wall == 3 then tint(128, 127, 127, 255) end
sprite(self.sprites[pixelX], x + self.res / 2, HEIGHT / 2, self.res, height)
end
end
end
-- casts a ray at ang
-- returns distance, wall hit (1 - 4), and relative location of hit
function Map:castRay(ang)
cosA = math.cos(ang)
sinA = math.sin(ang)
slope = sinA / cosA
-- does ray go left/right? up/down?
hori = 0
vert = 0
if cosA > 0 then hori = 1 else hori = 0 end
if sinA > 0 then vert = 1 else vert = 0 end
x = 0
y = 0
dx = 0
dy = 0
----------------------------------------------
-- first handle vertical walls
if hori == 1 then x = math.ceil(self.cam.x) else x = math.floor(self.cam.x) end
y = self.cam.y + slope * (x - self.cam.x)
if hori == 1 then dx = 1 else dx = -1 end
dy = slope * dx
d1 = 0
h1 = 0
t1 = 0
while x > 0 and x < self.w and y > 0 and y < self.h do
mapX = 0
mapY = 0
if hori == 1 then mapX = math.floor(x) else mapX = math.floor(x) - 1 end
mapY = math.floor(y)
if self.d[mapX + 1][mapY + 1] == 1 then
d1 = (x - self.cam.x) * (x - self.cam.x) + (y - self.cam.y) * (y - self.cam.y)
t1 = y - math.floor(y)
h1 = 1
break
end
x = x + dx
y = y + dy
end
----------------------------------------------
-- then horizontal
if vert == 1 then y = math.ceil(self.cam.y) else y = math.floor(self.cam.y) end
x = self.cam.x + (1 / slope) * (y - self.cam.y)
if vert == 1 then dy = 1 else dy = -1 end
dx = 1 / slope * dy
d2 = 0
h2 = 0
t2 = 0