-- 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.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