From f155eac22cedcbae388d7d6dcee1a7191821557e Mon Sep 17 00:00:00 2001 From: J Banana <9792206+jbanana@users.noreply.github.com> Date: Sun, 1 Dec 2024 22:14:05 +0000 Subject: [PATCH] Added EscapeRoom game --- EscapeRoom/EscapeRoom.py | 647 +++++++++++++++++++++++++++++ EscapeRoom/arcade_description.txt | 1 + EscapeRoom/arcade_title_video.webm | Bin 0 -> 22371 bytes EscapeRoom/images.bin | Bin 0 -> 18000 bytes 4 files changed, 648 insertions(+) create mode 100755 EscapeRoom/EscapeRoom.py create mode 100755 EscapeRoom/arcade_description.txt create mode 100755 EscapeRoom/arcade_title_video.webm create mode 100755 EscapeRoom/images.bin diff --git a/EscapeRoom/EscapeRoom.py b/EscapeRoom/EscapeRoom.py new file mode 100755 index 00000000..21a8f8f1 --- /dev/null +++ b/EscapeRoom/EscapeRoom.py @@ -0,0 +1,647 @@ +from thumbyGraphics import display as d +import thumbyButton as b +from sys import print_exception +from random import randrange +from _thread import start_new_thread +from thumbyAudio import audio +from time import sleep + +inEmulator = None +try: + import emulator + inEmulator = True +except ImportError: + inEmulator = False + +baseFPS = 4 +sideScrollFPS = 45 +def setFPS( fps ): + d.setFPS( fps ) +setFPS( baseFPS ) + +def which(): + result = None + if b.buttonU.justPressed(): result = 'U' + elif b.buttonD.justPressed(): result = 'D' + elif b.buttonL.justPressed(): result = 'L' + elif b.buttonR.justPressed(): result = 'R' + elif b.buttonA.justPressed(): result = 'A' + elif b.buttonB.justPressed(): result = 'B' + b.inputJustPressed() + return result + +def sideScroll( displayString, x, y, w, b, lambdas ): + if ( 1 + len( displayString ) ) * 6 > w: + setFPS( sideScrollFPS ) + displayLen = len( displayString ) * 6 + w + o = b if b > 0 else 0 + pauseStart = 10 if o > 0 else 0 + while True: + d.drawFilledRectangle( x, y, w, 8, 1 ) + d.drawText( displayString, x + w - o, y, 0 ) + if 'lFill' in lambdas: + lambdas[ 'lFill' ]() + d.update() + b = which() + if b and b.upper() in lambdas: + result = lambdas[ b.upper() ]() + setFPS( baseFPS ) + return result + if pauseStart > 0: + pauseStart -= 1 + else: + o = ( o + 1 ) % displayLen + elif 'lFill' in lambdas: + lambdas[ 'lFill' ]() + d.drawText( displayString, x, y, 0 ) + d.update() + b = which() + if b and b in lambdas: + return lambdas[ b ]() + +class Menu: + def __init__( self, items ): + self.items = items + self.count = len( items ) + self.item = 0 + self.action = None + + def a( self ): + self.action = self.items[ self.item ][ 1 ] + return True + + def u( self ): + if self.item > 0: + self.item = self.item - 1 + return False + + def d( self ): + if self.item < len( self.items ) - 1: + self.item = self.item + 1 + return False + + def l( self ): + return True + + def r( self ): + item = self.items[ self.item ] + self.action = item[ 2 if len( item ) > 2 else 1 ] + return True + + def displayItems( self ): + return[ + ( None if self.item < 2 else self.items[ self.item - 2 ][ 0 ] ), + ( None if self.item < 1 else self.items[ self.item - 1 ][ 0 ] ), + ( self.items[ self.item ][ 0 ] ), + ( None if self.item + 1 >= self.count else self.items[ self.item + 1 ][ 0 ] ), + ( None if self.item + 2 >= self.count else self.items[ self.item + 2 ][ 0 ] ) + ] + + def display( self ): + def leftFill(): + d.drawFilledRectangle( 0, 16, 6, 8, 1 ) + d.drawText( '>', 0, 16, 0 ) + self.action = None + while True: + d.fill( 1 ) + itemY = 0 + for item in self.displayItems(): + if item: + if itemY == 16: + curr = item + else: + d.drawText( item, 6, itemY, 0 ) + itemY = itemY + 8 + if sideScroll( + curr, 6, 16, d.width, d.width, { + 'A': lambda: self.a(), + 'B': lambda: True, + 'U': lambda: self.u(), + 'D': lambda: self.d(), + 'u': lambda: self.u(), + 'd': lambda: self.d(), + 'L': lambda: self.l(), + 'R': lambda: self.r(), + 'lFill': lambda: leftFill() + }): + break + return self.action() if self.action else None + +images = { + 'BoxClosed' : 0, + 'BoxOpen' : 360, + 'Desk' : 720, + 'DeskL' : 1080, + 'DeskM' : 1440, + 'DeskMSmsh1' : 1800, + 'DeskMSmsh2' : 2160, + 'DeskOpen' : 2520, + 'DeskOpenSm' : 2880, + 'DeskR' : 3240, + 'DeskRLock' : 3600, + 'DeskROpen' : 3960, + 'Door' : 4320, + 'DoorM' : 4680, + 'DoorMNoKey' : 5040, + 'DoorNoKey' : 5400, + 'DoorOpen1' : 5760, + 'DoorOpen2' : 6120, + 'DoorOpen3' : 6480, + 'DoorOpen4' : 6840, + 'Instructions': 7200, + 'Lamp' : 7560, + 'LampL' : 7920, + 'LampLFlip' : 8280, + 'LampLit' : 8640, + 'LampLLit' : 9000, + 'LampM' : 9360, + 'LampMLit' : 9720, + 'LampR' : 10080, + 'LampRLit' : 10440, + 'Sink' : 10800, + 'SinkL' : 11160, + 'SinkLHammer' : 11520, + 'SinkLOpen' : 11880, + 'SinkM' : 12240, + 'SinkMOpen' : 12600, + 'SinkR' : 12960, + 'SinkRTick' : 13320, + 'SinkRTick3' : 13680, + 'SinkRTickM' : 14040, + 'SinkRTock' : 14400, + 'SinkRTock3' : 14760, + 'SinkRTockM' : 15120, + 'SinkTick' : 15480, + 'SinkTickO' : 15840, + 'SinkTickOO' : 16200, + 'SinkTock' : 16560, + 'SinkTockO' : 16920, + 'SinkTockOO' : 17280, + 'Title' : 17640, +} +images[ 'LampUnlit' ] = images[ 'Lamp' ] +images[ 'LampLUnlit' ] = images[ 'LampL' ] +images[ 'LampMUnlit' ] = images[ 'LampM' ] +images[ 'LampRUnlit' ] = images[ 'LampR' ] + +sequences = { + 'main': [ 'Door', 'Desk', 'Sink', 'Lamp' ], + 'Door': [ None, None, 'DoorM', None ], + 'Desk': [ None, 'DeskL', 'DeskM', 'DeskR', None ], + 'Sink': [ None, 'SinkL', 'SinkM', 'SinkR', None ], + 'Lamp': [ None, 'LampL', 'LampM', 'LampR', None ] +} + +states = { + 'Start' : [ ( 'DoorM', 'Unhook key', 'ClockKey' ), ( 'DeskL', 'Read diary', 'SeqPt1' ) ], + 'ClockKey' : [ ( 'SinkR', 'Wind clock', 'SeqPt2' ), ( 'DeskL', 'Read diary', 'ClockKeySeqPt1' ) ], + 'SeqPt2' : [ ( 'DeskL', 'Read diary', 'Sequence' ) ], + 'ClockKeySeqPt1' : [ ( 'SinkR', 'Wind clock', 'Sequence' ) ], + 'SeqPt1' : [ ( 'DoorM', 'Unhook key', 'ClockKeySeqPt1' ) ], + 'Sequence' : [ ( 'SinkM', 'Open box', 'LightBulb' ) ], + 'LightBulb' : [ ( 'LampM', 'Use bulb', 'LampOff' ) ], + 'LampOff' : [ ( 'LampM', 'Pull switch', 'Combination' ) ], + 'Combination' : [ ( 'DeskR', 'Enter combination', 'SmallKey' ) ], + 'SmallKey' : [ ( 'SinkL', 'Unlock cupboard', 'Hammer' ) ], + 'Hammer' : [ ( 'DeskM', 'Smash piggy bank', 'DoorKey' ) ], + 'DoorKey' : [ ( 'DoorM', 'Unlock door', 'Complete' ) ], + 'Complete' : [] +} + +for state in states: + states[ state ].append ( ( 'LampL', "Press button", state ) ) + states[ state ].append ( ( 'LampR', 'Look at art', state ) ) + if state != 'LampOff': + states[ state ].append ( ( 'LampM', 'Pull switch', state ) ) + +gameData = { + 'seq' : [ 'main', 2 ], + 'state': 'Start', + 'items': [], + 'bulbFitted': False, + 'lampLit': False, + 'clockTick': 0, + 'boxOpen': False, + 'drawOpen': False, + 'cupbdOpen': False, + 'pigSmashed': False, + 'film': False +} + +def drawImage( imageName ): + with open( '/Games/EscapeRoom/images.bin', 'rb' ) as imgFile: + imgFile.seek( images[ imageName ] ) + d.blit( imgFile.read( 360 ), 0, 0, 72, 40, -1, 0, 0 ) + +def drawState( times = 1 ): + seqData = gameData[ 'seq' ] + imageName = sequences[ seqData[ 0 ] ][ seqData[ 1 ] ] + for _ in range( times ): + drawImage( imageName ) + if gameData[ 'film' ]: + d.drawFilledRectangle( 1, 0, 6, d.height, 0 ) + d.drawLine( 0, 0, 0, d.height, 1 ) + d.drawFilledRectangle( d.width - 7, 0, 6, d.height, 0 ) + rhs = d.width - 1 + d.drawLine( rhs, 0, rhs, d.height, 1 ) + startY = 2 - randrange( 2 ) + for y in range( d.height / 5 ): + h = y * 5 + startY + d.drawFilledRectangle( 2, h, 4, 3, 1 ) + d.drawFilledRectangle( d.width - 6, h, 4, 3, 1 ) + for scratch in range( randrange( 5 ) ): + if scratch > 1: + x = randrange( 7, d.width - 7 ) + d.drawLine( x, 0, x, d.height, 0 ) + d.update() + +def drawDup( x1, y1, x2, y2 ): + d.drawLine( x1, y1, x2, y2, 0 ) + d.drawLine( d.width - x1 - 1, y1, d.width - x2 - 1, y2, 0 ) + d.drawLine( x1, d.height - y1 - 1, x2, d.height - y2 - 1, 0 ) + d.drawLine( d.width - x1 - 1, d.height - y1 - 1, d.width - x2 - 1, d.height - y2 - 1, 0 ) + +def overlay(): + drawDup( 0, 0, 5, 0 ) + drawDup( 0, 1, 3, 1 ) + drawDup( 0, 2, 2, 2 ) + drawDup( 0, 3, 1, 3 ) + drawDup( 0, 4, 0, 5 ) + +def playCuckoo(): + audio.play( 523, 500 ) # 12|16| + audio.play( 392, 750 ) # 7|16| . + sleep( 0.5 ) # 7|16| r + audio.play( 523, 500 ) # 12|16| + audio.play( 392, 750 ) # 7|16| . + sleep( 0.5 ) # 7|16| r + audio.play( 523, 500 ) # 12|16| + audio.play( 392, 750 ) # 7|16| . + +def showText( line1, line2 = None, line3 = None ): + d.fill( 1 ) + d.drawText( line1, 37 - len( line1 ) * 3, 8, 0 ) + if line2: + d.drawText( line2, 37 - len( line2 ) * 3, 18, 0 ) + if line3: + d.drawText( line3, 37 - len( line3 ) * 3, 28, 0 ) + overlay() + if line1 == '"...U,L,R"': + if inEmulator: + print( "Emulator detected - cuckoo on the main thread." ) + playCuckoo() + else: + start_new_thread( playCuckoo, () ) + b.inputJustPressed() + while not which(): + d.update() + +def cuckoo(): + gameData[ 'film' ] = True + for _ in range( 3 ): + images[ 'SinkR' ] = images[ 'SinkRTick' ] + drawState( 3 ) + images[ 'SinkR' ] = images[ 'SinkRTock' ] + drawState( 3 ) + images[ 'SinkR' ] = images[ 'SinkRTick3' ] + drawState( 3 ) + images[ 'SinkR' ] = images[ 'SinkRTock3' ] + drawState( 3 ) + images[ 'SinkR' ] = images[ 'SinkRTickM' ] + drawState( 3 ) + images[ 'SinkR' ] = images[ 'SinkRTockM' ] + drawState( 3 ) + +def showInventory(): + items = gameData[ 'items' ] + if items: + Menu( [ ( 'Inventory:', lambda: None ) ] + [ ( item, lambda: None ) for item in items ] ).display() + else: + Menu( [ ( 'Inventory:', lambda: None ), ( 'No items', lambda: None ) ] ).display() + +def openBox(): + showText( 'How?' ) + images[ 'temp' ] = images[ 'SinkM' ] + images[ 'SinkM' ] = images[ 'BoxClosed' ] + drawState() + seq = 'UDLULR' + index = 0 + correct = True + while True: + btn = which() + if not btn: + d.update() + continue + if btn == 'A': + break + if btn == 'B': + showInventory() + elif btn == seq[ index ]: + index += 1 + if index == len( seq ): + if correct: + gameData[ 'film' ] = True + drawState( 3 ) + images[ 'SinkM' ] = images[ 'BoxOpen' ] + drawState( 6 ) + images[ 'SinkM' ] = images[ 'SinkMOpen' ] + gameData[ 'film' ] = False + return True + showText( "Didn't open" ) + break + else: + index += 1 + correct = False + if index == len( seq ): + showText( "Didn't open" ) + break + drawState() + images[ 'SinkM' ] = images[ 'temp' ] + return False; + +def drawDrawBackground(): + drawImage( 'DeskRLock' ) + +def openDraw(): + numbers = [ 0, 0, 0 ] + currentIndex = 0 + drawDrawBackground() + while True: + d.drawFilledRectangle( 16, 16, 27, 9, 1 ) + d.drawFilledRectangle( 16 + currentIndex * 9, 16, 9, 9, 0 ) + for index, number in enumerate( numbers ): + d.drawText( str( number ), 18 + index * 9, 17, 1 if index == currentIndex else 0 ) + d.update() + btn = which() + if not btn: + continue + if btn == 'B': + return False + if btn == 'A': + if numbers == [ 4, 5, 3 ]: + break; + else: + showText( "Doesn't open" ) + drawDrawBackground() + elif btn == 'U': + numbers [ currentIndex ] += 1 + if numbers [ currentIndex ] > 9: numbers [ currentIndex ] = 0 + elif btn == 'D': + numbers [ currentIndex ] -= 1 + if numbers [ currentIndex ] < 0: numbers [ currentIndex ] = 9 + elif btn == 'L': + currentIndex -= 1 + if currentIndex < 0: currentIndex = 0 + elif btn == 'R': + currentIndex += 1 + if currentIndex > 2: currentIndex = 2 + gameData[ 'film' ] = True + drawState( 3 ) + images[ 'DeskR' ] = images[ 'DeskROpen' ] + drawState( 4 ) + return True + +def redHerring(): + gameData[ 'film' ] = True + tmp = images[ 'LampL' ] + def flip( t1, t2 ): + drawState( 2 ) + images[ 'LampL' ] = images[ 'LampLFlip' ] + drawState( 3 ) + showText( t1, t2 ) + drawState( 2 ) + images[ 'LampL' ] = tmp + drawState( 2 ) + flip( "I'm a fish ", ' who sings' ) + flip( "I can't do ", 'other things' ) + flip( "I'm lovely ", " & so caring" ) + flip( 'But only a ', ' red herring' ) + +def openDoor(): + gameData[ 'film' ] = True + drawState( 2 ) + images[ 'DoorM' ] = images[ 'DoorOpen1' ] + drawState( 2 ) + images[ 'DoorM' ] = images[ 'DoorOpen2' ] + drawState( 2 ) + images[ 'DoorM' ] = images[ 'DoorOpen3' ] + drawState( 10 ) + showText( '', 'Freedom!' ) + gameData[ 'film' ] = False + images[ 'DoorM' ] = images[ 'DoorOpen4' ] + b.inputJustPressed() + while not which(): + drawState() + +def makeTransition( title, nextState ): + bigKey = 'Big key' + smallKey = 'Small key' + tinyKey = 'Tiny key' + seqPt1 = '"U,D,L..."' + seqPt2 = '"...U,L,R"' + bulb = 'Bulb' + hammer = 'Hammer' + success = True + isItem = True + textToShow = None + if title == 'Unhook key': + images[ 'Door' ] = images[ 'DoorNoKey'] + images[ 'DoorM' ] = images[ 'DoorMNoKey'] + gameData[ 'items' ].append( smallKey ) + textToShow = 'Small key' + elif title == 'Wind clock': + gameData[ 'items' ].remove( smallKey ) + cuckoo() + gameData[ 'items' ].append( seqPt2 ) + gameData[ 'clockTick' ] = 1 + isItem = False + textToShow = seqPt2 + elif title == 'Read diary': + gameData[ 'items' ].append( seqPt1 ) + isItem = False + textToShow = seqPt1 + elif title == 'Open box': + success = openBox() + if success: + gameData[ 'items' ].remove( seqPt1 ) + gameData[ 'items' ].remove( seqPt2 ) + gameData[ 'items' ].append( bulb ) + textToShow = 'Light bulb' + gameData[ 'boxOpen' ] = True + elif title == 'Use bulb': + gameData[ 'items' ].remove( bulb ) + textToShow = 'Bulb is in' + isItem = False + gameData[ 'bulbFitted' ] = True + elif title == 'Pull switch': + textToShow = 'CLICK!' + isItem = False + if gameData[ 'bulbFitted' ]: + if not gameData[ 'lampLit' ]: + images[ 'Lamp' ] = images[ 'LampLit' ] + images[ 'LampL' ] = images[ 'LampLLit' ] + images[ 'LampM' ] = images[ 'LampMLit' ] + images[ 'LampR' ] = images[ 'LampRLit' ] + gameData[ 'lampLit' ] = True + else: + images[ 'Lamp' ] = images[ 'LampUnlit' ] + images[ 'LampL' ] = images[ 'LampLUnlit' ] + images[ 'LampM' ] = images[ 'LampMUnlit' ] + images[ 'LampR' ] = images[ 'LampRUnlit' ] + gameData[ 'lampLit' ] = False + elif title == 'Enter combination': + success = openDraw() + if success: + gameData[ 'items' ].append( tinyKey ) + images[ 'Desk' ] = images[ 'DeskOpen' ] + gameData[ 'drawOpen' ] = True + textToShow = tinyKey + elif title == 'Unlock cupboard': + gameData[ 'film' ] = True + drawState( 3 ) + gameData[ 'items' ].remove( tinyKey ) + images[ 'SinkL' ] = images[ 'SinkLHammer' ] + drawState( 4 ) + images[ 'SinkL' ] = images[ 'SinkLOpen' ] + gameData[ 'items' ].append( hammer ) + gameData[ 'cupbdOpen' ] = True + textToShow = hammer + elif title == 'Smash piggy bank': + gameData[ 'film' ] = True + drawState( 3 ) + images[ 'DeskM' ] = images[ 'DeskMSmsh1' ] + drawState( 2 ) + images[ 'DeskM' ] = images[ 'DeskMSmsh2' ] + images[ 'Desk' ] = images[ 'DeskOpenSm' ] + drawState( 4 ) + gameData[ 'items' ].remove( hammer ) + gameData[ 'items' ].append( bigKey ) + gameData[ 'pigSmashed' ] = True + textToShow = bigKey + elif title == 'Unlock door': + openDoor() + elif title == "Press button": + if gameData[ 'lampLit' ]: + redHerring() + else: + textToShow = "What button?" + isItem = False + elif title == 'Look at art': + if gameData[ 'lampLit' ]: + gameData[ 'film' ] = True + drawState( 12 ) + textToShow = 'Nice shapes' + else: + textToShow = 'Too dark' + isItem = False + else: + raise Exception( 'Unexpected transition: ' + title ) + if textToShow: + if isItem: + showText( 'Item:', textToShow ) + else: + showText( textToShow ) + if success: + gameData[ 'state' ] = nextState + +def nothingText(): + seqData = gameData[ 'seq' ] + image = sequences[ seqData[ 0 ] ][ seqData[ 1 ] ] + if image == 'DoorM': + return 'Door is locked' + if image == 'DeskL': + return 'You already read the diary' + if image == 'DeskM': + return 'Piggy bank is smashed' if gameData[ 'pigSmashed' ] else 'Piggy bank - rattles when shaken' + if image == 'DeskR': + return 'Draw is empty' if gameData[ 'drawOpen' ] else 'Draw has a combination lock' + if image == 'SinkL': + return 'Cupboard is empty' if gameData[ 'cupbdOpen' ] else 'Cupboard is locked' + if image == 'SinkM': + return 'The box is empty' if gameData[ 'boxOpen' ] else 'Locked box under sink' + if image == 'SinkR': + return 'Clock is ticking' if gameData[ 'clockTick' ] > 0 else "Clock has stopped just before 3 o'clock" + return 'Nothing to do here' + +def handleInput(): + gameData[ 'film' ] = False + key = which() + seqData = gameData[ 'seq' ] + oldImg = sequences[ seqData[ 0 ] ][ seqData[ 1 ] ] + if key == 'L': + seqData[ 1 ] = seqData[ 1 ] - 1 + seqData[ 1 ] = seqData[ 1 ] % len( sequences[ seqData[ 0 ] ] ) + if not sequences[ seqData[ 0 ] ][ seqData[ 1 ] ]: + gameData[ 'seq' ] = [ 'main', gameData[ 'mainIndex' ] ] + elif key == 'R': + seqData[ 1 ] = seqData[ 1 ] + 1 + seqData[ 1 ] = seqData[ 1 ] % len( sequences[ seqData[ 0 ] ] ) + if not sequences[ seqData[ 0 ] ][ seqData[ 1 ] ]: + gameData[ 'seq' ] = [ 'main', gameData[ 'mainIndex' ] ] + elif key == 'U': + if seqData[ 0 ] == 'main': + gameData[ 'mainIndex' ] = seqData[ 1 ] + gameData[ 'seq' ] = [ oldImg, 2 ] + elif key == 'D': + if seqData[ 0 ] != 'main': + gameData[ 'seq' ] = [ 'main', gameData[ 'mainIndex' ] ] + elif key == 'A': + if seqData[ 0 ] == 'main': + gameData[ 'mainIndex' ] = seqData[ 1 ] + gameData[ 'seq' ] = [ oldImg, 2 ] + else: + menuItems = [] + def makeTransitionLambda( title, nextState ): + return lambda: makeTransition( title, nextState ) + for stateInfo in states[ gameData[ 'state' ] ]: + if stateInfo[ 0 ] == oldImg: + menuItems.append( ( stateInfo[ 1 ], makeTransitionLambda( stateInfo[ 1 ], stateInfo[ 2 ] ) ) ) + if not menuItems: + menuItems.append( ( nothingText(), lambda: None ) ) + Menu ( menuItems ).display() + elif key == 'B': + showInventory() + clockTick = gameData[ 'clockTick' ] + if clockTick > 0: + if clockTick % 6 == 0: + images[ 'SinkR' ] = images[ 'SinkRTick3'] + if gameData[ 'cupbdOpen' ]: + images[ 'Sink' ] = images[ 'SinkTickOO' ] + elif gameData[ 'boxOpen' ]: + images[ 'Sink' ] = images[ 'SinkTickO' ] + else: + images[ 'Sink' ] = images[ 'SinkTick' ] + elif clockTick % 3 == 0: + images[ 'SinkR' ] = images[ 'SinkRTock3'] + if gameData[ 'cupbdOpen' ]: + images[ 'Sink' ] = images[ 'SinkTockOO' ] + elif gameData[ 'boxOpen' ]: + images[ 'Sink' ] = images[ 'SinkTockO' ] + else: + images[ 'Sink' ] = images[ 'SinkTock' ] + gameData[ 'clockTick' ] = clockTick + 1 + +try: + drawImage( 'Title' ) + while not which(): + d.update() + showText( 'How are you', 'stuck in', 'this room?' ) + showText( 'And,', 'more', 'importantly,' ) + showText( 'How will you', '', 'ESCAPE?' ) + showText( 'UDLR:move', 'A:menu/act', 'B:items/cncl' ) + while gameData[ 'state' ] != 'Complete': + drawState() + handleInput() +except Exception as x: + if inEmulator: + print_exception( x ) + else: + with open( '/Games/EscapeRoom/crashdump.log', 'w', encoding = "utf-8" ) as f: + print_exception( x, f ) + d.fill( 0 ) + d.drawText( "Oops - died", 3, 8, 1 ) + d.drawText( "Problem was:", 0, 22, 1 ) + sideScroll( str( x ), 0, 30, d.width, -1, { + 'A': lambda: True, + 'B': lambda: True + } ) diff --git a/EscapeRoom/arcade_description.txt b/EscapeRoom/arcade_description.txt new file mode 100755 index 00000000..3669296d --- /dev/null +++ b/EscapeRoom/arcade_description.txt @@ -0,0 +1 @@ +You're stuck in a locked room. Can you escape? diff --git a/EscapeRoom/arcade_title_video.webm b/EscapeRoom/arcade_title_video.webm new file mode 100755 index 0000000000000000000000000000000000000000..3c98f97a2bef1a4b3352ddda858da71138a3cc25 GIT binary patch literal 22371 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%0A4-C1aoICbsk4IFewOA9JD&afBU4 zcp8Te_aJ(hClnPjjy{)(^ymOsDzXuoFJg#_9>)7xg`%PxZ)(u6D!d^tEe9&ORNSl| z?wLO`s;*UK;%I)>LluA>27WNMELQ?N4AjEi?^s^#uw*}>$H|2ZQt&D&er$HlA=1Dy z1?dq|XFXPUb*|ouQV>$x*aiiFygfKFDLREQ`vdiVS@k}ZtIS12M%S0JUDECH2TMW* zX4dJvg>+KkxgeuW;w9v?y#^fQk2jyx`WpWa;4J}wL9X>R2^vLhw1hV>R9+MR4e<;= zYnX4}`~Ex2YgynQ5^J2&_ie;qG;#|Ch-bhuBKe13;YE!_!ifyaa3I%}TyUhpa9w*v z51{@40CWaql|H-3&PpU}w!Ulk3 z7-!}Xb|>*)41kVbjejdY<^N4^LH|at5o(2mBnt1)u z2JYK&WUryu^B}N(q9$iED(Ue{1rOioc2bKQTw7gZ=}G>=d4sEVHBYz1+Z!>9fc|V-Ze8t5>tSFol|5;wzS7as^Gl~$)fBv~#Q~?%L zu2bmz>r;7{4lfDYd9IF4xxCede`<%;?)L#P-E|xuTIYiT2Hqu1RT$?!d7G$)+E-9V z+Rl~jR4{>!5`J|`Fuon9Z2>-3Y&&pOAOqeUa3sxRnE^di9U+4#u{SG zTgE&M*6d$Yba~i5$$+CmHB@P-RN&FEjeS$uLX%u);PGg^ zxeeu7eQnMS$D;sn1_wF}^kurqTEEZ3nIC^O=Lb+^ZNr9+DrX~(s2OT|!B1P&V{)We z^zkTK&GSH*pT^{Uh^4*?vzcC&A0RoO9mI!lS#YW+7?4k;(n}m9`rtG`zfZXqH76RK zVZLov_?Sc^$3V`4v>CXAG}BE&Zl3<{LeMCZ2Z%Hw5VEA>;@0}TWT|Ke%AV?2*aa`I zW(j+JCay!WJ#wBEO~BZ#076uN;Cj<_Pa6S2&kUOz$4)#!?PNPVfim?jD?jT?uLsB# z0pRvHP91Sm@qJYk7GjJ#lP|W8r*Vs)pFfb^6KsK~q0R4e)!TO?gNetK| zi!Rd?h?RV^u7@cHoF&Ve*k6qrr17ywLa}w(sDd6U zpMv9Caz#`qVZq?W&vBxy8$>k_1~|dIJNI4je8!F19DL!9Wdgtm|q`=Jn*cau$1$ z_EQ(PyXPDG??gDT+_1eFj>bwU%9rPCbGXvc)>m#ZRO_(oNsJS3OSm`O!xXZ7RUKzO zoF>=;K7YEI13!o;+Sw{VZGGgT1XelBqb5p;7twk7U0n9!xV?L$CQjag_!-mahp&jRBQo-ER~C+z{qEloU3TTd==JaGjDV|G{;6V%$1s z8-cmn68Vf`5%LEQbc;uAEt1(W9WPGSb(a4+%lHRStf-r4`OVsIJ&M3I?^EajW&XoM zVknPNYVufI3gjDoBl-gzcqypI|KQgmt`<_$Bef%AVras_d2+6QZ~>PmeYJGjcda0Q zR)5~@qnEULXF_O+T}f%=LO$+Vo}E(lp6c|+wE0_+u?++I-s(?nh1jZM_AX~~TrF2k z)=xkvp*w^-MO)f57Gm_LiaP zk%X~lr}AJWYw753og1wQJ_V^(*mEs}(T)=OpUl=^w0-zG5s1mO@@y15D*)zw=0nkz zg6^w0Q~@VujlkRbdUKsuDGfk9FdPaO0{}EZ+kPl2%~R%&Q^7JFRTyKMyKW>mPfg>T zyW^eDaCLDv^iFM4S1z&I{O(6ihKOq0JR8TwXi!&18`6x4r7hm5$HSn=oIq>322=-pwm!NhbDKXHY!%F)`Hqo@E6IA`qXr#Z>?f_;L3(fMJI; zBwCel0Q|rZ`RO&C{6%Q)`UK?93uULV`p73X2dGafHtv%5AoczOpneS{oxGxk6#~)+ zV?(E(5?bK@Fp<|Kj?KauyikylG>K?+dbEFK_B$6qp{8};rzHP6b0JaA9vG$C$?bM} z{)y~Hk1L)TkYJ4L{cnJhC9%4K$Ss3Ff2%}xn%TWCdIyym{zom1Yb`hB+o2jc0fP}v zpXCddN}tZ<&TyCWf7T5oR&(_Qc=a=-2G>c$zm}Ke@OUujUm{n z^AlgtG$|i070g#?|DhWc)31rPW679DTkXJy-;RNH@keW{T6s6}WY;dc`F6lfd`{>0`;0s!h|0I<;ReJEH0m{|oO36>5#sxGKz zJZ>^vPVVK6aX<+<`7w;K9DJ=a=1|4~O7kT!vU}hy0NB6ILyMFlFg}&*Evg&HE7LuX zLuuqLYg6?^Fi0(DUybNoJsz?;d!K}DPP>neh3T(vI7BmF?;QmvT%e{|wawqfgXrH# z#fk$>M^`*c z0Whc9CNOHKYxS_%Imlgg@0{B@Fed#S8XY$K8e0wQ6n5tZ0ZXXVzDzsSH#(HY3 zS>yPKT?xu*KJCZ5#VgS3#D#vsDE~;gt^JhE<_d2hMIo8Q%x3NfTL)c-g82Ocb34Cj zFLLeMxAo4apD;Kj5dy*O__J@p*!*NdpqabDmx&mD!}LIj&kRYfz*pe zJXVM&WSn9^tYffpNI`KlW`CB%TT;RDvpG)OF$5s1a>l=>Qa_wAVmR500k3Q7suvA| zlR9^1%rJ5-p3tI7v1ZM1p7q7P%dsb)KQ}2nv$KzAWN=H)vlr{eXPmmjXU+b(^K}f( zKqvTMu>3$Y-K9Ou|7c$;UEq_@BxB)1)&taulwbvVgOpWkLzdFS;nqpU$jgJ^p;-8! z);7v=RCfJ03H$FkB1I2aspJB~Bs_1NE%OOsc44-RQibRe*igDlvIG7QY|QZ5&iLj- zE0OUQcg7_enaG);nTsigWbT(E*;DWAovnNmZc`F&@nCEWfNb0om)v2oE_EeZvnwj3 zD%#Z{pNZYPXK>Jn-pAurFTazfM7t-LFQ!4E-(GRH5$XH_ow+>JyP+|YPX`Ees}D-i ze4|+Da=QFsD3XNJg93r zS9>f+zScl8>jSOI#ah8bII@j)lw`6~l1M(Sr&A~`H-~HYhoqKmNyfO--|<_> zMe(YsGDVI2{&}&m_TXEcs;f?&sE>gmCCWC8-iXtJM-&wl*V_c=-YH7oCBUbQ8>@%6 z_{SW6Z`j-d1@3iV-q_mNU`xNhpPyLeh;^rxobo#q5K%E-@@=B+=j2~YDNSkf60*Hj z5@^Jl@2xlZX5L+))rR8fp_&^}z(ZfUFUQB+BLT83e0=D<*}82{^q1_I&4XX_lSmhL zSZZdNCD>-kh`E>OUnw#G0L^|nb(W-z0Aes8w@VrmZ7rzm&P0FZETTcH`(gh zGuUl*6KWL3+{?d00fAESi|FD8_rYrA%Hcvq>eF^&231?iF0#D!oAmV9->1CoF z6GR`_$vhZA#dxdbKviqNa_G|GEfkay`gkY&bsJIT4o;P-4majpHAxPI#sH?#u)!U@ zQ-fgwO@`|O0C5}YP$VkoC*DFk&N)8$yw!rL7e+Ox$ND_94A+uo;VjC~&syvhw*;V* zPbE<~U%gB;*CcYg;>^mK#TvSENAl>Ihk>Ybufh9w0zOm_+nlWLx1`keb}rN>gM!a= z+jXXUiRmhW%?FbHpQVEkk@V|?&vY%T7H=uMN@zR4R9G9IaYkD$ThJK_M>s=i{HsUi zQlR1OV;*0NWA;Zzu-wHe-$BGX&3HDivZ#M-KtMj?Tt^t=E{B)^O63Fnfoj%;QI~V~ z9JL1gke3q48V?xHuYffJ!a!(W_+cV6x0Y>Gp;7_vjqwimQ2_vEPKJF!``|;r z29gCjs-NQY^PZI?1sdHy)9G7{#W}e>wP|UhuHNX4@fMqHt6U+PGMl%wbhP1;v}`SQ z|2d(o$tb19-9sy1B+x!GR}~l(Y_xTw_ay@e2USzZOC1(1@QSx@5~* zHfZsEy{%b;FAe&Yh5^6~|X9SP%bF(;iL?DQWx6MA%x5*>=Lx{TK&aP8eejyK@|rPewexeAR- zHE}*(rvL>;+|oV_p1rs$-E@rG$&0fLur6X;hP5wE^!Y01zm%clTDe;@OR{p?!&Vf8 zfylRyN}26L7tegB4@UWXvQQ47lMG5a*Q!k+J7TL~vSxb^m&sq`Bj3{xzyN&%+uz+o zHEwyTPExncqt{zkuYRK+??h9y7j4-uxGc>O>i)ObH3D{FE#;Rt94wC6TX1Rt{`fy1 zfEUzP?7849f|4ZDAr(|sPC@-!TTrjNXyTJkcFzw#ecf+6Z$FS?#8Yza z$AE$s-%vG}_2mV_!A;`LafXW*34}rvRYpxm(3n&frY=djl zqyJzD3KxXP4K?&8vA2*IM^%*wS^v&PFXLAmWqSx+qA(V|pq=1(`oh3QCTA>-%TU#> zUX1&A=ogdb*6a0VQysHrc!fqw!;$Ii^Tx+{@rc7j&~@7C!r^ ztDDmvk_4>ta@_k#)ml-Rg`_w8*F_`~%)lBP`cFhlsZ%X(-jdrC`>BUmkOCHcTSf3x zI_l3f5R~_vE-?d3+w#}-ehOa~Eg65KKM~%wKj>QFKS;oMqmOc+dom6HikGfeMfALU ziU5F9OhOk5-~>Sb5N36dN*{EXXr>uCp{arV&cefa86hViLaX(ACDJwr{8wlG`|3U~ zPj?xf6$MH~4t!b^BFX=~IWm=0>{TGcv!55*ZP38oz2buW&PJw^sgnilB3g!tv^%zp z_K~f>a{;V&xfB(q!aiCM)65{&bL(+Ls%C9@G*8RxGmH-FKSY-=|Fl8qiR7cjYIpXw zhHm5iE{HY_ydwZpLl{tRLLrzQpu(G@^kV{+9KXu|pt|P+RlS=XEeX>njQV+L@TNcP z9>5{U!{N+1pHQ~DB+U)|qba&`pUVny^2Q0r*1cnzb;@EbCe_~rK%Wt$-(p@p*6~y= z{P2X)A|3gsvflxXJaK8^7<-aBg2pe6p%-z+Wce2Ix4@B=LSs3tAQbF2wt^NWQK=kmZCGxoQO?5-@jcDN1*B30GLu~JqE7I^ zw^>qBg7Vy`kn!v9ASuUFhjisH{;946A0g}P-)8~}R1$1Zpq(U3Gw%B(wXIHze@jM=c1DnW*QSTd128+y;GqRcAr6Cc~?h{0i? z-$)6($PI0b38k?fz#;+9-AkbyIT*b)`HjDxf)X)8pWwLQyTIKP;2n~tBru`%b*qW! z$NUsex@NN_rJ?w+`POX!m2t(6F1MlSvks z2g5F!o{%WuyHQAE#2r`I5lpz&gz!CMaPa3tV^Nq(sQzI;eW$U{{To#CQ68w6L~ybW z1LrOT#vxR^^3cq3W2!TSk;_EWNB$6c`>{QSi;=|^?L8CX zKr&yd2n<=FvoHfulR^dngD-ZFh=5pmY55>%3Ub!PJY~aG$QI%7sU@cmOk(EcIuwQa zY8b}-8eHZc%8Fx0;*|ImV`nU~q@LS=<4w*|`4f%r`iQu_V@~zN! z^lwGMvL=ddZZp=F0ziu01A2j?bp3^~HrVMZ*s^Dh`ZD~udu=L7XM)OT?e2N3a<84E zcb36uPoc3?w^N2(lR2t4j3!@&12`m*LnTn|G#E}??>s3nMkVA`z44H>Xbib#&GwN6@9tmI>oiNNsG48tGd z7fNQPb?BlhXsXRPAF;a_f=tY-219&?;_CGlyPpp%jUi?~?7kt*rbClyA7qggHZAja z^2TLvCN_?IsO?W({8alZ_|}hA{4pq+ssKSByUL-s4+*(pVQBkYs%}BTyI}44Tk}Qe z6f`6$!zlv}0X*cU49!k_;Br}UCV^)};0D!L<>>#ldpFECY+Q7#TgouuXtNkkgx(c~ z*cA-B0~pH|aLq_y;F|yc@9rzFCW@cFw4>e8s%puCeV1B1Y976IJ(c=F>YRZh$ErK& zKo9V7P0W!IRk~qh&Tg>c-Z#d;y>FNnSKnY|y}H8L@0N?n+a*8>=2P(vqILQST7M3t zIcqK}o>-jhf8OGUy}9o<28P$bW5LR89+%nFU9jsDli_@E=*K=Ckq!Fh1rP)rIIt68 zTp`uCf`LKq>6FVW6h4=HcWatocR<>Jf$#32-)8d-{p&VN^>`p!AX*GusF0^v0L;L- zOSdccP2_P+2(vf_+@g}(?X^t&*wu`+i!QERA71U8{=;cyxaZ#5Kob_W{ePD%SkwFFx7nnh*VLQ7 z@6d}V-|EyfFXC&kkd)9nj>l6AX6Vf+S|`kUxi9ok!mh%DccX9fSy->GPCU+DnxA9w z;N9A{EoE6U9dE8!3f^2%q`W!c$*OY;rFItg-^n_ee@%fi-Fv20qwA!Fs^Yar7}$2- zh?}^y)7Vnn%&_6Q4A077#V4x9OkDLZ_^mGrygAv!?j>M$@8HiJD%0F=Fen9RM8Dj) z{%j9#gk!D$54Lcgh|L~NyW{)V_ssexf9v&vH}BsUo|Xmx(lxZ>o*Cdwg+w`-dLGt6#M==ch*UKn8U8rYhWmg}P*e&y&!F&0# zSsu!pr+RJBx_@)!>as8E=AX!2q~0u|(3$Hs(`(-?mF@g;5A91&Z3=c~Sb0;QCH0uI z`Ai!D%c31KyKY|t9umO75I<-6jjA~NWwjY``@DZdXomfK6ZrkmLB_u+bAJ2Ro_5>P zRz8V2^y&6J1xubU)ST}U{C7rWU;wZWKfTdJ1hza?>UBD3zqahliJKAs)7z9=wU=Z+Gz%)nL$4Vipqd;e3!$AcBrrL>4TqY}6-S|VThl%;V<;gxRkalw> zqO|`kz@hywRJ%w5Lz)9{oy5-fn}7LTkhtIcBr;rL76Sp>ExuA@yX6O>wBz!-)hnX3 z4Y;f7`cz`0? hopuqW9hdFS+lbPR%kM6GiPDbCc2~-5cUw%p?EoBSEFqF%^n0 z$oSTY6IpedI5eWPL2#5P*uKL#n#f$51K&{#tU8IEgct*t^V&G(id}mTANHPa?{@lT zc4q(F?w!w$DTL&$z4K<~&6}CuzJ2p=ZItK>^Qai&!+i-V=fEMpX^fhoDA~D4QB+ZK z*NSV5AyZ{bf#kArYW6|SKjMUq^bZKv|F-0$lIXz~S!F)t0Oc5-U!fj3gQy&JZ z$4Mv`XPyrWf@nuV-wB>49nhMgF>1dII472G_Z7I7lSif%*BDig>7H0#?kg|rADft( zo|+yT?dkXJhxm#zNL95MV9BrNmgB5GJ5$f*9kJ^kPo9Z-#|pOun%4~+kR;LN&8wg3 zX%4istnsgHU)%nm&)?qeYxT9a`+aTgn2xu%9ryeF?GGMbOXAvXt$$5ROQ0DmXL?lL z0dbOITW>~ANHa6i^bF9nQ*ufMVJIa^#5iTSK3oryiNbFe%f*7Y0Hu^9vbys(gh><^ z-Yh-vmWh%KUa9U-EEb!4I#j2*E!JAr9$KDytw#UxzyPN4@$rF1 z{}(vG6bJ+wqhHAxR$33Ko& zx`Zy`A0ZaD_WG9vCm&ly|DhJYfX=M_U>Ld&SG2|{$!BUm69I)#^SsXt2F`a z8p+=Gr^t@o&;R!0%j>JQt$EXb@W&PF0xlv1FG!`g62BXKn8SBtQIkE#e(l^Sqf9zG z*et+{|NFK;wGiglZ2sLa*zKMtHY%BHb~d9Zne-nXXm1*A`b*pTv(%!E_B@(~*J<*z z^B+mc5phTqf<9kR5aA^t{sDAkva&2m6O)r;{}H9&tI<*2iZp|q7h`A7%!3(wJ5Qdd z$kC(Xr=xfi#=)F{aLt0p!O6(r<;#N?E?-`C`TK*b2UkaE|Dj8ZHxB0DGf#+#I8l+K z$A~j0h?PQE;1B{6_J%8gu_dM_?|h@x-*)WS-`kGEv}29`{`)bb(`}A(o&m`g2uq?7 zT;AJ#k&KsxZ?=P}rYH838^X(QI*Zn15qUl6T0FEs@GaT6lpPSfd0D75~_fOrw_gM%4>&CXR}tO zP*qwx9Z2kMSfchi&wWWlw>B?jB0T4Em6kf4sGe7oSWo5TNY_YGM###rvMdxwhKCY~ zR0cdhcGQ5gBno6{2Q#h(5>{h7!D5vb4p}d-)NN_cnKVpxPd-U5GL+TSJgieq4IAEW z+?bl0S*8LDeKGcn*mDlhr%#>g?9?Fd>^ybqv=)Pvwg#F(kAc;)3Bwsmsw`&~&|*YI zUOhCUREj4{)gt*si_(K`B)42 z8~%f#961s-tOr>$=i0YnYRLW#D`XQ|o6dQ;M&2T6l{1T2@`qQ0!0KVMRDP(KoJEf` z4$n(s*$6ee-ELU4TjX>J!p)VBP`m*zzUGpgXfADlT{I}GJ8pOZ@@m|7{>M?S(z=7)18h4oN!NHE#*?xnE> z;p{3EUC1#l!)A?=Ns3+`E1L@+yZqi|dN~JRTolg9@o1~*y3+3G?^>hw)dJM8UYsDEMj2Ewq5Ds8y z*?hbr0DI0il5tBmOx1I7&QXB)@Rqmn4r95mKb_SwGcr|{7iUeBG89YINU@<>e{FHj z6^p>Shpc*XD=0b+FI_4T0deQG$;oR@RE=b(On#6v8r`XK;Ai9#8F5{imC>jZfq*1C zWF#*oY^f@wRtXz9f-v|f_+iEKH1fDSio$yoUh#Mk{6HeFBs07h#}!CX5YB=wV3s7V zR`m!GI1q?kK@$iG5djSe!ca8&U(7~u!pG6*RlYHW(+APNqEQHOmO$cI5^_LMNvVsY z7O1MVyF**^6Aw$&QgE||_YwAf2pI~MRv)OKS2cKm(!dscwQO$R+(NiOsf((>dYUWz zE6TRNRDN`8YkAqGrYDw``zutx5y8iLE8yDi>xYNpC@aso98jJ#h1`_P?9}zCudd*0 z6gb%Zq>U1LM35swI7P9NQ4cMYCot%-m4I6Ety7p+mB-JALPr8eLZS2HT3Rk+Sf^+W zx2x(U?Ae=DJF=Q9WjH6vUOc$lz5z)JfuT%1 zg^%qe$sG{FE|E=pGHgkY6)sWB5ZzQyW<;&*#pi${-@b!hFOKxZLpu{&3 z`wjJjgPs1W@wm#G{f1jU^jysaDY*NeQgyZWxn)_|P5p)dqbUa$jHwZ5_O6xuHv?T=Qq?xc9ZQHrWf#L5Obb|cZ1k7yboSZh=-Pt z78GaC896D9k56E8jxl=%#j`LCiPeLin`+N0Qnq)nZ?HcjL5i&;Y9S8S%jLQPEDdhA z+`r9qK?;4390u2oojAD3rcFzU(|x;?GggM(uFch|A`P=ELct}&fw#v~1ALQ(BB97PA?S@x`K{f6Q?T%KFk};8%ZpX|6XD~N# zxI)+#XX24fgD|8poQc!cZcrYq*9onGeepid*eavjHKb=K&agxv3b1dv8?)}}DfgzF;5ao~GpXEX zHp17FT&joj27(qk(9-dbj^K$P+*=N)xdAUnmGWhtbDy45*sPiA!6&hV(&@MU3=t9? z8q{eBpVX3c);Kcr&8Fz|67-ODis@{yzR&oP-HRO^?{sv$2q(3M@99h~5+c@hN@;w2 zVp4n7MKZVyYbfa=cs3x`OSP}+9gKQ%5{{bHY_kN3<>gCVe$;#(1z?Bi44qc znUo>Rz_MIpXw^etZZ+$KgzS`gL(;fLZmBoMLgtn^uJEakg^}*T%?Mip_28no)joVm!{a6>MCA*;>uU zOiYhILup4h>*x^IqoCD{*kKJ3gom}@mwl|NS#lo4i zXO0`!C&8{U&`Qa?a}=Ua*Mx5;n(-*C3N*c`WE6TSWRk$wr=NVoSs(zg@m zr!oD;Lw4(6Ny==AF!R~3tM7R4o%cHMpUTd)Uw0?t3_DquFPxmb8?S!N`=ay28TO4b zuseNclgM7yb zAB@sXb2c8>g!x3Y?-=oxx!e8Bqv)Zks=uLzy1H8Qy{f7PUS3jLSJ!|Vo`3!!tQLgg zf8}|c-$q_dfCE2pmIKH