Mothback/code/level.lua
2022-03-16 14:21:35 -04:00

777 lines
21 KiB
Lua

function loadLevelTiles()
math.randomseed(3)
level_current = "data/levels/level1"
LevelData = dofile(level_current.."/level.lua")
LoadedObjects.Collisions = {}
LoadedObjects.Platforms = {}
LoadedObjects.Ladders = {}
LoadedObjects.Hazards = {}
if type(LevelData.tileset) == "string" then
LevelData.tileset_name = LevelData.tileset
end
LevelData.tileset = tileset[LevelData.tileset_name]
getLevelTileData()
for _, v in ipairs(LevelData.chunks) do
Chunk:new(v[1], v[2])
end
local global_chunk = next(Chunk.all)
global_chunk:load()
LoadedObjects.Collisions = global_chunk.loaded.collisions
LevelTiles = global_chunk.data.tiles
--[[
on level format:
id = tile identifier
depth = order in the render
force = rendering other tile instead of the one in this position
overlay = render another tile id or, if multiple tiles {id, id, id,}, choose at random
overlay_depth = foreground/background overlay depth
type = collision type
]]
updateLevelDimensions()
--
--createTileObjects()
--createRoomObjects()
--getSpawns()
end
function createRoomObjects()
LoadedObjects.Rooms = {}
for _, v in pairs(LevelData.objects.rooms) do
table.insert(LoadedObjects.Rooms, Collision:new(v[1],v[2],v[3],v[4]))
end
end
function getSpawns()
LoadedObjects.Spawns = {}
for _, v in pairs(LevelData.objects.spawns) do
--addSpawn(v[1],unpack(v[2]))
end
end
function expandLevelCanvas(horizontal,vertical)
local horizontal = horizontal or 0
local vertical = vertical or 0
local h = getLevelTileWidth()
local v = getLevelTileHeight()
-- get new canvas size
local newCanvasH = h + math.abs(horizontal)
local newCanvasV = v + math.abs(vertical)
-- lets make a new temporal canvas
local ExpandedLevel = {}
for i = 1, newCanvasV do
ExpandedLevel[i] = {}
for j = 1, newCanvasH do
ExpandedLevel[i][j] = instanceTile(0)
end
end
-- lets guess how the new canvas and positions are offset
local expand_h = 0
if horizontal < 0 then
expand_h = -horizontal
end
local expand_v = 0
if vertical < 0 then
expand_v = -vertical
end
-- get data from old canvas to new canvas
for i = 1, #LevelTiles do
for j = 1, #LevelTiles[i] do
ExpandedLevel[i+expand_v][j+expand_h] = instanceTile(LevelTiles[i][j].id)
end
end
-- use new canvas
LevelTiles = ExpandedLevel
end
function reduceLevelCanvas(horizontal,vertical)
local horizontal = horizontal or 0
local vertical = vertical or 0
local h = getLevelTileWidth()
local v = getLevelTileHeight()
-- get new canvas size
local newCanvasH = h - math.abs(horizontal)
local newCanvasV = v - math.abs(vertical)
-- lets make a new temporal canvas
local ExpandedLevel = {}
for i = 1, newCanvasV do
ExpandedLevel[i] = {}
for j = 1, newCanvasH do
ExpandedLevel[i][j] = instanceTile(0)
end
end
-- lets guess how the new canvas and positions are offset
local expand_h = 0
if horizontal < 0 then
expand_h = -horizontal
end
local expand_v = 0
if vertical < 0 then
expand_v = -vertical
end
-- get data from old canvas to new canvas
for i = 1, #ExpandedLevel do
for j = 1, #ExpandedLevel[i] do
ExpandedLevel[i][j] = instanceTile(LevelTiles[i+expand_v][j+expand_h].id)
end
end
-- use new canvas
LevelTiles = ExpandedLevel
expandLevelCanvas()
end
function getLevelTileData()
TileData = dofile("data/tileset/"..LevelData.tileset_name..".lua")
end
function reloadLevelTiles()
updateLevelDimensions()
end
function updateLevelDimensions()
LevelData.Width = getLevelWidth()
LevelData.Height = getLevelHeight()
end
function getLevelTileHeight()
return #LevelTiles
end
function getLevelTileWidth()
local width = 0
for i = 1, #LevelTiles do
if width < #LevelTiles[i] then width = #LevelTiles[i] end
end
return width
end
function getLevelHeight()
return getLevelTileHeight() * tile_properties.height
end
function getLevelWidth()
return getLevelTileWidth() * tile_properties.width
end
function indexLevelTiles()
TileIndex = {}
local this_tileset = LevelData.tileset
-- index from tileset
local width = this_tileset:getPixelWidth()/tile_properties.width
local height = this_tileset:getPixelHeight()/tile_properties.height
for i = 0, height do
for j = 0, width do
TileIndex[i*width+j+1] = love.graphics.newQuad(
j*tile_properties.width,
i*tile_properties.height,
tile_properties.width,
tile_properties.height,
this_tileset:getDimensions()
)
end
end
initTileData()
-- instance level tiles according to the Properties
for i = 1, #LevelTiles do
for j = 1, #LevelTiles[i] do
setTile(i,j,LevelTiles[i][j])
end
end
end
function initTileData()
for _, Properties in pairs(TileData) do
if Properties.animation ~= nil then
Properties.tileset = love.graphics.newImage("assets/terrain/"..Properties.animation..".png")
Properties.imgs = {}
Properties.current_image = 1
Properties.current_subimage = 1
local tileset = Properties.tileset
local width = tileset:getPixelWidth()/tile_properties.width
local height = tileset:getPixelHeight()/tile_properties.height
local image_count = 0
for i = 0, height-1 do
for j = 0, width-1 do
local quad =
love.graphics.newQuad(
j*tile_properties.width,
i*tile_properties.height,
tile_properties.width,
tile_properties.height,
tileset:getDimensions()
)
image_count = image_count + 1
table.insert(Properties.imgs,quad)
end
end
Properties.image_count = image_count
end
end
end
function instanceTile(id)
local tile = {}
if type(id) == "table" then
id = id.id
end
tile.id = id
local Properties = TileData[tile.id]
if Properties ~= nil then
if type(Properties.overlay) == "table" then
tile.display_overlay = Properties.overlay[math.random(#Properties.overlay)]
else
tile.display_overlay = Properties.overlay
end
if type(Properties.force) == "table" then
tile.display = Properties.force[math.random(#Properties.force)]
else
tile.display = Properties.force
end
end
return tile
end
function setTile(i,j,id)
LevelTiles[i][j] = instanceTile(id)
end
function drawGridDisplay()
for i = 1, #LevelTiles do
for j = 1, #LevelTiles[i] do
love.graphics.rectangle(
"line",
tile_properties.scale * (j * tile_properties.width + (level_properties.offset.x - tile_properties.width)) - Camera.pos.x,
tile_properties.scale * (i * tile_properties.height + (level_properties.offset.y - tile_properties.height)) - Camera.pos.y,
tile_properties.scale * tile_properties.width,
tile_properties.scale * tile_properties.height
)
end
end
end
function optimizeTileObjects(dest)
logPrint("Optimizing Objects...")
local unoptimized = 0
local isTileOptimized = {}
for i = 1, #LevelTiles do
isTileOptimized[i] = {}
for j= 1, #LevelTiles[i] do
isTileOptimized[i][j] = false
end
end
for i = 1, #LevelTiles do
for j = 1, #LevelTiles[i] do
if LevelTiles[i][j].id ~= 0 and TileData[LevelTiles[i][j].id] then
local tile_dat = TileData[LevelTiles[i][j].id]
local type = tile_dat.type
if type == "whole" and not isTileOptimized[i][j] then
isTileOptimized[i][j] = true
local n = 1
local check = true
while check do
check = false
if LevelTiles[i][j+n] ~= nil
and TileData[LevelTiles[i][j+n].id] ~= nil
then
local type_check = TileData[LevelTiles[i][j+n].id].type
if type_check == "whole"
and not isTileOptimized[i][j+n]
then
check = true
isTileOptimized[i][j+n] = true
n = n + 1
end
end
end
local m = 1
local check = true
while check do
check = false
local checkline = true
-- for as long as line, check
for l = 0, n-1 do
checkline = false
if LevelTiles[i+m] ~= nil
and LevelTiles[i+m][j+l] ~= nil
and TileData[LevelTiles[i+m][j+l].id] ~= nil
then
local type_check = TileData[LevelTiles[i+m][j+l].id].type
if type_check == "whole"
and not isTileOptimized[i+m][j+l]
then
checkline = true
else
break
end
else
break
end
end
if checkline then
check = true
for l = 0, n-1 do
isTileOptimized[i+m][j+l] = true
end
m = m + 1
else
break
end
end
logPrint("- Group size: "..m.."x"..n)
unoptimized = unoptimized + m * n
local base_x = tile_properties.scale * j * tile_properties.width + tile_properties.scale * (level_properties.offset.x - tile_properties.height)
local base_y = tile_properties.scale * i * tile_properties.height + tile_properties.scale * (level_properties.offset.y - tile_properties.height)
local col = Collision:new(
base_x,
base_y,
base_x + tile_properties.width * tile_properties.scale * n,
base_y + tile_properties.height * tile_properties.scale * m
)
table.insert(dest,col)
end
end
end
end
--logPrint("collisions optimized from " .. unoptimized .. " to " .. #LoadedObjects.Collisions)
end
-- currently broken
function createTileObjects(dest)
optimizeTileObjects(dest.collisions)
for i = 1, #LevelTiles do
for j = 1, #LevelTiles[i] do
if LevelTiles[i][j].id ~= 0 then
local tile_dat = TileData[LevelTiles[i][j].id] or {}
local type = tile_dat.type
local light = tile_dat.light
local base_x = tile_properties.scale * j * tile_properties.width + tile_properties.scale * (level_properties.offset.x - tile_properties.height)
local base_y = tile_properties.scale * i * tile_properties.height + tile_properties.scale * (level_properties.offset.y - tile_properties.height)
if light ~= 0 and light ~= nil then
CreateLight(
base_x + tile_properties.width/2 * tile_properties.scale,
base_y + tile_properties.height/2 * tile_properties.scale,
light
)
end
local col
local list = dest.collisions
-- wholes are handled in optimization now
--[[if type == "whole" then
local col = Collision:new(
base_x,
base_y,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height * tile_properties.scale
)
table.insert(LoadedObjects.Collisions,col)
else]]if type == "half_bottom" then
col = Collision:new(
base_x,
base_y + tile_properties.height/2 * tile_properties.scale,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height * tile_properties.scale
)
elseif type == "half_top" then
col = Collision:new(
base_x,
base_y ,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height/2 * tile_properties.scale
)
elseif type == "half_right" then
col = Collision:new(
base_x + tile_properties.height/2 * tile_properties.scale,
base_y,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height * tile_properties.scale
)
elseif type == "half_left" then
local col = Collision:new(
base_x,
base_y,
base_x + tile_properties.height/2 * tile_properties.scale,
base_y + tile_properties.height * tile_properties.scale
)
table.insert(LoadedObjects.Collisions,col)
elseif type == "platform" then
local plat = Collision:new(
base_x,
base_y + tile_properties.scale * 2,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height/4 * tile_properties.scale + tile_properties.scale * 2
)
table.insert(LoadedObjects.Platforms,plat)
elseif type == "ramp2_bot_left_whole" then
for k = 1, 8 do
-- do ramp owo
local slope = Collision:new(
base_x,
base_y + k * tile_properties.scale - tile_properties.scale,
base_x + k * 2 * tile_properties.scale,
base_y + k * tile_properties.scale
)
table.insert(LoadedObjects.Collisions,slope)
end
-- fill lower half
local col = Collision:new(
base_x,
base_y + tile_properties.height/2 * tile_properties.scale,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height * tile_properties.scale
)
table.insert(LoadedObjects.Collisions,col)
elseif type == "ramp2_bot_left_half" then
for k = 1, 8 do
-- do ramp owo
local slope = Collision:new(
base_x,
base_y + tile_properties.height/2 * tile_properties.scale + k * tile_properties.scale - tile_properties.scale,
base_x + k * 2 * tile_properties.scale,
base_y + tile_properties.height/2 * tile_properties.scale + k * tile_properties.scale
)
table.insert(LoadedObjects.Collisions,slope)
end
elseif type == "ramp2_top_left_whole" then
for k = 1, 8 do
-- do ramp owo
local slope = Collision:new(
base_x,
base_y + tile_properties.height/2 * tile_properties.scale - tile_properties.scale + k * tile_properties.scale,
base_x + tile_properties.width * tile_properties.scale - (k-1) * 2 * tile_properties.scale,
base_y + tile_properties.height/2 * tile_properties.scale - tile_properties.scale + k * tile_properties.scale + tile_properties.scale
)
table.insert(LoadedObjects.Collisions,slope)
end
-- fill higher half
local col = Collision:new(
base_x,
base_y,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height/2 * tile_properties.scale
)
table.insert(LoadedObjects.Collisions,col)
elseif type == "ramp2_top_left_half" then
for k = 1, 8 do
-- do ramp owo
local slope = Collision:new(
base_x,
base_y - tile_properties.scale + k * tile_properties.scale,
base_x + tile_properties.width * tile_properties.scale - (k-1) * 2 * tile_properties.scale,
base_y - tile_properties.scale + k * tile_properties.scale + tile_properties.scale
)
table.insert(LoadedObjects.Collisions,slope)
end
elseif type == "ramp2_bot_right_whole" then
for k = 1, 8 do
-- do ramp owo
local slope = Collision:new(
base_x + (k-8) * -2 * tile_properties.scale,
base_y - tile_properties.scale + k * tile_properties.scale,
base_x + tile_properties.width * tile_properties.scale,
base_y - tile_properties.scale + k * tile_properties.scale + tile_properties.scale
)
table.insert(LoadedObjects.Collisions,slope)
end
-- fill lower half
local col = Collision:new(
base_x,
base_y + tile_properties.height/2 * tile_properties.scale,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height * tile_properties.scale
)
table.insert(LoadedObjects.Collisions,col)
elseif type == "ramp2_bot_right_half" then
for k = 1, 8 do
-- do ramp owo
local slope = Collision:new(
base_x + (k-8) * -2 * tile_properties.scale,
base_y + tile_properties.height/2 * tile_properties.scale - tile_properties.scale + k * tile_properties.scale,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height/2 * tile_properties.scale - tile_properties.scale + k * tile_properties.scale + tile_properties.scale
)
table.insert(LoadedObjects.Collisions,slope)
end
elseif type == "ramp2_top_right_half" then
for k = 1, 8 do
-- do ramp owo
local slope = Collision:new(
base_x + (k-8) * -2 * tile_properties.scale,
base_y + tile_properties.height/2 * tile_properties.scale - k * tile_properties.scale,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height/2 * tile_properties.scale - k * tile_properties.scale + tile_properties.scale
)
table.insert(LoadedObjects.Collisions,slope)
end
elseif type == "ramp2_top_right_whole" then
for k = 1, 8 do
-- do ramp owo
local slope = Collision:new(
base_x + (k-8) * -2 * tile_properties.scale,
base_y + tile_properties.height/2 * tile_properties.scale + tile_properties.height/2 * tile_properties.scale - k * tile_properties.scale,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height/2 * tile_properties.scale + tile_properties.height/2 * tile_properties.scale - k * tile_properties.scale + tile_properties.scale
)
table.insert(LoadedObjects.Collisions,slope)
end
-- fill higher half
local col = Collision:new(
base_x,
base_y,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height/2 * tile_properties.scale
)
table.insert(LoadedObjects.Collisions,col)
elseif type == "ramp1_bot_left" then
for k = 1, 16 do
-- do ramp owo
local slope = Collision:new(
base_x,
base_y + k * tile_properties.scale - tile_properties.scale,
base_x + k * tile_properties.scale,
base_y + k * tile_properties.scale
)
table.insert(LoadedObjects.Collisions,slope)
end
-- TODO: fix ladders
--[[elseif type == "ladder_right" then
local ladder = Collision:new(
base_x + (tile_properties.width-4)* tile_properties.scale,
base_y,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height * tile_properties.scale
)
table.insert(LoadedObjects.Ladders,ladder)
elseif type == "ladder_platform_right" then
local ladder = Collision:new(
base_x + (tile_properties.width-4)* tile_properties.scale,
base_y + tile_properties.scale * 2,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height * tile_properties.scale
)
table.insert(LoadedObjects.Ladders,ladder)
local plat = Collision:new(
base_x,
base_y + tile_properties.scale * 2,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height/4 * tile_properties.scale + tile_properties.scale * 2
)
table.insert(LoadedObjects.Platforms,plat)
elseif type == "ladder_left" then
local ladder = Collision:new(
base_x,
base_y,
base_x + tile_properties.scale * 4,
base_y + tile_properties.height * tile_properties.scale
)
table.insert(LoadedObjects.Ladders,ladder)
elseif type == "ladder_platform_left" then
local ladder = Collision:new(
base_x,
base_y + tile_properties.scale * 2,
base_x + tile_properties.scale * 4,
base_y + tile_properties.height * tile_properties.scale
)
table.insert(LoadedObjects.Ladders,ladder)
local plat = Collision:new(
base_x,
base_y + tile_properties.scale * 2,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height/4 * tile_properties.scale + tile_properties.scale * 2
)
table.insert(LoadedObjects.Platforms,plat)
]]elseif type == "bottom_hazard" then
local hazard = Collision:new(
base_x,
base_y + tile_properties.height * 12/16 * tile_properties.scale,
base_x + tile_properties.width * tile_properties.scale,
base_y + tile_properties.height * tile_properties.scale
)
list = dest.hazards
end
table.insert(list, col)
end
end
end
--CreateCollisionTable()
end
function animateTiles()
for _, Properties in pairs(TileData) do
if Properties ~= nil then
if Properties.animation ~= nil then
-- calculate subimage
Properties.current_subimage = Properties.current_subimage + current_dt
-- cycle image
if Properties.current_subimage >= Properties.delay then
Properties.current_subimage = Properties.current_subimage - Properties.delay
Properties.current_image = Properties.current_image + 1
end
if Properties.current_image > Properties.image_count then
Properties.current_image = Properties.current_image - Properties.image_count
end
end
end
end
end
function drawTile(tile,x,y,depth)
local Properties = TileData[tile.id]
if Properties ~= nil then
if Properties.animation ~= nil then
if Properties.imgs[Properties.current_image] ~= nil
and Properties.depth == depth
then love.graphics.draw(
Properties.tileset,
Properties.imgs[Properties.current_image],
x,
y,
0,
tile_properties.scale,
tile_properties.scale
)
end
elseif Properties.depth == depth then
if Properties.force ~= nil then
if Properties.force ~= 0 then
love.graphics.draw(
LevelData.tileset,
TileIndex[tile.display],
x,
y,
0,
tile_properties.scale,
tile_properties.scale
)
end
else
love.graphics.draw(
LevelData.tileset,
TileIndex[tile.id],
x,
y,
0,
tile_properties.scale,
tile_properties.scale
)
end
end
if Properties.overlay ~= nil then
if Properties.overlay_depth == depth or Properties.overlay_depth == nil and Properties.depth == depth then
if Properties.overlay_animated then
local overlay_properties = TileData[Properties.overlay]
love.graphics.draw(
overlay_properties.tileset,
overlay_properties.imgs[overlay_properties.current_image],
x,
y,
0,
tile_properties.scale,
tile_properties.scale
)
else
love.graphics.draw(
LevelData.tileset,
TileIndex[tile.display_overlay],
x,
y,
0,
tile_properties.scale,
tile_properties.scale
)
end
end
end
--[[
love.graphics.setColor(0,0,1)
love.graphics.print(tostring(tile.display),x+16,y)
love.graphics.setColor(1,1,1)
]]
end
end