-- some commonly modified settings
dark_theme = true
curr_level = 1
-- levels available:
-- 1-59: Sokoban Jr 1
-- 60-106: Sokoban Jr 2
-- 107-149: Sasquatch
-- 150-187: Sasquatch II
-- 188-226: Sasquatch III
-- 227-267: Microcosmos
-- 268-300: Nabocosmos
-- 301-454: Microban I
-- 455-588: Microban II
-- 589-688: Microban III
-- 689-789: Microban IV
ui_state = {} -- for buttons; recreated each frame
-- level dimensions in cells
lw, lh = nil, nil
-- each cell is a 6x6 square (or multiple thereof)
num_tile_px = 6
-- data structures for current level
level_state = nil -- 2D array of lh*lw sprite ids
crate_id = nil -- 2D array of lh*lw crate ids; only a debugging aid
player = nil -- {x=,y=} coordinate of player in current level
undo_history = {} -- an array of undo states, each an array of {x=,y=,cell=} square states.
pending_moves = {} -- moves already made, but need to be animated
next_pending_move = nil -- timestamp for next frame of animation
crate_to_move = nil
-- sprite ids
CELL_PLAYER = 0
CELL_PLAYER_ON_TARGET = 1
CELL_CRATE = 2
CELL_CRATE_ON_TARGET = 3
CELL_TARGET = 4
CELL_WALL = 5
CELL_GRASS = 6 -- vestigial?
CELL_VACANT = 7
function car.load()
love.keyboard.setTextInput(false)
level_state = load_level(levels[curr_level])
player = player_state(level_state)
crate_id = load_crate_id(level_state)
-- some constants for draw_level_number
if dark_theme then
level_color = {0.8,0.8,0.8}
else
level_color = {0,0,0}
end
level_width = App.width('MMM')+10
end
function load_level(level)
local result = {}
for _,row in ipairs(level) do
local dest = {}
for _,pair in ipairs(row) do
table.insert(dest, floor(pair/16))
table.insert(dest, pair%16)
end
table.insert(result, dest)
end
lw = math.max(#level[1]*2, 8)
lh = math.max(#level, 6)
car.resize()
return result
end
function car.update()
local x, y = love.mouse.getPosition()
if mouse_hover_on_any_button(ui_state, x,y) then
love.mouse.setCursor(Hand_icon)
else
love.mouse.setCursor(Arrow_icon)
end
animate_pending_moves()
end
function car.mouse_press(x,y, b)
if #pending_moves > 0 then
make_all_pending_moves()
return
end
if mouse_press_consumed_by_any_button(ui_state, x,y, b) then
crate_to_move = nil
return
end
if x > left and x < left+lw*side and y > top and y < top+lh*side then
local y, x = 1+floor((y-top)/side), 1+floor((x-left)/side)
if crate_to_move == nil and (level_state[y][x] == CELL_VACANT or level_state[y][x] == CELL_TARGET) then
plan_move_to_empty_space(y, x)
elseif level_state[y][x] == CELL_CRATE or level_state[y][x] == CELL_CRATE_ON_TARGET then
assert(crate_id[y][x])
crate_to_move = {x=x, y=y, id=crate_id[y][x]}
elseif crate_to_move and level_state[y][x] ~= CELL_WALL and level_state[y][x] ~= CELL_CRATE and level_state[y][x] ~= CELL_CRATE_ON_TARGET then
plan_move_crate(y, x)
crate_to_move = nil
end
end
end
function car.keychord_press(chord)
if chord == 'f1' then
stop_app()
elseif chord == 'left' or chord == 'right' or chord == 'up' or chord == 'down' then
move(chord, --[[add to undo]] true)
elseif chord == 'C-z' then
undo_move()
elseif chord == 'C-right' then
next_level()
elseif chord == 'C-left' then
previous_level()
end
end
function move(dir, add_to_undo)
if dir == 'left' then
move_left(add_to_undo)
crate_to_move = nil
elseif dir == 'right' then
move_right(add_to_undo)
crate_to_move = nil
elseif dir == 'up' then
move_up(add_to_undo)
crate_to_move = nil
elseif dir == 'down' then
move_down(add_to_undo)
crate_to_move = nil
end
end
function next_level()
if curr_level >= #levels then return end
curr_level = curr_level+1
level_state = load_level(levels[curr_level])
player = player_state(level_state)
crate_id = load_crate_id(level_state)
undo_history = {}
end
function previous_level()
if curr_level <= 1 then return end
curr_level = curr_level-1
level_state = load_level(levels[curr_level])
player = player_state(level_state)
crate_id = load_crate_id(level_state)
undo_history = {}
end
function player_state(level_state)
for r,row in ipairs(level_state) do
for c,cell in ipairs(row) do
if cell == CELL_PLAYER or cell == CELL_PLAYER_ON_TARGET then
return {x=c, y=r}
end end end end
function load_crate_id(level_state)
local next_crate_id = 1
local result = {}
for r,row in ipairs(level_state) do
local dest = {}
for c,cell in ipairs(row) do
if cell == CELL_CRATE or cell == CELL_CRATE_ON_TARGET then
table.insert(dest, next_crate_id)
next_crate_id = next_crate_id+1
else
table.insert(dest, false)
end
end
table.insert(result, dest)
end
return result
end