1-- Breakout for Golly 2-- Author: Chris Rowett (crowett@gmail.com), November 2016 3-- Use F12 to save a screenshot 4 5local build = 84 6local g = golly() 7-- require "gplus.strict" 8local gp = require "gplus" 9local split = gp.split 10local op = require "oplus" 11local ov = g.overlay 12local ovt = g.ovtable 13local rand = math.random 14local maketext = op.maketext 15local pastetext = op.pastetext 16 17math.randomseed(os.time()) -- init seed for math.random 18 19-- text alignment 20local text = { 21 alignleft = 0, 22 aligncenter = 1, 23 alignright = 2, 24 aligntop = 3, 25 alignbottom = 4, 26 fontscale = 1 27} 28 29-- overlay width and height 30local wd, ht = g.getview(g.getlayer()) 31local minwd = 400 32local minht = 400 33local edgegapl = 0 34local edgegapr = 0 35 36-- background settings 37local bgclip = "bg" 38 39-- shadow settings 40local shadow = { 41 x = -wd // 100, 42 y = ht // 100, 43 alpha = 128, -- default alpha for shadows 44 delta = 8, -- brick fade rate when hit 45 txtx = -2, 46 txty = 2, 47} 48shadow.color = {"rgba", 0, 0, 0, shadow.alpha} 49shadow.fadecolor = {"rgba", 0, 0, 0, shadow.alpha} 50 51-- colors as tables 52local colors = { 53 white = {"rgba", 255, 255, 255, 255}, 54 black = {"rgba", 0, 0, 0, 255}, 55 red = {"rgba", 255, 0, 0, 255}, 56 green = {"rgba", 0, 255, 0, 255}, 57 blue = {"rgba", 0, 0, 255, 255}, 58 cyan = {"rgba", 0, 255, 255, 255}, 59 magenta = {"rgba", 255, 0, 255, 255}, 60 yellow = {"rgba", 255, 255, 0, 255} 61} 62 63-- brick settings 64local brick = { 65 numrows = 6, 66 numcols = 20, 67 rows = {}, 68 wd, 69 ht = ht // 40, 70 maxoffsety = 20, 71 offsety = 0, 72 startoffset = 0, 73 movedown = 0, 74 movesteps = 24, 75 cols = { 76 [1] = colors.red, 77 [2] = colors.yellow, 78 [3] = colors.magenta, 79 [4] = colors.green, 80 [5] = colors.cyan, 81 [6] = colors.blue 82 }, 83 bricksleft = 0, 84 totalbricks = 0, 85 x = 0, 86 y = 0, 87 fading = {}, 88 fadecols = {} 89} 90brick.wd = wd // brick.numcols 91brick.bricksleft = brick.numrows * brick.numcols 92brick.totalbricks = brick.bricksleft 93 94-- bat settings 95local bat = { 96 x = 0, 97 y = 0, 98 wd = wd // 10, 99 ht = brick.ht, 100 lastx = 0, 101 fade = 128 102} 103 104-- ball settings 105local ball = { 106 size = wd // 80, 107 x = 0, 108 y = 0, 109 numsteps = 80 110} 111 112-- particle settings 113local particle = { 114 particles = {}, 115 brickparticles = brick.wd * brick.ht // 10, 116 ballparticles = 1, 117 ballpartchance = 0.25, 118 wallparticles = 20, 119 batparticles = 20, 120 highparticles = 4, 121 comboparticles = 4, 122 bonusparticles = 4, 123 lostparticles = 1024, 124 bonusparticlesg = 6, 125 bonusparticlesy = 3 126} 127 128-- points settings 129local points = {} 130 131-- game settings 132local game = { 133 level = 1, 134 newball = true, 135 pause = false, 136 hiscore = 0, 137 score = 0, 138 combo = 1, 139 combomult = 1, 140 combofact = 1.04, 141 comboraw = 0, 142 comboextra = 0, 143 gamecombo = 1, 144 maxcombo = 2, 145 balls = 3, 146 newhigh = false, 147 newcombo = false, 148 newbonus = false, 149 offoverlay = false, 150 finished = false, 151 again = true 152} 153 154-- timing settings 155local timing = { 156 times = {}, 157 timenum = 1, 158 numtimes = 8, 159 framemult = 1, 160 framecap = 100, 161 sixtyhz = 16.7 162} 163 164-- game options 165local options = { 166 brickscore = 1, 167 showtiming = 0, 168 showparticles = 1, 169 autopause = 0, 170 autostart = 0, 171 showmouse = 1, 172 showshadows = 1, 173 confirmquit = 0, 174 showoptions = false, 175 confirming = false, 176 comboscore = 1, 177 soundvol = 1, 178 musicvol = 1, 179 fullscreen = 0 180} 181 182-- settings are saved in this file 183local settingsfile = g.getdir("data").."breakout.ini" 184 185-- notifications 186local notification = { 187 duration = 300, 188 trans = 20, 189 current = 0, 190 message = "" 191} 192 193-- bonus level 194local bonus = { 195 bricks = { 196 805203, 197 699732, 198 830290, 199 698705, 200 698705, 201 805158 202 }, 203 level = false, 204 interval = 3, 205 time = 60, 206 current = 0, 207 green = 10, 208 yellow = 20, 209 best = 0 210} 211 212-- key highlight color and names 213local keys = "keys" 214local buttons = "buttons" 215local keycolor = {"rgba", 32, 32, 32, 255} 216local mousecolor = {"rgba", 48, 0, 0, 255} 217local keynames = { 218 [keys] = { "Esc", "Tab", "Enter" }, 219 [buttons] = { "Click", "Right Click", "Mouse" } 220} 221local keycols = { 222 [keys] = keycolor, 223 [buttons] = mousecolor 224} 225 226-- static messages and clip names 227local optcol = {"rgba", 192, 192, 192, 255} 228local messages = { 229 ["gameover"] = { text = "Game Over", size = 30, color = colors.red }, 230 ["newball"] = { text = "Click or Enter to launch ball", size = 10, color = colors.white }, 231 ["control"] = { text = "Mouse to move bat", size = 10, color = colors.white }, 232 ["askquit"] = { text = "Quit Game?", size = 15, color = colors.yellow }, 233 ["pause"] = { text = "Paused", size = 15, color = colors.yellow }, 234 ["askleft"] = { text = "Click or Enter to Confirm", size = 10, color = colors.white }, 235 ["askright"] = { text = "Right Click to Cancel", size = 10, color = colors.white }, 236 ["resume"] = { text = "Click or Enter to continue", size = 10, color = colors.white }, 237 ["focus"] = { text = "Move mouse onto game window to continue", size = 10, color = colors.white }, 238 ["manfocus"] = { text = "Move mouse onto game window and", size = 10, color = colors.white }, 239 ["quitgame"] = { text = "Right Click to quit game", size = 10, color = colors.white }, 240 ["option"] = { text = "Tab for Game Settings", size = 10, color = colors.white }, 241 ["restart"] = { text = "Click or Enter to start again", size = 10, color = colors.white }, 242 ["quit"] = { text = "Right Click or Esc to exit", size = 10, color = colors.white }, 243 ["continue"] = { text = "Click or Enter for next level", size = 10, color = colors.white }, 244 ["newhigh"] = { text = "New High Score!", size = 10, color = colors.green }, 245 ["newcombo"] = { text = "New Best Combo!", size = 10, color = colors.green }, 246 ["newbonus"] = { text = "New Best Bonus!", size = 10, color = colors.green }, 247 ["close"] = { text = "Click or Tab to close Game Settings", size = 10, color = colors.white }, 248 ["autopause"] = { text = "Autopause", size = 10, color = optcol }, 249 ["brickscore"] = { text = "Brick Score", size = 10, color = optcol }, 250 ["comboscore"] = { text = "Combo Score", size = 10, color = optcol }, 251 ["shadows"] = { text = "Shadows", size = 10, color = optcol }, 252 ["mouse"] = { text = "Mouse Pointer", size = 10, color = optcol }, 253 ["particles"] = { text = "Particles", size = 10, color = optcol }, 254 ["confirm"] = { text = "Confirm Quit", size = 10, color = optcol }, 255 ["autostart"] = { text = "Autostart", size = 10, color = optcol }, 256 ["timing"] = { text = "Timing", size = 10, color = optcol }, 257 ["fullscreen"] = { text = "Fullscreen", size = 10, color = optcol }, 258 ["function"] = { text = "Function", size = 10, color = colors.white }, 259 ["on"] = { text = "On", size = 10, color = colors.green }, 260 ["off"] = { text = "Off", size = 10, color = colors.red }, 261 ["state"] = { text = "State", size = 10, color = colors.white }, 262 ["key"] = { text = "Key", size = 10, color = colors.white }, 263 ["a"] = { text = "A", size = 10, color = optcol }, 264 ["b"] = { text = "B", size = 10, color = optcol }, 265 ["c"] = { text = "C", size = 10, color = optcol }, 266 ["d"] = { text = "D", size = 10, color = optcol }, 267 ["m"] = { text = "M", size = 10, color = optcol }, 268 ["p"] = { text = "P", size = 10, color = optcol }, 269 ["q"] = { text = "Q", size = 10, color = optcol }, 270 ["s"] = { text = "S", size = 10, color = optcol }, 271 ["t"] = { text = "T", size = 10, color = optcol }, 272 ["f11"] = { text = "F11", size = 10, color = optcol }, 273 ["-"] = { text = "-", size = 10, color = optcol }, 274 ["="] = { text = "+", size = 10, color = optcol }, 275 ["["] = { text = "[", size = 10, color = optcol }, 276 ["]"] = { text = "]", size = 10, color = optcol }, 277 ["sound"] = { text = "Sound Volume", size = 10, color = optcol }, 278 ["music"] = { text = "Music Volume", size = 10, color = optcol }, 279 ["fxvol"] = { text = "100%", size = 10, color = colors.green }, 280 ["musicvol"] = { text = "100%", size = 10, color = colors.green }, 281 ["level"] = { text = "Level ", size = 15, color = colors.white }, 282 ["bonus"] = { text = "Bonus Level", size = 15, color = colors.white }, 283 ["bcomplete"] = { text = "Bonus Level Complete", size = 15, color = colors.white }, 284 ["remain"] = { text = "Bricks left", size = 10, color = colors.green }, 285 ["time"] = { text = "Time", size = 15, color = colors.green }, 286 ["left"] = { text = "3 balls left", size = 15, color = colors.yellow }, 287 ["score"] = { text = "Score", size = 10, color = colors.white }, 288 ["high"] = { text = "High Score", size = 10, color = colors.white }, 289 ["balls"] = { text = "Balls", size = 10, color = colors.white }, 290 ["combo"] = { text = "Combo", size = 10, color = colors.white }, 291 ["notify"] = { text = "Notify", size = 7, color = colors.white }, 292 ["ms"] = { text = "1 ms", size = 7, color = colors.white }, 293 ["complete"] = { text = "Level Complete", size = 20, color = colors.green }, 294 ["awarded"] = { text = "No Bonus", size = 15, color = colors.red } 295} 296 297-- music 298local music = { 299 currenttrack = "", 300 fade = 1, 301 faderate = -0.01, 302 gameovertime = 1000 * 64, 303 folder = "oplus/sounds/breakout/" 304} 305 306-------------------------------------------------------------------------------- 307 308local function showcursor() 309 if options.showmouse == 0 then 310 ov("cursor hidden") 311 else 312 ov("cursor arrow") 313 end 314end 315 316-------------------------------------------------------------------------------- 317 318local function setfullscreen() 319 g.setoption("fullscreen", options.fullscreen) 320 showcursor() 321end 322 323-------------------------------------------------------------------------------- 324 325local function readsettings() 326 local f = io.open(settingsfile, "r") 327 if f then 328 game.hiscore = tonumber(f:read("*l")) or 0 329 options.fullscreen = tonumber(f:read("*l")) or 0 330 options.showtiming = tonumber(f:read("*l")) or 0 331 options.showparticles = tonumber(f:read("*l")) or 1 332 options.autopause = tonumber(f:read("*l")) or 0 333 options.autostart = tonumber(f:read("*l")) or 0 334 options.showmouse = tonumber(f:read("*l")) or 1 335 options.showshadows = tonumber(f:read("*l")) or 1 336 game.maxcombo = tonumber(f:read("*l")) or 2 337 options.brickscore = tonumber(f:read("*l")) or 1 338 options.confirmquit = tonumber(f:read("*l")) or 1 339 bonus.best = tonumber(f:read("*l")) or 0 340 options.comboscore = tonumber(f:read("*l")) or 1 341 options.soundvol = tonumber(f:read("*l")) or 100 342 options.musicvol = tonumber(f:read("*l")) or 70 343 f:close() 344 345 if options.soundvol == 1 then 346 options.soundvol = 100 347 end 348 if options.musicvol == 1 then 349 options.musicvol = 100 350 end 351 end 352end 353 354-------------------------------------------------------------------------------- 355 356local function writesettings() 357 local f = io.open(settingsfile, "w") 358 if f then 359 f:write(tostring(game.hiscore).."\n") 360 f:write(tostring(options.fullscreen).."\n") 361 f:write(tostring(options.showtiming).."\n") 362 f:write(tostring(options.showparticles).."\n") 363 f:write(tostring(options.autopause).."\n") 364 f:write(tostring(options.autostart).."\n") 365 f:write(tostring(options.showmouse).."\n") 366 f:write(tostring(options.showshadows).."\n") 367 f:write(tostring(game.maxcombo).."\n") 368 f:write(tostring(options.brickscore).."\n") 369 f:write(tostring(options.confirmquit).."\n") 370 f:write(tostring(bonus.best).."\n") 371 f:write(tostring(options.comboscore).."\n") 372 f:write(tostring(options.soundvol).."\n") 373 f:write(tostring(options.musicvol).."\n") 374 f:close() 375 end 376end 377 378-------------------------------------------------------------------------------- 379 380local function updatemessage(name, s, color) 381 -- lookup the message 382 local message = messages[name] 383 if color ~= nil then 384 message.color = color 385 end 386 -- get the font size for this message 387 ov("font "..((message.size * text.fontscale) // 1 | 0)) 388 -- create the text message clips 389 message.text = s 390 local textcol = message.color 391 if type(textcol) == "table" then textcol = table.concat(textcol, " ") end 392 local w, h = maketext(message.text, name, textcol, shadow.txtx, shadow.txty) 393 -- save the clip width and height 394 message.width = w 395 message.height = h 396end 397 398-------------------------------------------------------------------------------- 399 400local function createstatictext() 401 -- create each static text clip 402 for clipname, message in pairs(messages) do 403 updatemessage(clipname, message.text) 404 end 405end 406 407-------------------------------------------------------------------------------- 408 409local function soundstate(sound) 410 return ov("sound state "..music.folder..sound..".ogg") 411end 412 413-------------------------------------------------------------------------------- 414 415local function setvolume(sound, vol) 416 ov("sound volume "..music.folder..sound..".ogg "..vol) 417end 418 419-------------------------------------------------------------------------------- 420 421local function setchannelvolume(channel, vol) 422 if vol == 0 then 423 updatemessage(channel.."vol", "Off", colors.red) 424 else 425 updatemessage(channel.."vol", vol.."%", colors.green) 426 end 427 -- update the music volume immediately since it may be playing 428 if channel == "music" then 429 if music.currenttrack ~= "" then 430 setvolume(music.currenttrack, (options.musicvol * music.fade / 100)) 431 end 432 end 433end 434 435-------------------------------------------------------------------------------- 436 437local function stopallsound() 438 ov("sound stop") 439end 440 441-------------------------------------------------------------------------------- 442 443local function stopmusic() 444 if music.currenttrack ~= "" then 445 ov("sound stop "..music.folder..music.currenttrack..".ogg") 446 end 447end 448-------------------------------------------------------------------------------- 449 450local function playsound(name, loop) 451 if options.soundvol > 0 then 452 loop = loop or false 453 if loop then 454 ov("sound loop "..music.folder..name..".ogg "..(options.soundvol / 100)) 455 else 456 ov("sound play "..music.folder..name..".ogg "..(options.soundvol / 100)) 457 end 458 end 459end 460 461-------------------------------------------------------------------------------- 462 463local function playmusic(name, loop) 464 loop = loop or false 465 stopmusic() 466 music.currenttrack = name 467 if loop then 468 ov("sound loop "..music.folder..name..".ogg "..(options.musicvol / 100)) 469 else 470 ov("sound play "..music.folder..name..".ogg "..(options.musicvol / 100)) 471 end 472end 473 474-------------------------------------------------------------------------------- 475 476local function updatelevel(value) 477 game.level = value 478 updatemessage("level", "Level "..game.level) 479 updatemessage("complete", "Level "..game.level.." complete!") 480end 481 482-------------------------------------------------------------------------------- 483 484local function updatescore(value) 485 game.score = value 486 updatemessage("score", "Score "..game.score) 487end 488 489-------------------------------------------------------------------------------- 490 491local function updatehighscore(value) 492 local color = colors.white 493 if game.newhigh then 494 color = colors.green 495 end 496 game.hiscore = value 497 updatemessage("high", "High Score "..game.hiscore, color) 498end 499 500-------------------------------------------------------------------------------- 501 502local function updateballs(value) 503 game.balls = value 504 local color = colors.white 505 if game.balls == 1 then 506 updatemessage("left", "Last ball!", colors.red) 507 color = colors.red 508 elseif game.balls == 2 then 509 color = colors.yellow 510 updatemessage("left", game.balls.." balls left", colors.yellow) 511 else 512 updatemessage("left", game.balls.." balls left", colors.green) 513 end 514 updatemessage("balls", "Balls "..game.balls, color) 515end 516 517-------------------------------------------------------------------------------- 518 519local function updatecombo(value) 520 local color = colors.white 521 game.combo = value 522 if game.combo == game.maxcombo then 523 color = colors.green 524 end 525 updatemessage("combo", "Combo x"..game.combo - 1, color) 526end 527 528-------------------------------------------------------------------------------- 529 530local function highlightkey(textstr, x, y, w, h, token, color) 531 local t1, t2 = textstr:find(token) 532 if t1 ~= nil then 533 local charw = w / textstr:len() 534 local x1 = x + (t1 - 1) * charw 535 local oldblend = ov("blend 0") 536 local oldrgba = ovt(colors.black) 537 ovt{"fill", x1 + shadow.txtx, y + shadow.txty, (charw * (t2 - t1 + 1) + 5), h - 4} 538 ovt(color) 539 ovt{"fill", x1, y, (charw * (t2 - t1 + 1) + 5), h - 4} 540 ovt(oldrgba) 541 ov("blend "..oldblend) 542 end 543end 544 545-------------------------------------------------------------------------------- 546 547local function drawtextclip(name, x, y, xalign, yalign, highlight) 548 xalign = xalign or text.alignleft 549 yalign = yalign or text.aligntop 550 highlight = highlight or false 551 -- lookup the message 552 local message = messages[name] 553 local w = message.width 554 local h = message.height 555 local xoffset = 0 556 local yoffset = 0 557 if xalign == text.aligncenter then 558 xoffset = (wd - w) / 2 559 elseif xalign == text.alignright then 560 xoffset = wd - w - edgegapr - edgegapl 561 end 562 if yalign == text.aligncenter then 563 yoffset = (ht - h) / 2 564 elseif yalign == text.alignbottom then 565 yoffset = ht - h 566 end 567 -- check for highlight text 568 if highlight == true then 569 for colkey, list in pairs(keynames) do 570 for _, textstr in pairs(list) do 571 highlightkey(message.text, x + xoffset + edgegapl, y + yoffset, w, h, textstr, keycols[colkey]) 572 end 573 end 574 end 575 -- draw the text clip 576 pastetext(x + xoffset + edgegapl, y + yoffset, nil, name) 577 -- return clip dimensions 578 return w, h 579end 580 581-------------------------------------------------------------------------------- 582 583local function updatenotification() 584 -- check if there is a message to display 585 if notification.message ~= "" then 586 local y 587 -- check if notification finished 588 if notification.current >= notification.duration then 589 notification.message = "" 590 notification.current = 0 591 else 592 -- check which phase 593 if notification.current < notification.trans then 594 -- appear 595 y = (notification.current / notification.trans) * (8 * text.fontscale) 596 elseif notification.current > notification.duration - notification.trans then 597 -- disappear 598 y = (notification.duration - notification.current) / notification.trans * (8 * text.fontscale) 599 else 600 -- hold 601 y = (8 * text.fontscale) 602 end 603 -- draw notification 604 drawtextclip("notify", 4, (8 * text.fontscale) - y, text.alignleft, text.alignbottom) 605 notification.current = notification.current + timing.framemult 606 end 607 end 608end 609 610-------------------------------------------------------------------------------- 611 612local function notify(message, flag) 613 flag = flag or -1 614 notification.message = message 615 if flag == 0 then 616 notification.message = message.." off" 617 elseif flag == 1 then 618 notification.message = message.." on" 619 end 620 -- create the text clip 621 updatemessage("notify", notification.message) 622 if notification.current ~= 0 then 623 notification.current = notification.trans 624 end 625end 626 627-------------------------------------------------------------------------------- 628 629local function initparticles() 630 particle.particles = {} 631end 632 633-------------------------------------------------------------------------------- 634 635local function createparticles(x, y, areawd, areaht, howmany, color) 636 color = color or colors.white 637 -- find the first free slot 638 local i = 1 639 while i <= #particle.particles and particle.particles[i].alpha > 0 do 640 i = i + 1 641 end 642 for _ = 1, howmany do 643 local item = { alpha = 255, x = x - rand(areawd // 1), y = y + rand(areaht // 1), dx = rand() - 0.5, dy = rand() - 0.5, color = color } 644 particle.particles[i] = item 645 i = i + 1 646 -- find the next free slot 647 while i <= #particle.particles and particle.particles[i].alpha > 0 do 648 i = i + 1 649 end 650 end 651end 652 653-------------------------------------------------------------------------------- 654 655local function drawparticles() 656 ov("blend 2") 657 local xy = {"fill"} 658 local m = 2 659 local color = {"rgba", 0, 0, 0, -1} 660 661 for i = 1, #particle.particles do 662 local item = particle.particles[i] 663 local scale = ht / 1000 664 -- check if particle is still alive 665 local alpha = item.alpha 666 if alpha > 0 then 667 if options.showparticles ~= 0 then 668 local itemcol = item.color 669 -- check if this item has a different color than the current batch 670 if alpha ~= color[5] or itemcol[2] ~= color[2] or itemcol[3] ~= color[3] or itemcol[4] ~= color[4] then 671 -- draw the current batch 672 if m > 2 then 673 ovt(xy) 674 m = 2 675 xy = {"fill"} 676 end 677 -- start a new batch with the new color 678 color = {"rgba", itemcol[2], itemcol[3], itemcol[4], alpha} 679 ovt(color) 680 end 681 -- add the item to the batch to draw 682 xy[m] = item.x 683 xy[m + 1] = item.y 684 xy[m + 2] = 2 685 xy[m + 3] = 2 686 m = m + 4 687 end 688 -- fade item 689 alpha = alpha - 4 * timing.framemult 690 if alpha < 0 then 691 alpha = 0 692 end 693 item.alpha = alpha 694 item.x = item.x + item.dx * timing.framemult * scale 695 item.y = item.y + item.dy * timing.framemult * scale 696 item.dx = item.dx * 0.99 697 if item.dy < 0 then 698 item.dy = item.dy + 0.05 699 else 700 item.dy = (item.dy + 0.02) * 1.02 701 end 702 end 703 end 704 -- draw any unfinished batch 705 if m > 2 then 706 ovt(xy) 707 end 708end 709 710-------------------------------------------------------------------------------- 711 712local function initpoints() 713 points = {} 714end 715 716-------------------------------------------------------------------------------- 717 718local function createpoints(x, y, value) 719 -- find the first free slot 720 local i = 1 721 while i <= #points and points[i].duration > 0 do 722 i = i + 1 723 end 724 -- create the clip 725 ov("font "..((7 * text.fontscale) // 1 | 0).." mono") 726 local w, h = maketext(value, "point"..i, op.white, shadow.txtx, shadow.txty) 727 728 -- save the item 729 local item = { duration = 60, x = (x + brick.wd / 2 - w / 2), y = (y + brick.ht / 2 - h / 2) } 730 points[i] = item 731end 732 733-------------------------------------------------------------------------------- 734 735local function drawpoints() 736 ov("blend 2") 737 for i = 1, #points do 738 local item = points[i] 739 -- check if item is still alive 740 if item.duration > 0 then 741 if options.brickscore == 1 then 742 local y = item.y + brick.offsety * brick.ht 743 if item.duration < 8 then 744 -- fade out by replacing clip alpha 745 ov("target point"..i) 746 ov("replace *# *# *# *#-16") 747 ov("target") 748 end 749 -- draw item 750 ovt{"paste", item.x, y, "point"..i} 751 end 752 item.duration = item.duration - 1 * timing.framemult 753 if item.duration < 0 then 754 item.duration = 0 755 end 756 end 757 end 758end 759 760-------------------------------------------------------------------------------- 761 762local function createfadingbrick(x, y) 763 local fading = brick.fading 764 local i = 1 765 while i <= #fading and fading[i].alpha > 0 do 766 i = i + 1 767 end 768 fading[i] = { alpha = shadow.alpha, x = x, y = y } 769end 770 771-------------------------------------------------------------------------------- 772 773local function drawfadingbricks(pass, xoff, yoff) 774 ov("blend 2") 775 -- get the list of fading bricks 776 local fading = brick.fading 777 local fadecols = brick.fadecols 778 local fadecolor = shadow.fadecolor 779 for i = 1, #fading do 780 local alpha = fading[i].alpha 781 -- find each shadow that hasn't fully faded 782 if alpha > 0 then 783 -- increase transparency if this is the brick (not shadow) 784 if pass == 2 then 785 alpha = alpha - shadow.delta 786 if alpha < 0 then alpha = 0 end 787 fading[i].alpha = alpha 788 end 789 if alpha > 0 then 790 -- draw brick or shadow 791 local fy = fading[i].y 792 local y = (fy + brick.offsety) * brick.ht 793 local x = (fading[i].x - 1) * brick.wd + edgegapl 794 -- pick the color depending on drawing brick or shadow 795 if (pass == 1) then 796 fadecolor[5] = alpha 797 ovt(fadecolor) 798 else 799 fadecols[fy][5] = alpha * 2 800 ovt(fadecols[fy]) 801 end 802 ovt{"fill", (x + xoff), (y + yoff), (brick.wd - 1), (brick.ht - 1)} 803 end 804 end 805 end 806end 807 808-------------------------------------------------------------------------------- 809 810local function createbackground() 811 -- create background clip 812 ov("create "..wd.." "..ht.." "..bgclip) 813 ov("target "..bgclip) 814 -- create background gradient 815 ov("blend 0") 816 local c 817 local level = 96 818 local cmd = {"line", 0, 0, wd - 1, 0} 819 for y = 0, ht - 1 do 820 c = (y * level) // ht 821 ovt{"rgba", 0, (level - c), c, 255} 822 cmd[3] = y 823 cmd[5] = y 824 ovt(cmd) 825 end 826 827 -- add borders if required 828 if edgegapl > 0 then 829 ovt(colors.black) 830 ovt{"fill", 0, 0, edgegapl, (ht - 1)} 831 end 832 if edgegapr > 0 then 833 ovt(colors.black) 834 ovt{"fill", (wd - edgegapr), 0 , edgegapr, (ht - 1)} 835 end 836 837 -- reset target 838 ov("target") 839end 840 841-------------------------------------------------------------------------------- 842 843local function drawbackground() 844 ov("blend 0") 845 ovt{"paste", 0, 0, bgclip} 846end 847 848-------------------------------------------------------------------------------- 849 850local function drawbricks() 851 local xoff = shadow.x 852 local yoff = shadow.y 853 local bwd = brick.wd 854 local bht = brick.ht 855 local bwdm1 = bwd - 1 856 local bhtm1 = bht - 1 857 local ncols = brick.numcols 858 local startpass = 1 859 -- check whether to draw shadows 860 if options.showshadows == 0 then 861 startpass = 2 862 xoff = 0 863 yoff = 0 864 end 865 for pass = startpass, 2 do 866 drawfadingbricks(pass, xoff, yoff) 867 if pass == 1 then 868 ov("blend 2") 869 ovt(shadow.color) 870 else 871 ov("blend 0") 872 end 873 for y = 1, brick.numrows do 874 local bricks = brick.rows[y] 875 if pass == 2 then 876 ovt(brick.cols[y]) 877 end 878 local by = ((y + brick.offsety) * bht) // 1 + yoff 879 local bx = edgegapl + xoff 880 for x = 1, ncols do 881 if bricks[x] then 882 ovt{"fill", bx, by, bwdm1, bhtm1} 883 end 884 bx = bx + bwd 885 end 886 end 887 xoff = 0 888 yoff = 0 889 end 890end 891 892-------------------------------------------------------------------------------- 893 894local function drawball() 895 local oldwidth = ov("lineoption width "..(ball.size // 2)) 896 ov("blend 2") 897 if options.showshadows == 1 then 898 ovt(shadow.color) 899 ov("ellipse "..(((ball.x - ball.size // 2) + shadow.x) // 1 | 0).." "..(((ball.y - ball.size // 2) + shadow.y) // 1 | 0).." "..ball.size.." "..ball.size) 900 end 901 ovt(colors.white) 902 ov("ellipse "..((ball.x - ball.size // 2) // 1 | 0).." "..((ball.y - ball.size // 2) // 1 | 0).." "..ball.size.." "..ball.size) 903 if rand() < particle.ballpartchance * timing.framemult then 904 createparticles(ball.x + ball.size // 2, ball.y - ball.size // 2, ball.size, ball.size, particle.ballparticles) 905 end 906 ov("lineoption width "..oldwidth) 907end 908 909-------------------------------------------------------------------------------- 910 911local function drawbat(alpha) 912 alpha = alpha or 256 913 ov("blend 2") 914 if options.showshadows == 1 then 915 shadow.fadecolor[5] = alpha // 2 916 ovt(shadow.fadecolor) 917 ovt{"fill", bat.x + shadow.x, bat.y + shadow.y, bat.wd, bat.ht} 918 end 919 -- draw the bat in red if mouse is off the overlay 920 if game.offoverlay then 921 ovt(colors.red) 922 else 923 if alpha == 256 then alpha = 255 end 924 ovt{"rgba", 192, 192, 192, alpha} 925 end 926 ovt{"fill", bat.x, bat.y, bat.wd, bat.ht} 927end 928 929-------------------------------------------------------------------------------- 930 931local function initbricks() 932 brick.rows = {} 933 brick.wd = wd // brick.numcols 934 brick.ht = ht // 40 935 brick.bricksleft = 0 936 brick.totalbricks = 0 937 brick.offsety = game.level + 1 938 if brick.offsety > brick.maxoffsety then 939 brick.offsety = brick.maxoffsety 940 end 941 brick.movedown = 0 942 943 -- check for bonus level 944 bonus.current = bonus.time 945 bonus.level = false 946 if (game.level % bonus.interval) == 0 then 947 bonus.level = true 948 end 949 950 -- distribute any gap left and right 951 local edgegap = wd - brick.wd * brick.numcols 952 edgegapl = edgegap // 2 953 edgegapr = edgegap - edgegapl 954 955 -- clear the fading bricks (used when brick hit) 956 brick.fading = {} 957 958 -- create the brick fade colors from the brick colors 959 for i = 1, #brick.cols do 960 local col = brick.cols[i] 961 brick.fadecols[i] = {"rgba", col[2], col[3], col[4], col[5]} 962 end 963 964 -- set the required bricks alive 965 local match 966 for y = 1, brick.numrows do 967 local bricks = {} 968 if bonus.level then 969 local bonusrow = bonus.bricks[y] 970 match = 1 971 for x = brick.numcols - 1, 0, -1 do 972 if (bonusrow & match) == match then 973 bricks[x + 1] = true 974 brick.bricksleft = brick.bricksleft + 1 975 else 976 bricks[x + 1] = false 977 end 978 match = match + match 979 end 980 else 981 for x = 1, brick.numcols do 982 bricks[x] = true 983 brick.bricksleft = brick.bricksleft + 1 984 end 985 end 986 brick.rows[y] = bricks 987 end 988 brick.totalbricks = brick.bricksleft 989end 990 991-------------------------------------------------------------------------------- 992 993local function initbat() 994 bat.wd = wd // 10 995 bat.x = (wd - bat.wd) // 2 996 bat.ht = brick.ht 997 bat.y = ht - bat.ht * 4 998end 999 1000-------------------------------------------------------------------------------- 1001 1002local function initball() 1003 ball.size = wd // 80 1004 ball.x = (wd - ball.size) / 2 1005 ball.y = bat.y - ball.size 1006end 1007 1008-------------------------------------------------------------------------------- 1009 1010local function initshadow() 1011 shadow.x = -wd // 100 1012 shadow.y = ht // 100 1013 shadow.color = {"rgba", 0, 0, 0, shadow.alpha} 1014end 1015 1016-------------------------------------------------------------------------------- 1017 1018local function togglefullscreen() 1019 options.fullscreen = 1 - options.fullscreen 1020 writesettings() 1021 setfullscreen() 1022 initpoints() 1023end 1024 1025-------------------------------------------------------------------------------- 1026 1027local function toggletiming() 1028 options.showtiming = 1 - options.showtiming 1029 writesettings() 1030 notify("Timing", options.showtiming) 1031end 1032 1033-------------------------------------------------------------------------------- 1034 1035local function toggleparticles() 1036 options.showparticles = 1 - options.showparticles 1037 writesettings() 1038 notify("Particles", options.showparticles) 1039end 1040 1041-------------------------------------------------------------------------------- 1042 1043local function toggleautopause() 1044 options.autopause = 1 - options.autopause 1045 writesettings() 1046 notify("Autopause", options.autopause) 1047end 1048 1049-------------------------------------------------------------------------------- 1050 1051local function toggleautostart() 1052 options.autostart = 1 - options.autostart 1053 writesettings() 1054 notify("Autostart", options.autostart) 1055end 1056 1057-------------------------------------------------------------------------------- 1058 1059local function togglemouse() 1060 options.showmouse = 1 - options.showmouse 1061 writesettings() 1062 showcursor() 1063 notify("Mouse pointer", options.showmouse) 1064end 1065 1066-------------------------------------------------------------------------------- 1067 1068local function toggleshadowdisplay() 1069 options.showshadows = 1 - options.showshadows 1070 writesettings() 1071 notify("Shadows", options.showshadows) 1072end 1073 1074-------------------------------------------------------------------------------- 1075 1076local function togglebrickscore() 1077 options.brickscore = 1 - options.brickscore 1078 writesettings() 1079 notify("Brick Score", options.brickscore) 1080end 1081 1082-------------------------------------------------------------------------------- 1083 1084local function savescreenshot() 1085 local filename = g.getdir("data").."shot"..os.date("%y%m%d%H%M%S", os.time())..".png" 1086 ov("save 0 0 0 0 "..filename) 1087 notify("Saved screenshot "..filename) 1088end 1089 1090-------------------------------------------------------------------------------- 1091 1092local function toggleconfirmquit() 1093 options.confirmquit = 1 - options.confirmquit 1094 writesettings() 1095 notify("Confirm Quit", options.confirmquit) 1096end 1097 1098-------------------------------------------------------------------------------- 1099 1100local function togglecomboscore() 1101 options.comboscore = 1 - options.comboscore 1102 writesettings() 1103 notify("Combo Score", options.comboscore) 1104end 1105 1106-------------------------------------------------------------------------------- 1107 1108local function adjustsoundvol(delta) 1109 options.soundvol = options.soundvol + delta 1110 if options.soundvol > 100 then 1111 options.soundvol = 100 1112 end 1113 if options.soundvol < 0 then 1114 options.soundvol = 0 1115 end 1116 writesettings() 1117 notify("Sound Volume "..options.soundvol.."%") 1118 setchannelvolume("fx", options.soundvol) 1119end 1120 1121-------------------------------------------------------------------------------- 1122 1123local function adjustmusicvol(delta) 1124 options.musicvol = options.musicvol + delta 1125 if options.musicvol > 100 then 1126 options.musicvol = 100 1127 end 1128 if options.musicvol < 0 then 1129 options.musicvol = 0 1130 end 1131 writesettings() 1132 notify("Music Volume "..options.musicvol.."%") 1133 setchannelvolume("music", options.musicvol) 1134end 1135 1136-------------------------------------------------------------------------------- 1137 1138local function processstandardkeys(event) 1139 if event:find("^key") then 1140 if event == "key f11 none" then 1141 -- toggle fullscreen 1142 togglefullscreen() 1143 elseif event == "key a none" then 1144 -- toggle autopause when mouse moves off overlay 1145 toggleautopause() 1146 elseif event == "key b none" then 1147 -- toggle brick score display 1148 togglebrickscore() 1149 elseif event == "key c none" then 1150 -- toggle combo score display 1151 togglecomboscore() 1152 elseif event == "key d none" then 1153 -- toggle shadow display 1154 toggleshadowdisplay() 1155 elseif event == "key = none" then 1156 -- increase sound volume 1157 adjustsoundvol(10) 1158 elseif event == "key - none" then 1159 -- decrease sound volume 1160 adjustsoundvol(-10) 1161 elseif event == "key m none" then 1162 -- toggle mouse cursor display when not fullscreen 1163 togglemouse() 1164 elseif event == "key p none" then 1165 -- toggle particle display 1166 toggleparticles() 1167 elseif event == "key q none" then 1168 -- toggle confirm quit 1169 toggleconfirmquit() 1170 elseif event == "key s none" then 1171 -- toggle autostart when mouse moves onto overlay 1172 toggleautostart() 1173 elseif event == "key t none" then 1174 -- toggle timing display 1175 toggletiming() 1176 elseif event == "key [ none" then 1177 -- decrease music volume 1178 adjustmusicvol(-10) 1179 elseif event == "key ] none" then 1180 -- increase music volume 1181 adjustmusicvol(10) 1182 elseif event == "key tab none" then 1183 -- show options 1184 options.showoptions = not options.showoptions 1185 elseif event == "key f12 none" then 1186 -- save screenshot 1187 savescreenshot() 1188 end 1189 end 1190end 1191 1192-------------------------------------------------------------------------------- 1193 1194local function pausegame(paused) 1195 if paused ~= game.pause then 1196 game.pause = paused 1197 if music.currenttrack ~= "" then 1198 if game.pause then 1199 music.fade = 1 1200 music.faderate = -0.05 1201 else 1202 music.faderate = 0.05 1203 ov("sound resume") 1204 end 1205 end 1206 end 1207end 1208 1209-------------------------------------------------------------------------------- 1210 1211local function updatemusic() 1212 if music.currenttrack ~= "" then 1213 if game.pause then 1214 if music.fade > 0 then 1215 music.fade = music.fade + music.faderate 1216 if music.fade <= 0 then 1217 music.fade = 0 1218 ov("sound pause") 1219 else 1220 setvolume(music.currenttrack, (options.musicvol * music.fade / 100)) 1221 end 1222 end 1223 else 1224 if music.fade < 1 then 1225 music.fade = music.fade + music.faderate 1226 if music.fade > 1 then 1227 music.fade = 1 1228 end 1229 setvolume(music.currenttrack, (options.musicvol * music.fade / 100)) 1230 end 1231 end 1232 end 1233end 1234 1235-------------------------------------------------------------------------------- 1236 1237local function processinput() 1238 -- check for click, enter or return 1239 local event = g.getevent() 1240 if #event > 0 then 1241 local button, _ 1242 button = "" 1243 if event:find("^oclick") then 1244 _, _, _, button, _= split(event) 1245 end 1246 -- right click quits game 1247 if button == "right" then 1248 if options.confirmquit == 0 then 1249 updateballs(0) 1250 options.showoptions = false 1251 else 1252 options.confirming = not options.confirming 1253 end 1254 elseif button == "left" or event == "key return none" or event == "key space none" then 1255 -- left click, enter or space starts game, toggles pause or dismisses settings 1256 if options.confirming then 1257 updateballs(0) 1258 options.showoptions = false 1259 options.confirming = false 1260 elseif options.showoptions then 1261 options.showoptions = false 1262 elseif game.newball then 1263 if bonus.level then 1264 playmusic("bonusloop", true) 1265 else 1266 playmusic("gameloop", true) 1267 end 1268 game.newball = false 1269 pausegame(false) 1270 else 1271 -- do not unpause if off overlay 1272 if not (game.pause and game.offoverlay and options.autopause ~= 0) then 1273 pausegame(not game.pause) 1274 end 1275 end 1276 else 1277 processstandardkeys(event) 1278 end 1279 end 1280end 1281 1282-------------------------------------------------------------------------------- 1283 1284local function processendinput() 1285 local event = g.getevent() 1286 if #event > 0 then 1287 local button, _ 1288 button = "" 1289 if event:find("^oclick") then 1290 _, _, _, button, _ = split(event) 1291 end 1292 -- right click quits application 1293 if button == "right" then 1294 -- quit application 1295 game.again = false 1296 game.finished = true 1297 options.showoptions = false 1298 elseif button == "left" or event == "key return none" or event == "key space none" then 1299 -- left click, enter or space restarts game or dismisses settings 1300 if options.showoptions then 1301 options.showoptions = false 1302 else 1303 game.finished = true 1304 end 1305 else 1306 processstandardkeys(event) 1307 end 1308 end 1309end 1310 1311-------------------------------------------------------------------------------- 1312 1313local function resizegame(newwd, newht) 1314 -- check minimum size 1315 if newwd < minwd then 1316 newwd = minwd 1317 end 1318 if newht < minht then 1319 newht = minht 1320 end 1321 local xscale = newwd / wd 1322 local yscale = newht / ht 1323 1324 wd = newwd 1325 ht = newht 1326 text.fontscale = wd / minwd 1327 if (ht / minht) < text.fontscale then 1328 text.fontscale = ht / minht 1329 end 1330 1331 -- scale bat, ball and bricks 1332 brick.wd = wd // brick.numcols 1333 brick.ht = ht // 40 1334 particle.brickparticles = brick.wd * brick.ht // 10 1335 bat.wd = wd // 10 1336 bat.ht = brick.ht 1337 ball.size = wd // 80 1338 local edgegap = wd - brick.wd * brick.numcols 1339 edgegapl = edgegap // 2 1340 edgegapr = edgegap - edgegapl 1341 1342 -- reposition the bat and ball 1343 bat.x = bat.x * xscale 1344 bat.y = ht - bat.ht * 4 1345 ball.x = ball.x * xscale 1346 ball.y = ball.y * yscale 1347 1348 -- reposition particles 1349 for i = 1, #particle.particles do 1350 local item = particle.particles[i] 1351 item.x = item.x * xscale 1352 item.y = item.y * yscale 1353 end 1354 1355 -- resize shadow 1356 initshadow() 1357 1358 -- recreate background 1359 createbackground() 1360 1361 -- recreate static text 1362 createstatictext() 1363 1364 -- resize overlay 1365 ov("resize "..wd.." "..ht) 1366end 1367 1368-------------------------------------------------------------------------------- 1369 1370local function drawscoreline() 1371 ov("blend 2") 1372 drawtextclip("score", 4, 4, text.alignleft) 1373 drawtextclip("balls", -4, 4, text.alignright) 1374 drawtextclip("high", 0, 4, text.aligncenter) 1375 if game.combo > 2 then 1376 drawtextclip("combo", 0, 0, text.aligncenter, text.alignbottom) 1377 end 1378 if not game.newball and not game.pause and not options.showoptions and bonus.level and bonus.current >= 0 then 1379 local color = colors.green 1380 if bonus.current < 10 then 1381 color = colors.red 1382 elseif bonus.current < 20 then 1383 color = colors.yellow 1384 end 1385 updatemessage("time", "Time "..string.format("%.1f", bonus.current), color) 1386 drawtextclip("time", 0, ht / 2, text.aligncenter) 1387 color = colors.green 1388 if brick.bricksleft > bonus.yellow then 1389 color = colors.red 1390 elseif brick.bricksleft > bonus.green then 1391 color = colors.yellow 1392 end 1393 updatemessage("remain", "Bricks left "..brick.bricksleft, color) 1394 drawtextclip("remain", 0, ht / 2 + 25 * text.fontscale, text.aligncenter) 1395 end 1396end 1397 1398-------------------------------------------------------------------------------- 1399 1400local function drawgameover() 1401 ov("blend 2") 1402 if game.newhigh then 1403 local highscorew = drawtextclip("newhigh", 0, ht / 2 + 96 * text.fontscale, text.aligncenter) 1404 createparticles(edgegapl + (wd / 2 + highscorew / 2), (ht / 2 + 96 * text.fontscale), highscorew, 1, particle.highparticles) 1405 end 1406 updatecombo(game.gamecombo) 1407 if game.newcombo then 1408 local combow = drawtextclip("newcombo", 0, ht / 2 + 118 * text.fontscale, text.aligncenter) 1409 createparticles(edgegapl + (wd / 2 + combow / 2), (ht / 2 + 118 * text.fontscale), combow, 1, particle.comboparticles) 1410 end 1411 drawtextclip("gameover", 0, ht / 2 - 30 * text.fontscale, text.aligncenter) 1412 drawtextclip("restart", 0, ht / 2 + 30 * text.fontscale, text.aligncenter, nil, true) 1413 drawtextclip("quit", 0, ht / 2 + 52 * text.fontscale, text.aligncenter, nil, true) 1414 drawtextclip("option", 0, ht / 2 + 74 * text.fontscale, text.aligncenter, nil, true) 1415 if bat.fade > 0 then 1416 bat.fade = bat.fade - shadow.delta 1417 if bat.fade < 0 then bat.fade = 0 end 1418 drawbat(bat.fade) 1419 end 1420end 1421 1422-------------------------------------------------------------------------------- 1423 1424local function drawlevelcomplete() 1425 ov("blend 2") 1426 drawtextclip("complete", 0, ht / 2 - 30 * text.fontscale, text.aligncenter) 1427 drawtextclip("continue", 0, ht / 2 + 30 * text.fontscale, text.aligncenter, nil, true) 1428 drawtextclip("quitgame", 0, ht / 2 + 52 * text.fontscale, text.aligncenter, nil, true) 1429 drawtextclip("option", 0, ht / 2 + 74 * text.fontscale, text.aligncenter, nil, true) 1430end 1431 1432-------------------------------------------------------------------------------- 1433 1434local function drawbonuscomplete() 1435 ov("blend 2") 1436 drawtextclip("bcomplete", 0, ht / 2 - 30 * text.fontscale, text.aligncenter) 1437 1438 local w = drawtextclip("awarded", 0, ht / 2, text.aligncenter) 1439 if brick.bricksleft <= bonus.green then 1440 createparticles(edgegapl + (wd / 2 + w / 2), ht / 2, w, 1, particle.bonusparticlesg) 1441 elseif brick.bricksleft <= bonus.yellow then 1442 createparticles(edgegapl + (wd / 2 + w / 2), ht / 2, w, 1, particle.bonusparticlesy) 1443 end 1444 drawtextclip("continue", 0, ht / 2 + 30 * text.fontscale, text.aligncenter, nil, true) 1445 drawtextclip("quitgame", 0, ht / 2 + 52 * text.fontscale, text.aligncenter, nil, true) 1446 drawtextclip("option", 0, ht / 2 + 74 * text.fontscale, text.aligncenter, nil, true) 1447 if game.newbonus then 1448 local bonusw = drawtextclip("newbonus", 0, ht / 2 + 96 * text.fontscale, text.aligncenter) 1449 createparticles(edgegapl + (wd / 2 + bonusw / 2), (ht / 2 + 96 * text.fontscale), bonusw, 1, particle.bonusparticles) 1450 end 1451end 1452 1453-------------------------------------------------------------------------------- 1454 1455local function drawconfirm() 1456 ov("blend 2") 1457 drawtextclip("askquit", 0, ht / 2 - 15 * text.fontscale, text.aligncenter, nil, true) 1458 drawtextclip("askleft", 0, ht / 2 + 22 * text.fontscale, text.aligncenter, nil, true) 1459 drawtextclip("askright", 0, ht / 2 + 44 * text.fontscale, text.aligncenter, nil, true) 1460end 1461 1462-------------------------------------------------------------------------------- 1463 1464local function drawpause() 1465 ov("blend 2") 1466 drawtextclip("pause", 0, ht / 2 - 15 * text.fontscale, text.aligncenter) 1467 if game.offoverlay and options.autopause ~= 0 then 1468 if options.autostart ~= 0 then 1469 drawtextclip("focus", 0, ht / 2 + 22 * text.fontscale, text.aligncenter) 1470 else 1471 drawtextclip("manfocus", 0, ht / 2 + 22 * text.fontscale, text.aligncenter) 1472 drawtextclip("resume", 0, ht / 2 + 44 * text.fontscale, text.aligncenter, nil, true) 1473 drawtextclip("quitgame", 0, ht / 2 + 66 * text.fontscale, text.aligncenter, nil, true) 1474 drawtextclip("option", 0, ht / 2 + 88 * text.fontscale, text.aligncenter, nil, true) 1475 end 1476 else 1477 drawtextclip("resume", 0, ht / 2 + 22 * text.fontscale, text.aligncenter, nil, true) 1478 drawtextclip("quitgame", 0, ht / 2 + 44 * text.fontscale, text.aligncenter, nil, true) 1479 drawtextclip("option", 0, ht / 2 + 66 * text.fontscale, text.aligncenter, nil, true) 1480 end 1481end 1482 1483-------------------------------------------------------------------------------- 1484 1485local function drawnewball() 1486 ov("blend 2") 1487 drawtextclip("newball", 0, ht / 2 + 22 * text.fontscale, text.aligncenter, nil, true) 1488 drawtextclip("control", 0, ht / 2 + 44 * text.fontscale, text.aligncenter, nil, true) 1489 drawtextclip("quitgame", 0, ht / 2 + 66 * text.fontscale, text.aligncenter, nil, true) 1490 drawtextclip("option", 0, ht / 2 + 88 * text.fontscale, text.aligncenter, nil, true) 1491 drawtextclip("left", 0, ht / 2 - 15 * text.fontscale, text.aligncenter) 1492 if bonus.level then 1493 drawtextclip("bonus", 0, ht / 2 - 52 * text.fontscale, text.aligncenter) 1494 else 1495 drawtextclip("level", 0, ht / 2 - 52 * text.fontscale, text.aligncenter) 1496 end 1497end 1498 1499-------------------------------------------------------------------------------- 1500 1501local function drawtiming(t) 1502 timing.times[timing.timenum] = t 1503 timing.timenum = timing.timenum + 1 1504 if timing.timenum > timing.numtimes then 1505 timing.timenum = 1 1506 end 1507 local average = 0 1508 for i = 1, #timing.times do 1509 average = average + timing.times[i] 1510 end 1511 average = average / #timing.times 1512 local oldblend = ov("blend 2") 1513 updatemessage("ms", string.format("%.1fms", average)) 1514 drawtextclip("ms", -4, 0, text.alignright, text.alignbottom) 1515 ov("blend "..oldblend) 1516end 1517 1518-------------------------------------------------------------------------------- 1519 1520local function drawoption(key, setting, state, leftx, h, y) 1521 if key ~= "key" then 1522 ovt(colors.black) 1523 ovt{"fill", (leftx + edgegapl + shadow.txtx), (y + shadow.txty), (messages[key].width + 3), (messages[key].height - 4)} 1524 ovt(keycolor) 1525 ovt{"fill", (leftx + edgegapl), y, (messages[key].width + 3), (messages[key].height - 4)} 1526 end 1527 drawtextclip(key, leftx, y, text.alignleft) 1528 drawtextclip(setting, 0, y, text.aligncenter) 1529 drawtextclip(state, -leftx, y, text.alignright) 1530 return y + h 1531end 1532 1533-------------------------------------------------------------------------------- 1534 1535local function drawpercent(downkey, upkey, setting, valname, leftx, h, y) 1536 local width = messages[downkey].width 1537 local height = messages[downkey].height 1538 ovt(colors.black) 1539 ovt{"fill", (leftx + edgegapl + shadow.txtx), (y + shadow.txty), (width + 3), (height - 4)} 1540 ovt{"fill", (leftx + width * 2 + edgegapl + shadow.txtx), (y + shadow.txty), (width + 3), (height - 4)} 1541 ovt(keycolor) 1542 ovt{"fill", (leftx + edgegapl), y, (width + 3), (height - 4)} 1543 ovt{"fill", (leftx + width * 2 + edgegapl), y, (width + 3), (height - 4)} 1544 drawtextclip(downkey, leftx, y, text.alignleft) 1545 drawtextclip(upkey, leftx + width * 2, y, text.alignleft) 1546 drawtextclip(setting, 0, y, text.aligncenter) 1547 drawtextclip(valname, -leftx, y, text.alignright) 1548 return y + h 1549end 1550 1551-------------------------------------------------------------------------------- 1552 1553local function drawoptions() 1554 local leftx = wd // 6 1555 local state = {[0] = "off", [1] = "on"} 1556 1557 -- draw header 1558 ov("blend 2") 1559 local h = messages["key"].height 1560 local y = (ht - 14 * h) // 2 1561 y = drawoption("key", "function", "state", leftx, h, y) 1562 1563 -- draw options 1564 y = drawoption("a", "autopause", state[options.autopause], leftx, h, y) 1565 y = drawoption("b", "brickscore", state[options.brickscore], leftx, h, y) 1566 y = drawoption("c", "comboscore", state[options.comboscore], leftx, h, y) 1567 y = drawoption("d", "shadows", state[options.showshadows], leftx, h, y) 1568 y = drawoption("m", "mouse", state[options.showmouse], leftx, h, y) 1569 y = drawoption("p", "particles", state[options.showparticles], leftx, h, y) 1570 y = drawoption("q", "confirm", state[options.confirmquit], leftx, h, y) 1571 y = drawoption("s", "autostart", state[options.autostart], leftx, h, y) 1572 y = drawoption("t", "timing", state[options.showtiming], leftx, h, y) 1573 y = drawoption("f11", "fullscreen", state[options.fullscreen], leftx, h, y) 1574 y = drawpercent("-", "=", "sound", "fxvol", leftx, h, y) 1575 y = drawpercent("[", "]", "music", "musicvol", leftx, h, y) 1576 1577 -- draw close options 1578 drawtextclip("close", 0, y + h, text.aligncenter, nil, true) 1579 if game.balls == 0 then 1580 drawtextclip("quit", 0, y + h * 2.5, text.aligncenter, nil, true) 1581 else 1582 drawtextclip("quitgame", 0, y + h * 2.5, text.aligncenter, nil, true) 1583 end 1584end 1585 1586-------------------------------------------------------------------------------- 1587 1588local function checkforsystemevent() 1589 -- check for resize 1590 local newwd, newht = g.getview(g.getlayer()) 1591 if newwd ~= wd or newht ~= ht then 1592 resizegame(newwd, newht) 1593 end 1594 -- check for overlay hidden 1595 if not game.pause then 1596 if g.getoption("showoverlay") == 0 then 1597 pausegame(true) 1598 end 1599 end 1600end 1601 1602-------------------------------------------------------------------------------- 1603 1604local function updatebatposition() 1605 local mousepos = ov("xy") 1606 if mousepos ~= "" then 1607 local mousex, _ = split(mousepos) 1608 if mousex ~= bat.lastx then 1609 bat.lastx = mousex 1610 bat.x = tonumber(mousex) - bat.wd / 2 1611 if bat.x < edgegapl then 1612 bat.x = edgegapl 1613 elseif bat.x > wd - edgegapr - bat.wd then 1614 bat.x = wd - edgegapr - bat.wd 1615 end 1616 end 1617 -- check if mouse was off overlay 1618 if game.offoverlay then 1619 -- check if paused 1620 if game.pause and options.autostart ~= 0 and options.autopause ~= 0 then 1621 pausegame(false) 1622 end 1623 end 1624 game.offoverlay = false 1625 else 1626 -- mouse off overlay 1627 game.offoverlay = true 1628 -- check for autopause if in game 1629 if options.autopause ~= 0 and not game.newball then 1630 pausegame(true) 1631 end 1632 end 1633end 1634 1635-------------------------------------------------------------------------------- 1636 1637local function clearbonusbricks() 1638 local bricks 1639 local clearparticles = particle.brickparticles / 4 1640 for y = 1, brick.numrows do 1641 bricks = brick.rows[y] 1642 for x = 1, brick.numcols do 1643 if bricks[x] then 1644 bricks[x] = false 1645 createparticles(x * brick.wd + edgegapl, ((y + brick.offsety) * brick.ht), brick.wd, brick.ht, clearparticles, brick.cols[y]) 1646 createfadingbrick(x, y) 1647 end 1648 end 1649 end 1650end 1651 1652-------------------------------------------------------------------------------- 1653 1654local function computebonus() 1655 local bonusscore = 0 1656 if brick.bricksleft <= bonus.green then 1657 bonusscore = (brick.totalbricks - brick.bricksleft) * (100 + (game.level - 1) * 10) 1658 updatemessage("awarded", "Bricks left "..brick.bricksleft.." = "..bonusscore, colors.green) 1659 elseif brick.bricksleft <= bonus.yellow then 1660 bonusscore = (brick.totalbricks - brick.bricksleft) * (50 + (game.level - 1) * 10) 1661 updatemessage("awarded", "Bricks left "..brick.bricksleft.." = "..bonusscore, colors.yellow) 1662 else 1663 updatemessage("awarded", "Bricks left "..brick.bricksleft.." = ".."No Bonus", colors.red) 1664 end 1665 playmusic("levelcompleteloop", true) 1666 updatescore(game.score + bonusscore) 1667 if game.score > game.hiscore then 1668 game.newhigh = true 1669 updatehighscore(game.score) 1670 end 1671 if bonusscore > bonus.best then 1672 game.newbonus = true 1673 bonus.best = bonusscore 1674 end 1675end 1676 1677-------------------------------------------------------------------------------- 1678 1679local function resetcombo() 1680 updatecombo(1) 1681 game.combomult = 1 1682 game.comboraw = 0 1683 game.comboextra = 0 1684end 1685 1686-------------------------------------------------------------------------------- 1687 1688local function playexit() 1689 local box = {} 1690 local n = 1 1691 local tx, ty 1692 local tilesize = wd // 32 1693 ov("blend 0") 1694 1695 -- copy the screen into tiles 1696 for y = 0, ht, tilesize do 1697 for x = 0, wd, tilesize do 1698 tx = x + rand(0, wd // 8) - wd / 16 1699 ty = ht + rand(0, ht // 2) 1700 local entry = {} 1701 entry[1] = x 1702 entry[2] = y 1703 entry[3] = tx 1704 entry[4] = ty 1705 box[n] = entry 1706 ov("copy "..x.." "..y.." "..tilesize.." "..tilesize.." sprite"..n) 1707 n = n + 1 1708 end 1709 end 1710 local fadestart = music.fade 1711 ovt(colors.black) 1712 for i = 0, 100 do 1713 local t = g.millisecs() 1714 local a = i / 100 1715 local x, y 1716 ovt{"fill"} 1717 -- update each tile 1718 for j = 1, #box do 1719 x = box[j][1] 1720 y = box[j][2] 1721 tx = box[j][3] 1722 ty = box[j][4] 1723 ovt{"paste", (x * (1 - a) + tx * a), (y * (1 - a) + ty * a), "sprite"..j} 1724 end 1725 -- draw timing if on 1726 if options.showtiming == 1 then 1727 drawtiming(g.millisecs() - t) 1728 end 1729 -- fade the music 1730 music.fade = fadestart * ((100 - i) / 100) 1731 setvolume(music.currenttrack, (options.musicvol * music.fade / 100)) 1732 ov("update") 1733 while g.millisecs() - t < 15 do end 1734 end 1735 -- delete tiles 1736 for i = 1, #box do 1737 ov("delete sprite"..i) 1738 end 1739end 1740 1741-------------------------------------------------------------------------------- 1742 1743local function breakout() 1744 -- set font 1745 local oldfont = ov("font 16 mono") 1746 local oldbg = ov("textoption background 0 0 0 0") 1747 local oldblend = ov("blend 0") 1748 local oldcursor = ov("cursor arrow") 1749 1750 -- read saved settings 1751 readsettings() 1752 setfullscreen() 1753 1754 -- set sound and music volume 1755 setchannelvolume("fx", options.soundvol) 1756 setchannelvolume("music", options.musicvol) 1757 1758 -- play games until finished 1759 game.balls = 3 1760 game.score = 0 1761 game.level = 1 1762 game.again = true 1763 game.newhigh = false 1764 game.newcombo = false 1765 game.newbonus = false 1766 1767 -- initialise the bat and ball 1768 initbat() 1769 initball() 1770 1771 -- welcome message 1772 notify("Golly Breakout build "..build) 1773 1774 -- create static text 1775 createstatictext() 1776 1777 -- initialize dynamic text 1778 updatescore(game.score) 1779 updatehighscore(game.hiscore) 1780 updateballs(game.balls) 1781 updatelevel(game.level) 1782 1783 -- main loop 1784 while game.again do 1785 music.fade = 1 1786 1787 -- initialize the bricks 1788 initbricks() 1789 1790 -- create the background 1791 createbackground() 1792 1793 -- intiialize the bat 1794 local bathits = 0 1795 local maxhits = 7 1796 bat.lastx = -1 1797 1798 -- initialize the ball 1799 local balldx = 0.5 1800 local balldy = -1 1801 local maxspeed = 2.2 + (game.level - 1) * 0.1 1802 if maxspeed > 3 then 1803 maxspeed = 3 1804 end 1805 local speedinc = 0.02 1806 local speeddef = 1 + (game.level - 1) * speedinc * 4 1807 if speeddef > maxspeed then 1808 speeddef = maxspeed 1809 end 1810 local ballspeed = speeddef 1811 local speeddiv = 3 1812 1813 -- initialize shadow 1814 initshadow() 1815 1816 -- initialize particles 1817 initparticles() 1818 1819 -- initialise points 1820 initpoints() 1821 1822 -- whether alive 1823 game.newball = true 1824 options.confirming = false 1825 pausegame(false) 1826 1827 -- whether mouse off overlay 1828 game.offoverlay = false 1829 1830 -- reset combo 1831 resetcombo() 1832 1833 -- game loop 1834 playmusic("gamestart") 1835 while game.balls > 0 and brick.bricksleft > 0 and bonus.current > 0 do 1836 -- time frame 1837 local frametime = g.millisecs() 1838 1839 -- check for mouse click or key press 1840 processinput() 1841 1842 -- check if size of overlay has changed or overlay is hidden 1843 checkforsystemevent() 1844 1845 -- draw the background 1846 drawbackground() 1847 1848 -- process next game step unless paused or game finished due to user quit 1849 if not game.pause and not options.confirming and not options.showoptions and bonus.current > 0 and game.balls > 0 then 1850 -- check for new ball 1851 if not game.newball then 1852 -- update ball position incrementally 1853 local framesteps = (ball.numsteps * timing.framemult) // 1 1854 local i = 1 1855 while i <= framesteps and not game.newball do 1856 i = i + 1 1857 local stepx = ((balldx * ballspeed * ball.size) / speeddiv) / ball.numsteps 1858 local stepy = ((balldy * ballspeed * ball.size) / speeddiv) / ball.numsteps 1859 ball.x = ball.x + stepx 1860 ball.y = ball.y + stepy 1861 1862 -- check for ball hitting left or right boundary 1863 if ball.x < ball.size / 2 + edgegapl or ball.x >= wd - edgegapr - ball.size / 2 then 1864 createparticles(ball.x, ball.y, 1, 1, particle.wallparticles) 1865 -- invert x direction 1866 balldx = -balldx 1867 ball.x = ball.x - stepx 1868 playsound("edge") 1869 end 1870 1871 -- check for ball hitting top boundary 1872 if ball.y < ball.size / 2 then 1873 createparticles(ball.x, (ball.y - ball.size / 2), 1, 1, particle.wallparticles) 1874 -- ball hit top so speed up a little bit 1875 balldy = -balldy 1876 ball.y = ball.y - stepy 1877 ballspeed = ballspeed + speedinc / 2 1878 if ballspeed > maxspeed then 1879 ballspeed = maxspeed 1880 end 1881 playsound("top") 1882 1883 -- check for ball hitting bottom boundary 1884 elseif ball.y >= ht then 1885 -- check for bonus level 1886 if bonus.level then 1887 -- end bonus level 1888 bonus.current = 0 1889 else 1890 -- ball lost! 1891 updateballs(game.balls - 1) 1892 balldy = -1 1893 balldx = 0.5 1894 ballspeed = speeddef 1895 game.newball = true 1896 -- reset combo 1897 if game.comboextra - game.comboraw > 0 then 1898 if options.comboscore == 1 then 1899 notify("Combo x "..(game.combo - 1).." Score "..game.comboextra - game.comboraw.." (+"..(((100 * game.comboextra // game.comboraw) - 100) // 1 | 0).."%)") 1900 end 1901 end 1902 resetcombo() 1903 playmusic("lostball") 1904 end 1905 -- exit loop 1906 i = framesteps + 1 1907 1908 -- check for ball hitting bat 1909 elseif ball.y >= bat.y and ball.y <= bat.y + bat.ht - 1 and ball.x >= bat.x and ball.x < bat.x + bat.wd then 1910 -- set dx from where ball hit bat 1911 balldx = (3 * (ball.x - bat.x) / bat.wd) - 1.5 1912 if balldx >= 0 and balldx < 0.1 then 1913 balldx = 0.1 1914 end 1915 if balldx > -0.1 and balldx <= 0 then 1916 balldx = -0.1 1917 end 1918 balldy = -balldy 1919 ball.y = bat.y 1920 bathits = bathits + 1 1921 -- move the bricks down after a number of bat hits 1922 if bathits == maxhits then 1923 bathits = 0 1924 if brick.offsety < brick.maxoffsety then 1925 brick.movedown = brick.movesteps 1926 brick.startoffset = brick.offsety 1927 end 1928 end 1929 createparticles(ball.x, ball.y - ball.size / 2, 1, 1, particle.batparticles) 1930 -- reset combo 1931 if game.comboextra - game.comboraw > 0 then 1932 if options.comboscore == 1 then 1933 notify("Combo x "..(game.combo - 1).." Score "..game.comboextra - game.comboraw.." (+"..(((100 * game.comboextra / game.comboraw) - 100) // 1 | 0).."%)") 1934 end 1935 end 1936 resetcombo() 1937 playsound("bat") 1938 end 1939 1940 -- check for ball hitting brick 1941 brick.y = (ball.y - (brick.offsety * brick.ht)) // brick.ht 1942 if brick.y >= 1 and brick.y <= brick.numrows then 1943 brick.x = ((ball.x - edgegapl) // brick.wd) + 1 1944 if brick.rows[brick.y][brick.x] then 1945 -- hit a brick! 1946 brick.rows[brick.y][brick.x] = false 1947 -- adjust score 1948 local pointval = ((game.level + 9) * (brick.numrows - brick.y + 1) * game.combomult) // 1 | 0 1949 local rawpoints = ((game.level + 9) * (brick.numrows - brick.y + 1)) // 1 | 0 1950 if game.combo > 1 then 1951 game.comboraw = game.comboraw + rawpoints 1952 game.comboextra = game.comboextra + pointval 1953 end 1954 updatescore(game.score + pointval) 1955 if game.score > game.hiscore then 1956 game.newhigh = true 1957 updatehighscore(game.score) 1958 end 1959 createpoints((brick.x - 1) * brick.wd + edgegapl, brick.y * brick.ht, pointval) 1960 createfadingbrick(brick.x, brick.y) 1961 -- increment combo 1962 game.combomult = game.combomult * game.combofact 1963 if game.combo + 1 > game.maxcombo then 1964 game.maxcombo = game.combo + 1 1965 game.newcombo = true 1966 end 1967 updatecombo(game.combo + 1) 1968 if game.combo > game.gamecombo then 1969 game.gamecombo = game.combo 1970 end 1971 -- work out which axis to invert 1972 local lastbricky = ((ball.y - stepy) - (brick.offsety * brick.ht)) // brick.ht 1973 if lastbricky == brick.y then 1974 balldx = -balldx 1975 else 1976 balldy = -balldy 1977 end 1978 -- speed the ball up 1979 ballspeed = ballspeed + speedinc 1980 if ballspeed > maxspeed then 1981 ballspeed = maxspeed 1982 end 1983 -- create particles 1984 createparticles(brick.x * brick.wd + edgegapl, ((brick.y + brick.offsety) * brick.ht), brick.wd, brick.ht, particle.brickparticles, brick.cols[brick.y]) 1985 -- one less brick 1986 brick.bricksleft = brick.bricksleft - 1 1987 playsound("brick"..(brick.y | 0)) 1988 end 1989 end 1990 end 1991 end 1992 end 1993 1994 -- update brick position 1995 if brick.movedown > 0 then 1996 brick.movedown = brick.movedown - 1 1997 brick.offsety = brick.offsety + (1 / brick.movesteps) 1998 if brick.movedown <= 0 then 1999 brick.movedown = 0 2000 brick.offsety = brick.startoffset + 1 2001 end 2002 end 2003 2004 -- update bat position 2005 updatebatposition() 2006 2007 -- if new ball then set ball to sit on bat 2008 if game.newball then 2009 ball.x = bat.x + bat.wd / 2 2010 ball.y = bat.y - ball.size 2011 end 2012 2013 -- draw the particles 2014 drawparticles() 2015 2016 -- draw the bricks 2017 drawbricks() 2018 2019 -- draw the points 2020 drawpoints() 2021 2022 -- draw the ball 2023 if game.balls > 0 then 2024 drawball() 2025 end 2026 2027 -- draw the bat 2028 drawbat() 2029 2030 -- draw the score, high score and lives 2031 drawscoreline() 2032 2033 -- check for text overlay 2034 if options.confirming then 2035 drawconfirm() 2036 elseif options.showoptions then 2037 drawoptions() 2038 elseif game.pause then 2039 drawpause() 2040 elseif game.newball and game.balls > 0 then 2041 drawnewball() 2042 end 2043 2044 -- update music volume (used for pause fade/resume) 2045 updatemusic() 2046 2047 -- draw timing if on 2048 if options.showtiming == 1 then 2049 drawtiming(g.millisecs() - frametime) 2050 end 2051 2052 -- update notification 2053 updatenotification() 2054 2055 -- update the display 2056 ov("update") 2057 2058 -- pause until frame time reached 2059 while g.millisecs() - frametime < 16 do end 2060 2061 -- check what the actual frame time was and scale speed accordingly 2062 timing.framemult = 1 2063 local finaltime = g.millisecs() - frametime 2064 if finaltime > timing.sixtyhz then 2065 -- cap to maximum frame time in case external event took control for a long time 2066 if finaltime > timing.framecap then 2067 finaltime = timing.framecap 2068 end 2069 timing.framemult = finaltime / timing.sixtyhz 2070 end 2071 2072 -- update bonus time if on bonus level 2073 if bonus.level and not game.pause and not game.newball and not options.showoptions then 2074 bonus.current = bonus.current - (finaltime / 1000) 2075 end 2076 end 2077 2078 -- check for bonus level complete 2079 if bonus.level then 2080 computebonus() 2081 clearbonusbricks() 2082 else 2083 if brick.bricksleft == 0 then 2084 playmusic("levelcompleteloop", true) 2085 end 2086 end 2087 2088 -- save high score, max combo and best bonus 2089 if game.newhigh or game.newcombo or game.newbonus then 2090 writesettings() 2091 end 2092 2093 -- if game is over destroy bat and display best game combo 2094 if game.balls == 0 then 2095 -- destroy bat 2096 createparticles(bat.x + bat.wd, bat.y, bat.wd, bat.ht, particle.lostparticles) 2097 bat.fade = shadow.alpha 2098 notify("Best Combo x"..game.maxcombo - 1) 2099 end 2100 2101 -- loop until mouse button clicked or enter pressed 2102 bonus.current = -1 2103 game.finished = false 2104 local fading = false 2105 local musicplaytime = g.millisecs() 2106 2107 while not game.finished do 2108 -- time frame 2109 local frametime = g.millisecs() 2110 2111 -- check if size of overlay has changed or overlay hidden 2112 checkforsystemevent() 2113 2114 -- draw background 2115 drawbackground() 2116 2117 -- update bat position if game is not over 2118 if game.balls > 0 then 2119 updatebatposition() 2120 end 2121 2122 -- draw particles 2123 drawparticles() 2124 2125 -- draw bricks 2126 drawbricks() 2127 2128 -- draw brick score 2129 drawpoints() 2130 2131 -- check why game finished 2132 if options.showoptions then 2133 drawoptions() 2134 else 2135 if game.balls == 0 then 2136 -- game over 2137 drawgameover() 2138 else 2139 -- draw bat 2140 drawbat() 2141 if bonus.level then 2142 -- end of bonus level 2143 drawbonuscomplete() 2144 else 2145 -- level complete 2146 drawlevelcomplete() 2147 end 2148 end 2149 end 2150 2151 -- handle music during game over 2152 if game.balls == 0 then 2153 if fading then 2154 -- fade music after one play through 2155 if frametime - musicplaytime > music.gameovertime then 2156 music.fade = music.fade + music.faderate 2157 if music.fade < 0 then 2158 music.fade = 0 2159 end 2160 setvolume("gamelostloop", (options.musicvol * music.fade / 100)) 2161 end 2162 else 2163 -- wait for ball lost music to finish 2164 if soundstate("lostball") ~= "playing" then 2165 fading = true 2166 musicplaytime = g.millisecs() 2167 music.fade = 1 2168 music.faderate = -0.001 2169 playmusic("gamelostloop", true) 2170 else 2171 updatemusic() 2172 end 2173 end 2174 end 2175 2176 -- draw score line 2177 drawscoreline() 2178 2179 -- get key or mouse event 2180 processendinput() 2181 2182 -- draw timing if on 2183 if options.showtiming == 1 then 2184 drawtiming(g.millisecs() - frametime) 2185 end 2186 2187 -- update notification 2188 updatenotification() 2189 2190 -- update the display 2191 ov("update") 2192 2193 -- pause until frame time reached 2194 while g.millisecs() - frametime < 16 do end 2195 2196 -- check what the actual frame time was and scale speed accordingly 2197 timing.framemult = 1 2198 local finaltime = g.millisecs() - frametime 2199 if finaltime > timing.sixtyhz then 2200 -- cap to maximum frame time in case external event took control for a long time 2201 if finaltime > timing.framecap then 2202 finaltime = timing.framecap 2203 end 2204 timing.framemult = finaltime / timing.sixtyhz 2205 end 2206 end 2207 2208 -- check why game finished 2209 if game.balls == 0 then 2210 -- reset 2211 updatescore(0) 2212 updateballs(3) 2213 updatelevel(1) 2214 game.newhigh = false 2215 updatehighscore(game.hiscore) 2216 game.newcombo = false 2217 game.gamecombo = 1 2218 else 2219 -- level complete 2220 updatelevel(game.level + 1) 2221 end 2222 game.newbonus = false 2223 end 2224 2225 -- exit animation 2226 playexit() 2227 2228 -- free clips and restore settings 2229 ov("delete "..bgclip) 2230 ov("blend "..oldblend) 2231 ov("font "..oldfont) 2232 ov("textoption background "..oldbg) 2233 ov("cursor "..oldcursor) 2234end 2235 2236-------------------------------------------------------------------------------- 2237 2238local function main() 2239 -- get size of overlay 2240 wd, ht = g.getview(g.getlayer()) 2241 if wd < minwd then 2242 wd = minwd 2243 end 2244 if ht < minht then 2245 ht = minht 2246 end 2247 2248 -- create overlay 2249 ov("create "..wd.." "..ht) 2250 text.fontscale = wd / minwd 2251 if (ht / minht) < text.fontscale then 2252 text.fontscale = ht / minht 2253 end 2254 2255 -- run breakout 2256 breakout() 2257end 2258 2259-------------------------------------------------------------------------------- 2260 2261local oldoverlay = g.setoption("showoverlay", 1) 2262local oldbuttons = g.setoption("showbuttons", 0) -- disable translucent buttons 2263local oldscroll = g.setoption("showscrollbars", 0) 2264local oldfs = g.getoption("fullscreen") 2265 2266local status, err = xpcall(main, gp.trace) 2267if err then g.continue(err) end 2268-- the following code is always executed 2269 2270g.check(false) 2271stopallsound() 2272ov("delete") 2273g.setoption("showoverlay", oldoverlay) 2274g.setoption("showbuttons", oldbuttons) 2275g.setoption("showscrollbars", oldscroll) 2276g.setoption("fullscreen", oldfs) 2277