forked from len0rd/rockbox
New plugin: Picross
Picross is a puzzle game also known as Picture Crossword, Nonograms, or Paint By Numbers. See http://en.wikipedia.org/wiki/Nonogram for information on how to play. Update 1: nicer graphics with less images, fixed directory listing, changed how the board works to make a lot of math more sane Update 2: added missing rb.yield to viewPicture loop Update 3: you can now save a game in progress Update 4: fixed a file pointer leak, improved the numbers font Update 5: no images, use vector num draw library add zoom, freedraw -- Bilgus Change-Id: Idc476b46b6eaa10818400fa789701d5bac83467f
This commit is contained in:
parent
3ae48284c1
commit
2c7e47fc12
8 changed files with 872 additions and 0 deletions
820
apps/plugins/picross.lua
Normal file
820
apps/plugins/picross.lua
Normal file
|
@ -0,0 +1,820 @@
|
|||
--[[
|
||||
__________ __ ___.
|
||||
Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
\/ \/ \/ \/ \/
|
||||
|
||||
Port of Picross (aka. Picture Crossword, Nonograms, Paint By Numbers)
|
||||
Copyright (c) 2012 by Nathan Korth
|
||||
|
||||
See http://en.wikipedia.org/wiki/Nonogram for details on how to play, and
|
||||
http://nkorth.com/picross for more puzzles.
|
||||
|
||||
]]--
|
||||
|
||||
require "actions"
|
||||
require "luadir"
|
||||
require("rbsettings")
|
||||
require("settings")
|
||||
|
||||
local _nums = require("draw_num")
|
||||
local _clr = require("color") -- clrset, clrinc provides device independent colors
|
||||
local _lcd = require("lcd") -- lcd helper functions
|
||||
|
||||
local plugindir = rb.PLUGIN_GAMES_DATA_DIR
|
||||
local userdir = plugindir .. "/picross"
|
||||
|
||||
local wrap = rb.settings.read('global_settings', rb.system.global_settings.list_wraparound)
|
||||
wrap = (wrap or 1) == 1
|
||||
|
||||
do -- free up some ram by removing items we don't need
|
||||
local function strip_functions(t, ...)
|
||||
local t_keep = {...}
|
||||
local keep
|
||||
for key, val in pairs(t) do
|
||||
keep = false
|
||||
for _, v in ipairs(t_keep) do
|
||||
if string.find (key, v) then
|
||||
keep = true; break
|
||||
end
|
||||
end
|
||||
if keep ~= true then
|
||||
t[key] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
strip_functions(rb.actions, "PLA_", "TOUCHSCREEN", "_NONE")
|
||||
rb.contexts = nil
|
||||
|
||||
_clr.inc = nil
|
||||
rb.metadata = nil -- remove metadata settings
|
||||
rb.system = nil -- remove system settings
|
||||
rb.settings = nil --remove setting read/write fns
|
||||
end
|
||||
|
||||
--colors for fg/bg ------------------------
|
||||
local WHITE = _clr.set(-1, 255, 255, 255)
|
||||
local BLACK = _clr.set(0, 0, 0, 0)
|
||||
-------------------------------------------
|
||||
|
||||
-- set colors on color targets
|
||||
if rb.lcd_rgbpack ~= nil then
|
||||
rb.lcd_set_background(rb.lcd_rgbpack(255, 255, 255))
|
||||
rb.lcd_set_foreground(rb.lcd_rgbpack(0, 0, 0))
|
||||
end
|
||||
|
||||
local TEXT_COLOR = BLACK
|
||||
|
||||
if rb.LCD_DEPTH == 2 then TEXT_COLOR = bit.bnot(TEXT_COLOR) end
|
||||
|
||||
--[[
|
||||
-- load images
|
||||
local img_numbers = rb.read_bmp_file(rb.current_path().."picross_numbers.bmp")
|
||||
|
||||
-- image helper function
|
||||
function draw_image(img, x, y, w, h, tilenum)
|
||||
|
||||
local func = rb.lcd_bitmap_transparent_part
|
||||
or rb.lcd_bitmap_part -- Fallback version for grayscale targets
|
||||
or rb.lcd_mono_bitmap_part -- Fallback version for mono targets
|
||||
|
||||
func(img, 0, (tilenum * h), w, x, y, w, h)
|
||||
end
|
||||
]]
|
||||
|
||||
function draw_number(x, y, w, tilenum, scale)
|
||||
scale = scale or 1
|
||||
_nums.print(_LCD, tilenum, x, y, w, TEXT_COLOR, nil, nil, true, scale, scale)
|
||||
end
|
||||
|
||||
function showComplete(self)
|
||||
if self:isComplete() then
|
||||
rb.splash(rb.HZ * 2, "Puzzle complete!")
|
||||
self:saveGame()
|
||||
self.puzzleh = 50
|
||||
self.puzzlew = 50
|
||||
local old_boardh, old_boardw = self.boardh, self.boardw
|
||||
local old_numbersw, old_numbersh = self.numbersw, self.numbersh
|
||||
while self.numbersh > 0 do -- remove the number rows
|
||||
table.remove (self.board, 1)
|
||||
self.numbersh = self.numbersh - 1
|
||||
end
|
||||
self.numbersh = 0
|
||||
self.numbersw = 0
|
||||
self.solution = nil
|
||||
self.boardh = self.puzzleh
|
||||
self.boardw = self.puzzlew
|
||||
self.freedraw = 0
|
||||
-- find a free number
|
||||
while rb.file_exists(string.format("%s/user_freedraw_%d.picross",
|
||||
userdir, self.freedraw)) do
|
||||
self.freedraw = self.freedraw + 1
|
||||
end
|
||||
|
||||
for r = 1, self.boardh do
|
||||
local old_row = self.board[r] or {}
|
||||
|
||||
self.board[r] = {}
|
||||
-- copy over the last drawing
|
||||
for c = 1, self.boardw do
|
||||
local ch = old_row[c + old_numbersw]
|
||||
if not ch or ch ~= '*' then
|
||||
self.board[r][c] = '.'
|
||||
else
|
||||
self.board[r][c] = '*'
|
||||
end
|
||||
end
|
||||
end
|
||||
rb.splash(rb.HZ * 3, "Free Draw!")
|
||||
self.cursor.x = 1
|
||||
self.cursor.y = 1
|
||||
self.showComplete = function() end --show once, then remove the reference
|
||||
end
|
||||
end
|
||||
|
||||
local State = {
|
||||
puzzlew = 0,
|
||||
puzzleh = 0,
|
||||
numbersw = 0,
|
||||
numbersh = 0,
|
||||
boardw = 0,
|
||||
boardh = 0,
|
||||
board = {},
|
||||
solution = {},
|
||||
filename = '',
|
||||
cursor = {x = 0, y = 0},
|
||||
scale = 1,
|
||||
freedraw = false
|
||||
}
|
||||
|
||||
--[[
|
||||
|
||||
Notes on how puzzles work in the code:
|
||||
|
||||
The "board" array is bigger than the actual puzzle, so the numbers
|
||||
above and to the left of it can be stored and do not need to be recalculated
|
||||
for every draw. (The "solution" array is the same size as the puzzle, however.)
|
||||
The various width/height variables help keep track of where everything is.
|
||||
(they should be fairly self-explanatory)
|
||||
|
||||
The width/height of the numbers section is always half the width/height of the
|
||||
puzzle. For odd-number-sized puzzles, the value must be rounded up. This is
|
||||
because strings of squares must have at least one blank space in between. For
|
||||
example, on a board 5 wide, the maximum set of row numbers is "1 1 1".
|
||||
|
||||
Here are the values used in the "board" array:
|
||||
' ': empty space
|
||||
'.': unfilled square
|
||||
'*': filled square
|
||||
[number]: number (not a string)
|
||||
|
||||
The .picross puzzle files are text files which look pretty much the same as the
|
||||
"board" array, with two differences:
|
||||
|
||||
- puzzle files should not contain numbers, because they will be generated
|
||||
based on the puzzle at runtime
|
||||
- blank lines and lines starting with '#' are ignored
|
||||
|
||||
]]--
|
||||
|
||||
function State:initBoard()
|
||||
if self.freedraw then
|
||||
-- clear board (set the puzzle area to '.' and everything else to ' ')
|
||||
self.board = {}
|
||||
for r = 1,self.boardh do
|
||||
self.board[r] = {}
|
||||
for c = 1,self.boardw do
|
||||
if r > self.numbersh and c > self.numbersw then
|
||||
self.board[r][c] = '.'
|
||||
else
|
||||
self.board[r][c] = ' '
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- reset cursor
|
||||
self.cursor.x = 1
|
||||
self.cursor.y = 1
|
||||
return
|
||||
end --freedraw
|
||||
|
||||
-- metrics
|
||||
self.puzzleh = #self.solution
|
||||
self.puzzlew = #self.solution[1]
|
||||
self.numbersh = math.floor(self.puzzleh / 2) + 1
|
||||
self.numbersw = math.floor(self.puzzlew / 2) + 1
|
||||
self.boardh = self.puzzleh + self.numbersh
|
||||
self.boardw = self.puzzlew + self.numbersw
|
||||
self.showComplete = showComplete
|
||||
|
||||
-- clear board (set the puzzle area to '.' and everything else to ' ')
|
||||
self.board = {}
|
||||
for r = 1,self.boardh do
|
||||
self.board[r] = {}
|
||||
for c = 1,self.boardw do
|
||||
if r > self.numbersh and c > self.numbersw then
|
||||
self.board[r][c] = '.'
|
||||
else
|
||||
self.board[r][c] = ' '
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- reset cursor
|
||||
self.cursor.x = self.numbersw + 1
|
||||
self.cursor.y = self.numbersh + 1
|
||||
|
||||
-- calculate row numbers
|
||||
local rownums = {}
|
||||
for r = 1,self.puzzleh do
|
||||
rownums[r] = {}
|
||||
local count = 0
|
||||
for c = 1,self.puzzlew do
|
||||
if self.solution[r][c] == '*' then
|
||||
-- filled square
|
||||
count = count + 1
|
||||
else
|
||||
-- empty square
|
||||
if count > 0 then
|
||||
table.insert(rownums[r], count)
|
||||
count = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
-- if there were no empty squares
|
||||
if count > 0 then
|
||||
table.insert(rownums[r], count)
|
||||
count = 0
|
||||
end
|
||||
end
|
||||
|
||||
-- calculate column numbers
|
||||
local columnnums = {}
|
||||
for c = 1,self.puzzlew do
|
||||
columnnums[c] = {}
|
||||
local count = 0
|
||||
for r = 1,self.puzzleh do
|
||||
if self.solution[r][c] == '*' then
|
||||
-- filled square
|
||||
count = count + 1
|
||||
else
|
||||
-- empty square
|
||||
if count > 0 then
|
||||
table.insert(columnnums[c], count)
|
||||
count = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
-- if there were no empty squares
|
||||
if count > 0 then
|
||||
table.insert(columnnums[c], count)
|
||||
count = 0
|
||||
end
|
||||
end
|
||||
|
||||
-- add row numbers to board
|
||||
for r = 1,self.puzzleh do
|
||||
for i,num in ipairs(rownums[r]) do
|
||||
self.board[self.numbersh + r][self.numbersw - #rownums[r] + i] = num
|
||||
end
|
||||
end
|
||||
|
||||
-- add column numbers to board
|
||||
for c = 1,self.puzzlew do
|
||||
for i,num in ipairs(columnnums[c]) do
|
||||
self.board[self.numbersh - #columnnums[c] + i][self.numbersw + c] = num
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function State:saveGame()
|
||||
local file
|
||||
local boardw, boardh = self.boardw, self.boardh
|
||||
|
||||
if self.freedraw then
|
||||
self.filename = string.format("%s/user_freedraw_%d.picross", userdir, self.freedraw)
|
||||
|
||||
|
||||
--remove blank lines from the end
|
||||
while boardh > 1 and not string.find(table.concat(self.board[boardh]), "\*") do
|
||||
boardh = boardh - 1
|
||||
end
|
||||
--remove blank lines from right
|
||||
local max_w = 0
|
||||
for r = self.numbersh + 1, boardh do
|
||||
for c = max_w + 1, boardw do
|
||||
if self.board[r][c] == '*' then
|
||||
max_w = c
|
||||
end
|
||||
end
|
||||
end
|
||||
boardw = max_w
|
||||
if max_w == 0 then return end--nothing to save
|
||||
|
||||
file = io.open(self.filename, 'w')
|
||||
else
|
||||
file = io.open(plugindir .. '/picross.sav', 'w')
|
||||
end
|
||||
|
||||
if file then
|
||||
file:write("#"..self.filename.."\n")
|
||||
for r = self.numbersh + 1, boardh do
|
||||
for c = self.numbersw + 1, boardw do
|
||||
file:write(self.board[r][c])
|
||||
end
|
||||
file:write("\n")
|
||||
end
|
||||
file:close()
|
||||
if self.freedraw then
|
||||
rb.splash(rb.HZ, "Freedraw saved.")
|
||||
else
|
||||
rb.splash(rb.HZ, "Game saved.")
|
||||
end
|
||||
return true
|
||||
else
|
||||
rb.splash(rb.HZ * 2, "Failed to open save file")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function State:loadSave()
|
||||
local file = io.open(plugindir .. '/picross.sav')
|
||||
if file then
|
||||
-- first line is commented path of original puzzle
|
||||
path = file:read('*l')
|
||||
path = path:sub(2,-1)
|
||||
-- prepare original puzzle
|
||||
if self:loadFile(path) then
|
||||
-- load saved board
|
||||
contents = file:read('*all')
|
||||
file:close()
|
||||
local r = 1
|
||||
for line in contents:gmatch("[^\r\n]+") do
|
||||
local c = 1
|
||||
for char in line:gmatch('.') do
|
||||
self.board[self.numbersh + r][self.numbersw + c] = char
|
||||
c = c + 1
|
||||
end
|
||||
r = r + 1
|
||||
end
|
||||
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function State:loadDefault()
|
||||
self:loadFile(userdir .. '/picross_default.picross')
|
||||
end
|
||||
|
||||
function State:loadFile(path)
|
||||
local file = io.open(path)
|
||||
if file then
|
||||
self.freedraw = false
|
||||
local board = {}
|
||||
local boardwidth = 0
|
||||
local count = 0
|
||||
contents = file:read('*all')
|
||||
|
||||
for line in contents:gmatch("[^\r\n]+") do
|
||||
count = count + 1
|
||||
-- ignore blank lines and comments
|
||||
if line ~= '' and line:sub(1, 1) ~= '#' then
|
||||
table.insert(board, {}) -- add a new row
|
||||
|
||||
-- ensure all lines are the same width
|
||||
if boardwidth == 0 then
|
||||
boardwidth = #line
|
||||
elseif #line ~= boardwidth then
|
||||
-- a line was the wrong width
|
||||
local err = "Invalid puzzle file!"
|
||||
local msg =
|
||||
string.format("%s (wrong line width ln: %d w: %d)", err, count, #line)
|
||||
rb.splash(rb.HZ * 2, msg)
|
||||
return false
|
||||
end
|
||||
local pos = 0
|
||||
for char in line:gmatch('.') do
|
||||
pos = pos + 1
|
||||
if char == '*' or char == '.' then
|
||||
table.insert(board[#board], char)
|
||||
else
|
||||
local err = "Invalid puzzle file!"
|
||||
local msg = string.format("%s (invalid character ln: %d '%s' @ %d)",
|
||||
err, count, char, pos)
|
||||
-- invalid character in puzzle area
|
||||
rb.splash(rb.HZ * 2, msg)
|
||||
return false
|
||||
end
|
||||
end
|
||||
else
|
||||
-- display puzzle comments
|
||||
--rb.splash(rb.HZ, line:sub(2,#line))
|
||||
end
|
||||
end
|
||||
|
||||
if #board == 0 then
|
||||
-- empty file
|
||||
rb.splash(rb.HZ * 2, "Invalid puzzle file! (empty)")
|
||||
return false
|
||||
end
|
||||
|
||||
file:close()
|
||||
|
||||
self.solution = board
|
||||
self.filename = path
|
||||
if self.puzzleh < 100 and self.puzzlew < 100 then
|
||||
self:initBoard()
|
||||
return true
|
||||
else
|
||||
-- puzzle too big
|
||||
rb.splash(rb.HZ * 2, "Invalid puzzle file! (too big)")
|
||||
return false
|
||||
end
|
||||
else
|
||||
-- file open failed
|
||||
rb.splash(rb.HZ * 2, "Failed to open file!")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function State:drawBoard()
|
||||
local tw, th = 10 * self.scale, 10 * self.scale -- tile width and height (including bottom+right padding)
|
||||
|
||||
local ofsx = rb.LCD_WIDTH/2 - 4 - (self.cursor.x * tw)
|
||||
local ofsy = rb.LCD_HEIGHT/2 - 4 - (self.cursor.y * th)
|
||||
|
||||
rb.lcd_clear_display()
|
||||
|
||||
-- guide lines
|
||||
for r = 0, self.puzzleh do
|
||||
local x1, x2, y =
|
||||
ofsx + tw - 1,
|
||||
ofsx + ((self.boardw + 1) * tw) - 1,
|
||||
ofsy + ((self.numbersh + 1 + r) * th) - 1
|
||||
if r % 5 == 0 or r == self.puzzleh then
|
||||
rb.lcd_hline(x1, x2, y)
|
||||
else
|
||||
for x = x1, x2, 2 do
|
||||
rb.lcd_drawpixel(x, y)
|
||||
end
|
||||
end
|
||||
end
|
||||
for c = 0, self.puzzlew do
|
||||
local x, y1, y2 =
|
||||
ofsx + ((self.numbersw + 1 + c) * tw) - 1,
|
||||
ofsy + th - 1,
|
||||
ofsy + ((self.boardh + 1) * th) - 1
|
||||
if c % 5 == 0 or c == self.puzzlew then
|
||||
rb.lcd_vline(x, y1, y2)
|
||||
else
|
||||
for y = y1,y2, 2 do
|
||||
rb.lcd_drawpixel(x, y)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- cursor
|
||||
local cx, cy = ofsx + (self.cursor.x * tw) - 1, ofsy + (self.cursor.y * th) - 1
|
||||
rb.lcd_drawrect(cx, cy, tw + 1, th + 1)
|
||||
local n_width = tw / self.scale / 2 - 1
|
||||
local xc = (tw - 5 * self.scale) / 2
|
||||
-- tiles
|
||||
for r = 1, self.boardh do
|
||||
for c = 1, self.boardw do
|
||||
local x, y = ofsx + (c * tw) + 1, ofsy + (r * th) + 1
|
||||
|
||||
if self.board[r][c] == '.' then
|
||||
-- unfilled square
|
||||
elseif self.board[r][c] == '*' then
|
||||
-- filled square
|
||||
rb.lcd_fillrect(x, y, tw - 3, th - 3)
|
||||
elseif self.board[r][c] == 'x' then
|
||||
-- eliminated square
|
||||
rb.lcd_drawline(x + 1, y + 1, x + tw - 5, y + th - 5)
|
||||
rb.lcd_drawline(x + tw - 5, y + 1, x + 1, y + th - 5)
|
||||
elseif self.board[r][c] == ' ' then
|
||||
-- empty space
|
||||
elseif self.board[r][c] > 0 and self.board[r][c] < 100 then
|
||||
-- number
|
||||
local num = self.board[r][c]
|
||||
if num < 10 then
|
||||
draw_number(x + xc, y, n_width, num, self.scale)
|
||||
draw_number(x + xc + 1, y, n_width, num, self.scale)
|
||||
else
|
||||
draw_number(x, y, n_width, num, self.scale)
|
||||
draw_number(x + 1, y, n_width, num, self.scale)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
rb.lcd_update()
|
||||
end
|
||||
|
||||
function State:isComplete()
|
||||
for r = 1,self.puzzleh do
|
||||
for c = 1,self.puzzlew do
|
||||
if self.solution[r][c] == '*' and
|
||||
self.board[self.numbersh + r][self.numbersw + c] ~= '*' then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function State:moveCursor(dir)
|
||||
-- The cursor isn't allowed to move in the top-left quadrant of the board.
|
||||
-- This has to be checked in up and left moves.
|
||||
local in_board_area = (self.cursor.y > (self.numbersh + 1)
|
||||
and self.cursor.x > self.numbersw + 1)
|
||||
|
||||
if dir == 'left' then
|
||||
if (self.cursor.x > (self.numbersw + 1) or self.cursor.y > self.numbersh)
|
||||
and self.cursor.x > 1 then
|
||||
self.cursor.x = self.cursor.x - 1
|
||||
elseif wrap == true then
|
||||
if in_board_area then
|
||||
self.cursor.x = 1
|
||||
else
|
||||
self.cursor.x = self.boardw
|
||||
end
|
||||
dir = 'up'
|
||||
end
|
||||
elseif dir == 'right' then
|
||||
if self.cursor.x < self.boardw then
|
||||
self.cursor.x = self.cursor.x + 1
|
||||
elseif wrap == true then
|
||||
if in_board_area then
|
||||
self.cursor.x = 1
|
||||
else
|
||||
self.cursor.x = self.numbersw + 1
|
||||
end
|
||||
dir = 'down'
|
||||
end
|
||||
end
|
||||
|
||||
if dir == 'up' then
|
||||
if (self.cursor.y > (self.numbersh + 1) or self.cursor.x > self.numbersw)
|
||||
and self.cursor.y > 1 then
|
||||
self.cursor.y = self.cursor.y - 1
|
||||
elseif wrap == true then
|
||||
if in_board_area then
|
||||
self.cursor.y = 1
|
||||
else
|
||||
self.cursor.y = self.boardh
|
||||
end
|
||||
end
|
||||
elseif dir == 'down' then
|
||||
if self.cursor.y < self.boardh then
|
||||
self.cursor.y = self.cursor.y + 1
|
||||
elseif wrap == true then
|
||||
if in_board_area then
|
||||
self.cursor.y = 1
|
||||
else
|
||||
self.cursor.y = self.numbersh + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function State:fillSquare(mode)
|
||||
mode = mode or 0
|
||||
if self.cursor.x > self.numbersw and self.cursor.y > self.numbersh then
|
||||
if self.board[self.cursor.y][self.cursor.x] == '*' and mode ~= 2 then
|
||||
-- clear square
|
||||
self.board[self.cursor.y][self.cursor.x] = '.'
|
||||
elseif mode ~= 1 then -- '.' or 'x'
|
||||
-- fill square
|
||||
local x, y = self.cursor.x - self.numbersw, self.cursor.y - self.numbersh
|
||||
if not self.solution or self.solution[y][x] == '*' then
|
||||
self.board[self.cursor.y][self.cursor.x] = '*'
|
||||
else
|
||||
rb.splash(rb.HZ * 2, "Invalid move!")
|
||||
-- "x" square for convenience
|
||||
self.board[self.cursor.y][self.cursor.x] = 'x'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:showComplete()
|
||||
end
|
||||
|
||||
function State:eliminateSquare()
|
||||
if not self.freedraw
|
||||
and self.cursor.x > self.numbersw
|
||||
and self.cursor.y > self.numbersh then
|
||||
if self.board[self.cursor.y][self.cursor.x] == 'x' then
|
||||
-- clear square
|
||||
self.board[self.cursor.y][self.cursor.x] = '.'
|
||||
else-- '.' or '*'
|
||||
-- "x" square
|
||||
self.board[self.cursor.y][self.cursor.x] = 'x'
|
||||
end
|
||||
else
|
||||
self.board[self.cursor.y][self.cursor.x] = '.'
|
||||
end
|
||||
end
|
||||
|
||||
-- main code ------------------------------------------------------------------
|
||||
|
||||
local function mainMenu()
|
||||
local menu = {
|
||||
"Resume",
|
||||
"View picture",
|
||||
"Restart puzzle",
|
||||
"Load puzzle",
|
||||
"Zoom " .. State.scale - 1,
|
||||
"Save progress",
|
||||
"Save and quit",
|
||||
"Quit without saving"
|
||||
}
|
||||
local start
|
||||
|
||||
if State.freedraw then
|
||||
menu[6] = "Save freedraw " .. State.freedraw --Save Progress
|
||||
end
|
||||
while true do
|
||||
local s = rb.do_menu("Picross", menu, start, false)
|
||||
start = s
|
||||
if s == 0 then
|
||||
-- resume
|
||||
return
|
||||
elseif s == 1 then
|
||||
-- view picture
|
||||
viewPicture()
|
||||
start = 0 --resume
|
||||
elseif s == 2 then
|
||||
-- restart
|
||||
State:initBoard()
|
||||
return
|
||||
elseif s == 3 then
|
||||
-- choose puzzle
|
||||
if puzzleList() then
|
||||
return
|
||||
end
|
||||
elseif s == 4 then
|
||||
-- zoom
|
||||
State.scale = State.scale + 1
|
||||
if State.scale > 4 then
|
||||
State.scale = 1
|
||||
end
|
||||
menu[5] = "Zoom " .. State.scale - 1
|
||||
elseif s == 5 then
|
||||
-- save
|
||||
if State:saveGame() then
|
||||
return
|
||||
end
|
||||
elseif s == 6 then
|
||||
-- save and quit
|
||||
if State:saveGame() then
|
||||
os.exit()
|
||||
end
|
||||
elseif s == 7 then
|
||||
-- quit
|
||||
os.exit()
|
||||
elseif s == -2 then
|
||||
-- back button pressed
|
||||
return
|
||||
else
|
||||
-- something strange happened
|
||||
rb.splash(rb.HZ * 2, "Invalid menu index: "..s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function puzzleList()
|
||||
if rb.dir_exists(userdir) then
|
||||
local files = {}
|
||||
for file in luadir.dir(userdir) do
|
||||
if file ~= '.' and file ~= '..' then
|
||||
table.insert(files, file)
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(files)
|
||||
local udir = userdir .. "/"
|
||||
if #files > 0 then
|
||||
local s = rb.do_menu("Puzzles", files, nil, false)
|
||||
if s >= 0 and s < #files then
|
||||
if State:loadFile(udir..files[s+1]) then
|
||||
return true -- return to puzzle screen
|
||||
else
|
||||
-- puzzle failed to load, return to main menu
|
||||
return false
|
||||
end
|
||||
elseif s == -2 then
|
||||
-- back button pressed, return to main menu
|
||||
return false
|
||||
else
|
||||
-- something strange happened
|
||||
rb.splash(rb.HZ * 2, "Invalid menu index: "..s)
|
||||
return false
|
||||
end
|
||||
else
|
||||
rb.splash(rb.HZ * 2, "No puzzles found! Put .picross files in " .. userdir)
|
||||
return false
|
||||
end
|
||||
else
|
||||
rb.splash(rb.HZ * 2, "Put .picross files in " .. userdir)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function viewPicture()
|
||||
rb.lcd_clear_display()
|
||||
|
||||
-- draw filled squares as pixels (scaled 2x)
|
||||
for r = State.numbersh + 1, State.boardh do
|
||||
for c = State.numbersw + 1, State.boardw do
|
||||
if State.board[r][c] == '*' then
|
||||
--rb.lcd_drawpixel(c - State.numbersw, r - State.numbersh)
|
||||
local px = (c - State.numbersw) * State.scale - State.scale + 1
|
||||
local py = (r - State.numbersh) * State.scale - State.scale + 1
|
||||
|
||||
rb.lcd_fillrect(px, py, State.scale, State.scale)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
rb.lcd_update()
|
||||
|
||||
-- exit on button press
|
||||
while true do
|
||||
local action = rb.get_plugin_action(0)
|
||||
|
||||
if action == rb.actions.PLA_EXIT
|
||||
or action == rb.actions.PLA_CANCEL
|
||||
or action == rb.actions.PLA_SELECT then
|
||||
return
|
||||
end
|
||||
|
||||
rb.yield()
|
||||
end
|
||||
end
|
||||
|
||||
if not State:loadSave() then
|
||||
State:loadDefault()
|
||||
end
|
||||
|
||||
local act = rb.actions
|
||||
local action = act.ACTION_NONE
|
||||
local lockdraw = false
|
||||
|
||||
while true do
|
||||
action = rb.get_plugin_action(0)
|
||||
if action == rb.actions.PLA_EXIT then
|
||||
lockdraw = false
|
||||
mainMenu()
|
||||
elseif action == act.PLA_UP or action == act.PLA_UP_REPEAT then
|
||||
State:moveCursor('up')
|
||||
elseif action == act.PLA_DOWN or action == act.PLA_DOWN_REPEAT then
|
||||
State:moveCursor('down')
|
||||
elseif action == act.PLA_LEFT or action == act.PLA_LEFT_REPEAT then
|
||||
State:moveCursor('left')
|
||||
elseif action == act.PLA_RIGHT or action == act.PLA_RIGHT_REPEAT then
|
||||
State:moveCursor('right')
|
||||
elseif action == act.PLA_SELECT then
|
||||
if lockdraw then
|
||||
lockdraw = lockdraw - 1
|
||||
if lockdraw < 0 then
|
||||
lockdraw = false
|
||||
elseif lockdraw == 1 then
|
||||
rb.splash(50, "clear")
|
||||
else
|
||||
rb.splash(50, "invert")
|
||||
end
|
||||
else
|
||||
State:fillSquare()
|
||||
end
|
||||
action = act.ACTION_NONE
|
||||
elseif action == act.PLA_SELECT_REPEAT then
|
||||
if State.freedraw and not lockdraw then
|
||||
lockdraw = 2
|
||||
rb.splash(50, "draw")
|
||||
action = act.ACTION_NONE
|
||||
end
|
||||
elseif action == act.PLA_CANCEL then
|
||||
State:eliminateSquare()
|
||||
action = act.ACTION_NONE
|
||||
else
|
||||
action = act.ACTION_NONE
|
||||
end
|
||||
|
||||
if lockdraw and action ~= act.ACTION_NONE then
|
||||
State:fillSquare(lockdraw)
|
||||
end
|
||||
|
||||
State:drawBoard()
|
||||
|
||||
rb.yield()
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue