A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 822 lines 25 kB view raw
1--[[ 2 __________ __ ___. 3 Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 \/ \/ \/ \/ \/ 8 9Port of Picross (aka. Picture Crossword, Nonograms, Paint By Numbers) 10Copyright (c) 2012 by Nathan Korth 11 12See http://en.wikipedia.org/wiki/Nonogram for details on how to play, and 13http://nkorth.com/picross for more puzzles. 14 15]]-- 16 17require "actions" 18require "luadir" 19require("rbsettings") 20require("settings") 21 22local _nums = require("draw_num") 23local _clr = require("color") -- clrset, clrinc provides device independent colors 24local _lcd = require("lcd") -- lcd helper functions 25 26local plugindir = rb.PLUGIN_GAMES_DATA_DIR 27local userdir = plugindir .. "/.picross" 28 29local wrap = rb.settings.read('global_settings', rb.system.global_settings.list_wraparound) 30wrap = (wrap or 1) == 1 31 32do -- free up some ram by removing items we don't need 33 local function strip_functions(t, ...) 34 local t_keep = {...} 35 local keep 36 for key, val in pairs(t) do 37 keep = false 38 for _, v in ipairs(t_keep) do 39 if string.find (key, v) then 40 keep = true; break 41 end 42 end 43 if keep ~= true then 44 t[key] = nil 45 end 46 end 47 end 48 49 strip_functions(rb.actions, "PLA_", "TOUCHSCREEN", "_NONE") 50 rb.contexts = nil 51 52 _clr.inc = nil 53 rb.metadata = nil -- remove metadata settings 54 rb.system = nil -- remove system settings 55 rb.settings = nil --remove setting read/write fns 56end 57 58--colors for fg/bg ------------------------ 59local WHITE = _clr.set(-1, 255, 255, 255) 60local BLACK = _clr.set(0, 0, 0, 0) 61------------------------------------------- 62 63-- set colors on color targets 64if rb.lcd_rgbpack ~= nil then 65 rb.lcd_set_background(rb.lcd_rgbpack(255, 255, 255)) 66 rb.lcd_set_foreground(rb.lcd_rgbpack(0, 0, 0)) 67end 68 69local TEXT_COLOR = BLACK 70 71if rb.LCD_DEPTH == 2 then TEXT_COLOR = bit.bnot(TEXT_COLOR) end 72 73--[[ 74-- load images 75local img_numbers = rb.read_bmp_file(rb.current_path().."picross_numbers.bmp") 76 77-- image helper function 78function draw_image(img, x, y, w, h, tilenum) 79 80 local func = rb.lcd_bitmap_transparent_part 81 or rb.lcd_bitmap_part -- Fallback version for grayscale targets 82 or rb.lcd_mono_bitmap_part -- Fallback version for mono targets 83 84 func(img, 0, (tilenum * h), w, x, y, w, h) 85end 86]] 87 88function draw_number(x, y, w, tilenum, scale) 89 scale = scale or 1 90 _nums.print(_LCD, tilenum, x, y, w, TEXT_COLOR, nil, nil, true, scale, scale) 91end 92 93function showComplete(self) 94 if self:isComplete() then 95 rb.splash(rb.HZ * 2, "Puzzle complete!") 96 self:saveGame() 97 self.puzzleh = 50 98 self.puzzlew = 50 99 local old_boardh, old_boardw = self.boardh, self.boardw 100 local old_numbersw, old_numbersh = self.numbersw, self.numbersh 101 while self.numbersh > 0 do -- remove the number rows 102 table.remove (self.board, 1) 103 self.numbersh = self.numbersh - 1 104 end 105 self.numbersh = 0 106 self.numbersw = 0 107 self.solution = nil 108 self.boardh = self.puzzleh 109 self.boardw = self.puzzlew 110 self.freedraw = 0 111 -- find a free number 112 while rb.file_exists(string.format("%s/user_freedraw_%d.picross", 113 userdir, self.freedraw)) do 114 self.freedraw = self.freedraw + 1 115 end 116 117 for r = 1, self.boardh do 118 local old_row = self.board[r] or {} 119 120 self.board[r] = {} 121 -- copy over the last drawing 122 for c = 1, self.boardw do 123 local ch = old_row[c + old_numbersw] 124 if not ch or ch ~= '*' then 125 self.board[r][c] = '.' 126 else 127 self.board[r][c] = '*' 128 end 129 end 130 end 131 rb.splash(rb.HZ * 3, "Free Draw!") 132 self.cursor.x = 1 133 self.cursor.y = 1 134 self.showComplete = function() end --show once, then remove the reference 135 end 136end 137 138local State = { 139 puzzlew = 0, 140 puzzleh = 0, 141 numbersw = 0, 142 numbersh = 0, 143 boardw = 0, 144 boardh = 0, 145 board = {}, 146 solution = {}, 147 filename = '', 148 cursor = {x = 0, y = 0}, 149 scale = 1, 150 freedraw = false 151} 152 153--[[ 154 155Notes on how puzzles work in the code: 156 157The "board" array is bigger than the actual puzzle, so the numbers 158above and to the left of it can be stored and do not need to be recalculated 159for every draw. (The "solution" array is the same size as the puzzle, however.) 160The various width/height variables help keep track of where everything is. 161(they should be fairly self-explanatory) 162 163The width/height of the numbers section is always half the width/height of the 164puzzle. For odd-number-sized puzzles, the value must be rounded up. This is 165because strings of squares must have at least one blank space in between. For 166example, on a board 5 wide, the maximum set of row numbers is "1 1 1". 167 168Here are the values used in the "board" array: 169 ' ': empty space 170 '.': unfilled square 171 '*': filled square 172 [number]: number (not a string) 173 174The .picross puzzle files are text files which look pretty much the same as the 175"board" array, with two differences: 176 177 - puzzle files should not contain numbers, because they will be generated 178 based on the puzzle at runtime 179 - blank lines and lines starting with '#' are ignored 180 181]]-- 182 183function State:initBoard() 184 if self.freedraw then 185 -- clear board (set the puzzle area to '.' and everything else to ' ') 186 self.board = {} 187 for r = 1,self.boardh do 188 self.board[r] = {} 189 for c = 1,self.boardw do 190 if r > self.numbersh and c > self.numbersw then 191 self.board[r][c] = '.' 192 else 193 self.board[r][c] = ' ' 194 end 195 end 196 end 197 198 -- reset cursor 199 self.cursor.x = 1 200 self.cursor.y = 1 201 return 202 end --freedraw 203 204 -- metrics 205 self.puzzleh = #self.solution 206 self.puzzlew = #self.solution[1] 207 self.numbersh = math.floor(self.puzzleh / 2) + 1 208 self.numbersw = math.floor(self.puzzlew / 2) + 1 209 self.boardh = self.puzzleh + self.numbersh 210 self.boardw = self.puzzlew + self.numbersw 211 self.showComplete = showComplete 212 213 -- clear board (set the puzzle area to '.' and everything else to ' ') 214 self.board = {} 215 for r = 1,self.boardh do 216 self.board[r] = {} 217 for c = 1,self.boardw do 218 if r > self.numbersh and c > self.numbersw then 219 self.board[r][c] = '.' 220 else 221 self.board[r][c] = ' ' 222 end 223 end 224 end 225 226 -- reset cursor 227 self.cursor.x = self.numbersw + 1 228 self.cursor.y = self.numbersh + 1 229 230 -- calculate row numbers 231 local rownums = {} 232 for r = 1,self.puzzleh do 233 rownums[r] = {} 234 local count = 0 235 for c = 1,self.puzzlew do 236 if self.solution[r][c] == '*' then 237 -- filled square 238 count = count + 1 239 else 240 -- empty square 241 if count > 0 then 242 table.insert(rownums[r], count) 243 count = 0 244 end 245 end 246 end 247 -- if there were no empty squares 248 if count > 0 then 249 table.insert(rownums[r], count) 250 count = 0 251 end 252 end 253 254 -- calculate column numbers 255 local columnnums = {} 256 for c = 1,self.puzzlew do 257 columnnums[c] = {} 258 local count = 0 259 for r = 1,self.puzzleh do 260 if self.solution[r][c] == '*' then 261 -- filled square 262 count = count + 1 263 else 264 -- empty square 265 if count > 0 then 266 table.insert(columnnums[c], count) 267 count = 0 268 end 269 end 270 end 271 -- if there were no empty squares 272 if count > 0 then 273 table.insert(columnnums[c], count) 274 count = 0 275 end 276 end 277 278 -- add row numbers to board 279 for r = 1,self.puzzleh do 280 for i,num in ipairs(rownums[r]) do 281 self.board[self.numbersh + r][self.numbersw - #rownums[r] + i] = num 282 end 283 end 284 285 -- add column numbers to board 286 for c = 1,self.puzzlew do 287 for i,num in ipairs(columnnums[c]) do 288 self.board[self.numbersh - #columnnums[c] + i][self.numbersw + c] = num 289 end 290 end 291end 292 293function State:saveGame() 294 local file 295 local boardw, boardh = self.boardw, self.boardh 296 297 if self.freedraw then 298 self.filename = string.format("%s/user_freedraw_%d.picross", userdir, self.freedraw) 299 300 301 --remove blank lines from the end 302 while boardh > 1 and not string.find(table.concat(self.board[boardh]), "\*") do 303 boardh = boardh - 1 304 end 305 --remove blank lines from right 306 local max_w = 0 307 for r = self.numbersh + 1, boardh do 308 for c = max_w + 1, boardw do 309 if self.board[r][c] == '*' then 310 max_w = c 311 end 312 end 313 end 314 boardw = max_w 315 if max_w == 0 then return end--nothing to save 316 317 file = io.open(self.filename, 'w') 318 else 319 file = io.open(plugindir .. '/picross.sav', 'w') 320 end 321 322 if file then 323 file:write("#"..self.filename.."\n") 324 for r = self.numbersh + 1, boardh do 325 for c = self.numbersw + 1, boardw do 326 file:write(self.board[r][c]) 327 end 328 file:write("\n") 329 end 330 file:close() 331 if self.freedraw then 332 rb.splash(rb.HZ, "Freedraw saved.") 333 else 334 rb.splash(rb.HZ, "Game saved.") 335 end 336 return true 337 else 338 rb.splash(rb.HZ * 2, "Failed to open save file") 339 return false 340 end 341end 342 343function State:loadSave() 344 local file = io.open(plugindir .. '/picross.sav') 345 if file then 346 -- first line is commented path of original puzzle 347 path = file:read('*l') 348 path = path:sub(2,-1) 349 -- prepare original puzzle 350 if self:loadFile(path) then 351 -- load saved board 352 contents = file:read('*all') 353 file:close() 354 local r = 1 355 for line in contents:gmatch("[^\r\n]+") do 356 local c = 1 357 for char in line:gmatch('.') do 358 self.board[self.numbersh + r][self.numbersw + c] = char 359 c = c + 1 360 end 361 r = r + 1 362 end 363 364 return true 365 else 366 return false 367 end 368 else 369 return false 370 end 371end 372 373function State:loadDefault() 374 return self:loadFile(userdir .. '/picross_default.picross') 375end 376 377function State:loadFile(path) 378 local file = io.open(path) 379 if file then 380 self.freedraw = false 381 local board = {} 382 local boardwidth = 0 383 local count = 0 384 contents = file:read('*all') 385 386 for line in contents:gmatch("[^\r\n]+") do 387 count = count + 1 388 -- ignore blank lines and comments 389 if line ~= '' and line:sub(1, 1) ~= '#' then 390 table.insert(board, {}) -- add a new row 391 392 -- ensure all lines are the same width 393 if boardwidth == 0 then 394 boardwidth = #line 395 elseif #line ~= boardwidth then 396 -- a line was the wrong width 397 local err = "Invalid puzzle file!" 398 local msg = 399 string.format("%s (wrong line width ln: %d w: %d)", err, count, #line) 400 rb.splash(rb.HZ * 2, msg) 401 return false 402 end 403 local pos = 0 404 for char in line:gmatch('.') do 405 pos = pos + 1 406 if char == '*' or char == '.' then 407 table.insert(board[#board], char) 408 else 409 local err = "Invalid puzzle file!" 410 local msg = string.format("%s (invalid character ln: %d '%s' @ %d)", 411 err, count, char, pos) 412 -- invalid character in puzzle area 413 rb.splash(rb.HZ * 2, msg) 414 return false 415 end 416 end 417 else 418 -- display puzzle comments 419 --rb.splash(rb.HZ, line:sub(2,#line)) 420 end 421 end 422 423 if #board == 0 then 424 -- empty file 425 rb.splash(rb.HZ * 2, "Invalid puzzle file! (empty)") 426 return false 427 end 428 429 file:close() 430 431 self.solution = board 432 self.filename = path 433 if self.puzzleh < 100 and self.puzzlew < 100 then 434 self:initBoard() 435 return true 436 else 437 -- puzzle too big 438 rb.splash(rb.HZ * 2, "Invalid puzzle file! (too big)") 439 return false 440 end 441 else 442 -- file open failed 443 rb.splash(rb.HZ * 2, "Failed to open file!") 444 return false 445 end 446end 447 448function State:drawBoard() 449 local tw, th = 10 * self.scale, 10 * self.scale -- tile width and height (including bottom+right padding) 450 451 local ofsx = rb.LCD_WIDTH/2 - 4 - (self.cursor.x * tw) 452 local ofsy = rb.LCD_HEIGHT/2 - 4 - (self.cursor.y * th) 453 454 rb.lcd_clear_display() 455 456 -- guide lines 457 for r = 0, self.puzzleh do 458 local x1, x2, y = 459 ofsx + tw - 1, 460 ofsx + ((self.boardw + 1) * tw) - 1, 461 ofsy + ((self.numbersh + 1 + r) * th) - 1 462 if r % 5 == 0 or r == self.puzzleh then 463 rb.lcd_hline(x1, x2, y) 464 else 465 for x = x1, x2, 2 do 466 rb.lcd_drawpixel(x, y) 467 end 468 end 469 end 470 for c = 0, self.puzzlew do 471 local x, y1, y2 = 472 ofsx + ((self.numbersw + 1 + c) * tw) - 1, 473 ofsy + th - 1, 474 ofsy + ((self.boardh + 1) * th) - 1 475 if c % 5 == 0 or c == self.puzzlew then 476 rb.lcd_vline(x, y1, y2) 477 else 478 for y = y1,y2, 2 do 479 rb.lcd_drawpixel(x, y) 480 end 481 end 482 end 483 484 -- cursor 485 local cx, cy = ofsx + (self.cursor.x * tw) - 1, ofsy + (self.cursor.y * th) - 1 486 rb.lcd_drawrect(cx, cy, tw + 1, th + 1) 487 local n_width = tw / self.scale / 2 - 1 488 local xc = (tw - 5 * self.scale) / 2 489 -- tiles 490 for r = 1, self.boardh do 491 for c = 1, self.boardw do 492 local x, y = ofsx + (c * tw) + 1, ofsy + (r * th) + 1 493 494 if self.board[r][c] == '.' then 495 -- unfilled square 496 elseif self.board[r][c] == '*' then 497 -- filled square 498 rb.lcd_fillrect(x, y, tw - 3, th - 3) 499 elseif self.board[r][c] == 'x' then 500 -- eliminated square 501 rb.lcd_drawline(x + 1, y + 1, x + tw - 5, y + th - 5) 502 rb.lcd_drawline(x + tw - 5, y + 1, x + 1, y + th - 5) 503 elseif self.board[r][c] == ' ' then 504 -- empty space 505 elseif self.board[r][c] > 0 and self.board[r][c] < 100 then 506 -- number 507 local num = self.board[r][c] 508 if num < 10 then 509 draw_number(x + xc, y, n_width, num, self.scale) 510 draw_number(x + xc + 1, y, n_width, num, self.scale) 511 else 512 draw_number(x, y, n_width, num, self.scale) 513 draw_number(x + 1, y, n_width, num, self.scale) 514 end 515 end 516 end 517 end 518 519 rb.lcd_update() 520end 521 522function State:isComplete() 523 for r = 1,self.puzzleh do 524 for c = 1,self.puzzlew do 525 if self.solution[r][c] == '*' and 526 self.board[self.numbersh + r][self.numbersw + c] ~= '*' then 527 return false 528 end 529 end 530 end 531 532 return true 533end 534 535function State:moveCursor(dir) 536 -- The cursor isn't allowed to move in the top-left quadrant of the board. 537 -- This has to be checked in up and left moves. 538 local in_board_area = (self.cursor.y > (self.numbersh + 1) 539 and self.cursor.x > self.numbersw + 1) 540 541 if dir == 'left' then 542 if (self.cursor.x > (self.numbersw + 1) or self.cursor.y > self.numbersh) 543 and self.cursor.x > 1 then 544 self.cursor.x = self.cursor.x - 1 545 elseif wrap == true then 546 if in_board_area then 547 self.cursor.x = 1 548 else 549 self.cursor.x = self.boardw 550 end 551 dir = 'up' 552 end 553 elseif dir == 'right' then 554 if self.cursor.x < self.boardw then 555 self.cursor.x = self.cursor.x + 1 556 elseif wrap == true then 557 if in_board_area then 558 self.cursor.x = 1 559 else 560 self.cursor.x = self.numbersw + 1 561 end 562 dir = 'down' 563 end 564 end 565 566 if dir == 'up' then 567 if (self.cursor.y > (self.numbersh + 1) or self.cursor.x > self.numbersw) 568 and self.cursor.y > 1 then 569 self.cursor.y = self.cursor.y - 1 570 elseif wrap == true then 571 if in_board_area then 572 self.cursor.y = 1 573 else 574 self.cursor.y = self.boardh 575 end 576 end 577 elseif dir == 'down' then 578 if self.cursor.y < self.boardh then 579 self.cursor.y = self.cursor.y + 1 580 elseif wrap == true then 581 if in_board_area then 582 self.cursor.y = 1 583 else 584 self.cursor.y = self.numbersh + 1 585 end 586 end 587 end 588end 589 590function State:fillSquare(mode) 591 mode = mode or 0 592 if self.cursor.x > self.numbersw and self.cursor.y > self.numbersh then 593 if self.board[self.cursor.y][self.cursor.x] == '*' and mode ~= 2 then 594 -- clear square 595 self.board[self.cursor.y][self.cursor.x] = '.' 596 elseif mode ~= 1 then -- '.' or 'x' 597 -- fill square 598 local x, y = self.cursor.x - self.numbersw, self.cursor.y - self.numbersh 599 if not self.solution or self.solution[y][x] == '*' then 600 self.board[self.cursor.y][self.cursor.x] = '*' 601 else 602 rb.splash(rb.HZ * 2, "Invalid move!") 603 -- "x" square for convenience 604 self.board[self.cursor.y][self.cursor.x] = 'x' 605 end 606 end 607 end 608 609 self:showComplete() 610end 611 612function State:eliminateSquare() 613 if not self.freedraw 614 and self.cursor.x > self.numbersw 615 and self.cursor.y > self.numbersh then 616 if self.board[self.cursor.y][self.cursor.x] == 'x' then 617 -- clear square 618 self.board[self.cursor.y][self.cursor.x] = '.' 619 else-- '.' or '*' 620 -- "x" square 621 self.board[self.cursor.y][self.cursor.x] = 'x' 622 end 623 else 624 self.board[self.cursor.y][self.cursor.x] = '.' 625 end 626end 627 628-- main code ------------------------------------------------------------------ 629 630local function mainMenu() 631 local menu = { 632 "Resume", 633 "View picture", 634 "Restart puzzle", 635 "Load puzzle", 636 "Zoom " .. State.scale - 1, 637 "Save progress", 638 "Save and quit", 639 "Quit without saving" 640 } 641 local start 642 643 if State.freedraw then 644 menu[6] = "Save freedraw " .. State.freedraw --Save Progress 645 end 646 while true do 647 local s = rb.do_menu("Picross", menu, start, false) 648 start = s 649 if s == 0 then 650 -- resume 651 return 652 elseif s == 1 then 653 -- view picture 654 viewPicture() 655 start = 0 --resume 656 elseif s == 2 then 657 -- restart 658 State:initBoard() 659 return 660 elseif s == 3 then 661 -- choose puzzle 662 if puzzleList() then 663 return 664 end 665 elseif s == 4 then 666 -- zoom 667 State.scale = State.scale + 1 668 if State.scale > 4 then 669 State.scale = 1 670 end 671 menu[5] = "Zoom " .. State.scale - 1 672 elseif s == 5 then 673 -- save 674 if State:saveGame() then 675 return 676 end 677 elseif s == 6 then 678 -- save and quit 679 if State:saveGame() then 680 os.exit() 681 end 682 elseif s == 7 then 683 -- quit 684 os.exit() 685 elseif s == -2 then 686 -- back button pressed 687 return 688 else 689 -- something strange happened 690 rb.splash(rb.HZ * 2, "Invalid menu index: "..s) 691 end 692 end 693end 694 695function puzzleList() 696 if rb.dir_exists(userdir) then 697 local files = {} 698 for file in luadir.dir(userdir) do 699 if file ~= '.' and file ~= '..' then 700 table.insert(files, file) 701 end 702 end 703 704 table.sort(files) 705 local udir = userdir .. "/" 706 if #files > 0 then 707 local s = rb.do_menu("Puzzles", files, nil, false) 708 if s >= 0 and s < #files then 709 if State:loadFile(udir..files[s+1]) then 710 return true -- return to puzzle screen 711 else 712 -- puzzle failed to load, return to main menu 713 return false 714 end 715 elseif s == -2 then 716 -- back button pressed, return to main menu 717 return false 718 else 719 -- something strange happened 720 rb.splash(rb.HZ * 2, "Invalid menu index: "..s) 721 return false 722 end 723 else 724 rb.splash(rb.HZ * 2, "No puzzles found! Put .picross files in " .. userdir) 725 return false 726 end 727 else 728 rb.splash(rb.HZ * 2, "Put .picross files in " .. userdir) 729 return false 730 end 731end 732 733function viewPicture() 734 rb.lcd_clear_display() 735 736 -- draw filled squares as pixels (scaled 2x) 737 for r = State.numbersh + 1, State.boardh do 738 for c = State.numbersw + 1, State.boardw do 739 if State.board[r][c] == '*' then 740 --rb.lcd_drawpixel(c - State.numbersw, r - State.numbersh) 741 local px = (c - State.numbersw) * State.scale - State.scale + 1 742 local py = (r - State.numbersh) * State.scale - State.scale + 1 743 744 rb.lcd_fillrect(px, py, State.scale, State.scale) 745 end 746 end 747 end 748 749 rb.lcd_update() 750 751 -- exit on button press 752 while true do 753 local action = rb.get_plugin_action(0) 754 755 if action == rb.actions.PLA_EXIT 756 or action == rb.actions.PLA_CANCEL 757 or action == rb.actions.PLA_SELECT then 758 return 759 end 760 761 rb.yield() 762 end 763end 764 765if not State:loadSave() then 766 if not State:loadDefault() then 767 return; 768 end 769end 770 771local act = rb.actions 772local action = act.ACTION_NONE 773local lockdraw = false 774 775while true do 776 action = rb.get_plugin_action(0) 777 if action == rb.actions.PLA_EXIT then 778 lockdraw = false 779 mainMenu() 780 elseif action == act.PLA_UP or action == act.PLA_UP_REPEAT then 781 State:moveCursor('up') 782 elseif action == act.PLA_DOWN or action == act.PLA_DOWN_REPEAT then 783 State:moveCursor('down') 784 elseif action == act.PLA_LEFT or action == act.PLA_LEFT_REPEAT then 785 State:moveCursor('left') 786 elseif action == act.PLA_RIGHT or action == act.PLA_RIGHT_REPEAT then 787 State:moveCursor('right') 788 elseif action == act.PLA_SELECT then 789 if lockdraw then 790 lockdraw = lockdraw - 1 791 if lockdraw < 0 then 792 lockdraw = false 793 elseif lockdraw == 1 then 794 rb.splash(50, "clear") 795 else 796 rb.splash(50, "invert") 797 end 798 else 799 State:fillSquare() 800 end 801 action = act.ACTION_NONE 802 elseif action == act.PLA_SELECT_REPEAT then 803 if State.freedraw and not lockdraw then 804 lockdraw = 2 805 rb.splash(50, "draw") 806 action = act.ACTION_NONE 807 end 808 elseif action == act.PLA_CANCEL then 809 State:eliminateSquare() 810 action = act.ACTION_NONE 811 else 812 action = act.ACTION_NONE 813 end 814 815 if lockdraw and action ~= act.ACTION_NONE then 816 State:fillSquare(lockdraw) 817 end 818 819 State:drawBoard() 820 821 rb.yield() 822end