summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathan Korth <nkorth@nkorth.com>2012-02-25 10:34:26 -0500
committerWilliam Wilgus <wilgus.william@gmail.com>2024-04-28 00:21:16 -0400
commit2c7e47fc121014c1218d58248cd7c49cf2f38f0a (patch)
tree1900db6cb35b94dcda6b93f0d2d0ba096979e796
parent3ae48284c1dcca3515785d15444d3dd4000cbfe5 (diff)
downloadrockbox-2c7e47fc12.tar.gz
rockbox-2c7e47fc12.zip
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
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/plugins/SUBDIRS1
-rw-r--r--apps/plugins/picross.lua820
-rw-r--r--apps/plugins/picross/picross.make24
-rwxr-xr-xapps/plugins/picross/picross_default.picross7
-rw-r--r--apps/plugins/picross/rb.picross7
-rwxr-xr-xtools/buildzip.pl11
8 files changed, 872 insertions, 0 deletions
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index 28b8b67c68..db0eb70fc4 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -82,6 +82,7 @@ pacbox,games
pdbox,viewers
pegbox,games
periodic_table,apps
+picross,games
pictureflow,demos
pitch_detector,apps
pitch_screen,viewers
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index edc801362f..ff08074f82 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -116,6 +116,7 @@ metronome.c
/* Lua needs at least 160 KB to work in */
#if PLUGIN_BUFFER_SIZE >= 0x80000
boomshine.lua
+picross.lua
lua_scripts.lua
#ifdef HAVE_LCD_COLOR
pixel-painter.lua
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS
index b9c18b5fd9..7884989d1c 100644
--- a/apps/plugins/SUBDIRS
+++ b/apps/plugins/SUBDIRS
@@ -87,4 +87,5 @@ mpegplayer
#if PLUGIN_BUFFER_SIZE >= 0x80000
lua
lua_scripts
+picross
#endif
diff --git a/apps/plugins/picross.lua b/apps/plugins/picross.lua
new file mode 100644
index 0000000000..26ad57ee74
--- /dev/null
+++ b/apps/plugins/picross.lua
@@ -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
diff --git a/apps/plugins/picross/picross.make b/apps/plugins/picross/picross.make
new file mode 100644
index 0000000000..7e78c0fb1c
--- /dev/null
+++ b/apps/plugins/picross/picross.make
@@ -0,0 +1,24 @@
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+
+LUASCR_SRCDIR := $(APPSDIR)/plugins/picross
+LUASCR_BUILDDIR := $(BUILDDIR)/apps/plugins/picross
+LUASCRS := $(wildcard $(LUASCR_SRCDIR)/*.picross)
+
+#DUMMY := $(info [${LUASCRS}])
+
+DUMMY : all
+
+all: $(subst $(LUASCR_SRCDIR)/,$(LUASCR_BUILDDIR)/,$(LUASCRS))
+
+$(LUASCR_BUILDDIR)/%.picross: $(LUASCR_SRCDIR)/%.picross | $(LUASCR_BUILDDIR)
+ $(call PRINTS,CP $(subst $(LUASCR_SRCDIR)/,,$<))cp $< $@
+
+$(LUASCR_BUILDDIR):
+ $(call PRINTS,MKDIR $@)mkdir -p $(LUASCR_BUILDDIR)/
diff --git a/apps/plugins/picross/picross_default.picross b/apps/plugins/picross/picross_default.picross
new file mode 100755
index 0000000000..ad7643b79e
--- /dev/null
+++ b/apps/plugins/picross/picross_default.picross
@@ -0,0 +1,7 @@
+# simple default puzzle
+
+****..*...*..
+*...*.*...*..
+****..*.*****
+*.....*...*..
+*.....*...*..
diff --git a/apps/plugins/picross/rb.picross b/apps/plugins/picross/rb.picross
new file mode 100644
index 0000000000..bb63b506f5
--- /dev/null
+++ b/apps/plugins/picross/rb.picross
@@ -0,0 +1,7 @@
+#open source jukebox firmware
+
+****...**...***.*..*..............
+*...*.*..*.*....*.*.*.............
+****..*..*.*....**..***...**..*..*
+*..**.*..*.*....*.*.*..*.*..*..**.
+*...*..**...***.*..****...**..*..*
diff --git a/tools/buildzip.pl b/tools/buildzip.pl
index 43c304ff12..2956b49492 100755
--- a/tools/buildzip.pl
+++ b/tools/buildzip.pl
@@ -182,6 +182,13 @@ sub make_install {
#glob_mkdir("$temp_dir/rocks/demos/lua_scripts");
#glob_copy("$ROOT/apps/plugins/lua_scripts/*.lua", "$temp_dir/rocks/demos/lua_scripts/");
}
+ #lua picross puzzles
+ if(-e "$ROOT/apps/plugins/picross") {
+ unless (glob_mkdir("$libdir/rocks/games/picross")) {
+ return 0;
+ }
+ glob_install("$ROOT/apps/plugins/picross/*.picross", "$libdir/rocks/games/picross");
+ }
# all the rest directories
foreach my $t (@userstuff) {
@@ -533,6 +540,10 @@ sub buildzip {
copy("$ROOT/apps/plugins/sokoban.levels", "$temp_dir/rocks/games/sokoban.levels"); # sokoban levels
copy("$ROOT/apps/plugins/snake2.levels", "$temp_dir/rocks/games/snake2.levels"); # snake2 levels
copy("$ROOT/apps/plugins/rockbox-fonts.config", "$temp_dir/rocks/viewers/");
+ # picross files
+ copy("$ROOT/apps/plugins/picross_default.picross", "$temp_dir/rocks/games/picross_default.picross");
+ copy("$ROOT/apps/plugins/bitmaps/native/picross_numbers.bmp",
+ "$temp_dir/rocks/games/picross_numbers.bmp");
}
if(-e "$temp_dir/rocks/demos/pictureflow.rock") {